diff options
Diffstat (limited to 'drivers/sound/ymf_sb.c')
-rw-r--r-- | drivers/sound/ymf_sb.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/drivers/sound/ymf_sb.c b/drivers/sound/ymf_sb.c new file mode 100644 index 000000000..a221a05d0 --- /dev/null +++ b/drivers/sound/ymf_sb.c @@ -0,0 +1,867 @@ +/* + Legacy audio driver for YMF724, 740, 744, 754 series. + Copyright 2000 Daisuke Nagano <breeze.nagano@nifty.ne.jp> + + Based on the VIA 82Cxxx driver by Jeff Garzik <jgarzik@pobox.com> + And ported to 2.3.x by Jeff Garzik too :) My it is a small world. + + Distribued under the GNU PUBLIC LICENSE (GPL) Version 2. + See the "COPYING" file distributed with kernel source tree for more info. + + ------------------------------------------------------------------------- + + It only supports SBPro compatible function of YMF7xx series s.t. + * 22.05kHz, 8-bit and stereo sample + * OPL3-compatible FM synthesizer + * MPU-401 compatible "external" MIDI interface + + ------------------------------------------------------------------------- + + Revision history + + Tue May 14 19:00:00 2000 0.0.1 + * initial release + + Tue May 16 19:29:29 2000 0.0.2 + + * add a little delays for reset devices. + * fixed addressing bug. + + Sun May 21 15:14:37 2000 0.0.3 + + * Add 'master_vol' module parameter to change 'PCM out Vol' of AC'97. + * remove native UART401 support. External MIDI port should be supported + by sb_midi driver. + * add support for SPDIF OUT. Module parameter 'spdif_out' is now available. + + Wed May 31 00:13:57 2000 0.0.4 + + * remove entries in Hwmcode.h. Now YMF744 / YMF754 sets instructions + in 724hwmcode.h. + * fixed wrong legacy_io setting on YMF744/YMF754 . + + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ac97_codec.h> + +#include <asm/io.h> + +#include "sound_config.h" +#include "soundmodule.h" +#include "sb.h" + +#include "724hwmcode.h" + +#undef YMF_DEBUG +#define SUPPORT_UART401_MIDI 1 + +/* ---------------------------------------------------------------------- */ + +#ifndef SOUND_LOCK +#define SOUND_LOCK do {} while (0) +#define SOUND_LOCK_END do {} while (0) +#endif + +#ifndef PCI_VENDOR_ID_YAMAHA +#define PCI_VENDOR_ID_YAMAHA 0x1073 +#endif +#ifndef PCI_DEVICE_ID_YMF724 +#define PCI_DEVICE_ID_YMF724 0x0004 +#endif +#ifndef PCI_DEVICE_ID_YMF740 +#define PCI_DEVICE_ID_YMF740 0x000A +#endif +#ifndef PCI_DEVICE_ID_YMF740C +#define PCI_DEVICE_ID_YMF740C 0x000C +#endif +#ifndef PCI_DEVICE_ID_YMF724F +#define PCI_DEVICE_ID_YMF724F 0x000D +#endif +#ifndef PCI_DEVICE_ID_YMF744 +#define PCI_DEVICE_ID_YMF744 0x0010 +#endif +#ifndef PCI_DEVICE_ID_YMF754 +#define PCI_DEVICE_ID_YMF754 0x0012 +#endif + +/* ---------------------------------------------------------------------- */ + +#define YMFSB_RESET_DELAY 5 + +#define YMFSB_REGSIZE 0x8000 + +#define YMFSB_AC97TIMEOUT 2000 + +#define YMFSB_WORKBITTIMEOUT 250000 + +#define YMFSB_DSPLENGTH 0x0080 +#define YMFSB_CTRLLENGTH 0x3000 + +#define YMFSB_PCIR_VENDORID 0x00 +#define YMFSB_PCIR_DEVICEID 0x02 +#define YMFSB_PCIR_CMD 0x04 +#define YMFSB_PCIR_REVISIONID 0x08 +#define YMFSB_PCIR_BASEADDR 0x10 +#define YMFSB_PCIR_IRQ 0x3c + +#define YMFSB_PCIR_LEGCTRL 0x40 +#define YMFSB_PCIR_ELEGCTRL 0x42 +#define YMFSB_PCIR_DSXGCTRL 0x48 +#define YMFSB_PCIR_OPLADR 0x60 +#define YMFSB_PCIR_SBADR 0x62 +#define YMFSB_PCIR_MPUADR 0x64 + +#define YMFSB_INTFLAG 0x0004 +#define YMFSB_ACTIVITY 0x0006 +#define YMFSB_GLOBALCTRL 0x0008 +#define YMFSB_ZVCTRL 0x000A +#define YMFSB_TIMERCTRL 0x0010 +#define YMFSB_TIMERCOUNT 0x0012 +#define YMFSB_SPDIFOUTCTRL 0x0018 +#define YMFSB_SPDIFOUTSTATUS 0x001C +#define YMFSB_EEPROMCTRL 0x0020 +#define YMFSB_SPDIFINCTRL 0x0034 +#define YMFSB_SPDIFINSTATUS 0x0038 +#define YMFSB_DSPPROGRAMDL 0x0048 +#define YMFSB_DLCNTRL 0x004C +#define YMFSB_GPIOININTFLAG 0x0050 +#define YMFSB_GPIOININTENABLE 0x0052 +#define YMFSB_GPIOINSTATUS 0x0054 +#define YMFSB_GPIOOUTCTRL 0x0056 +#define YMFSB_GPIOFUNCENABLE 0x0058 +#define YMFSB_GPIOTYPECONFIG 0x005A +#define YMFSB_AC97CMDDATA 0x0060 +#define YMFSB_AC97CMDADR 0x0062 +#define YMFSB_PRISTATUSDATA 0x0064 +#define YMFSB_PRISTATUSADR 0x0066 +#define YMFSB_SECSTATUSDATA 0x0068 +#define YMFSB_SECSTATUSADR 0x006A +#define YMFSB_SECCONFIG 0x0070 +#define YMFSB_LEGACYOUTVOL 0x0080 +#define YMFSB_LEGACYOUTVOLL 0x0080 +#define YMFSB_LEGACYOUTVOLR 0x0082 +#define YMFSB_NATIVEDACOUTVOL 0x0084 +#define YMFSB_NATIVEDACOUTVOLL 0x0084 +#define YMFSB_NATIVEDACOUTVOLR 0x0086 +#define YMFSB_SPDIFOUTVOL 0x0088 +#define YMFSB_SPDIFOUTVOLL 0x0088 +#define YMFSB_SPDIFOUTVOLR 0x008A +#define YMFSB_AC3OUTVOL 0x008C +#define YMFSB_AC3OUTVOLL 0x008C +#define YMFSB_AC3OUTVOLR 0x008E +#define YMFSB_PRIADCOUTVOL 0x0090 +#define YMFSB_PRIADCOUTVOLL 0x0090 +#define YMFSB_PRIADCOUTVOLR 0x0092 +#define YMFSB_LEGACYLOOPVOL 0x0094 +#define YMFSB_LEGACYLOOPVOLL 0x0094 +#define YMFSB_LEGACYLOOPVOLR 0x0096 +#define YMFSB_NATIVEDACLOOPVOL 0x0098 +#define YMFSB_NATIVEDACLOOPVOLL 0x0098 +#define YMFSB_NATIVEDACLOOPVOLR 0x009A +#define YMFSB_SPDIFLOOPVOL 0x009C +#define YMFSB_SPDIFLOOPVOLL 0x009E +#define YMFSB_SPDIFLOOPVOLR 0x009E +#define YMFSB_AC3LOOPVOL 0x00A0 +#define YMFSB_AC3LOOPVOLL 0x00A0 +#define YMFSB_AC3LOOPVOLR 0x00A2 +#define YMFSB_PRIADCLOOPVOL 0x00A4 +#define YMFSB_PRIADCLOOPVOLL 0x00A4 +#define YMFSB_PRIADCLOOPVOLR 0x00A6 +#define YMFSB_NATIVEADCINVOL 0x00A8 +#define YMFSB_NATIVEADCINVOLL 0x00A8 +#define YMFSB_NATIVEADCINVOLR 0x00AA +#define YMFSB_NATIVEDACINVOL 0x00AC +#define YMFSB_NATIVEDACINVOLL 0x00AC +#define YMFSB_NATIVEDACINVOLR 0x00AE +#define YMFSB_BUF441OUTVOL 0x00B0 +#define YMFSB_BUF441OUTVOLL 0x00B0 +#define YMFSB_BUF441OUTVOLR 0x00B2 +#define YMFSB_BUF441LOOPVOL 0x00B4 +#define YMFSB_BUF441LOOPVOLL 0x00B4 +#define YMFSB_BUF441LOOPVOLR 0x00B6 +#define YMFSB_SPDIFOUTVOL2 0x00B8 +#define YMFSB_SPDIFOUTVOL2L 0x00B8 +#define YMFSB_SPDIFOUTVOL2R 0x00BA +#define YMFSB_SPDIFLOOPVOL2 0x00BC +#define YMFSB_SPDIFLOOPVOL2L 0x00BC +#define YMFSB_SPDIFLOOPVOL2R 0x00BE +#define YMFSB_ADCSLOTSR 0x00C0 +#define YMFSB_RECSLOTSR 0x00C4 +#define YMFSB_ADCFORMAT 0x00C8 +#define YMFSB_RECFORMAT 0x00CC +#define YMFSB_P44SLOTSR 0x00D0 +#define YMFSB_STATUS 0x0100 +#define YMFSB_CTRLSELECT 0x0104 +#define YMFSB_MODE 0x0108 +#define YMFSB_SAMPLECOUNT 0x010C +#define YMFSB_NUMOFSAMPLES 0x0110 +#define YMFSB_CONFIG 0x0114 +#define YMFSB_PLAYCTRLSIZE 0x0140 +#define YMFSB_RECCTRLSIZE 0x0144 +#define YMFSB_EFFCTRLSIZE 0x0148 +#define YMFSB_WORKSIZE 0x014C +#define YMFSB_MAPOFREC 0x0150 +#define YMFSB_MAPOFEFFECT 0x0154 +#define YMFSB_PLAYCTRLBASE 0x0158 +#define YMFSB_RECCTRLBASE 0x015C +#define YMFSB_EFFCTRLBASE 0x0160 +#define YMFSB_WORKBASE 0x0164 +#define YMFSB_DSPINSTRAM 0x1000 +#define YMFSB_CTRLINSTRAM 0x4000 + + +/* ---------------------------------------------------------------------- */ + +#define MAX_CARDS 4 + +#define PFX "ymf_sb: " + +#define YMFSB_VERSION "0.0.4" +#define YMFSB_CARD_NAME "YMF7xx Legacy Audio driver " YMFSB_VERSION + +#ifdef SUPPORT_UART401_MIDI +#if 0 +# define ymf7xxsb_probe_midi probe_uart401 +# define ymf7xxsb_attach_midi attach_uart401 +# define ymf7xxsb_unload_midi unload_uart401 +#else +# define ymf7xxsb_probe_midi probe_sbmpu +# define ymf7xxsb_attach_midi attach_sbmpu +# define ymf7xxsb_unload_midi unload_sbmpu +#endif +#endif + +/* ---------------------------------------------------------------------- */ + +static struct address_info sb_data[MAX_CARDS]; +static struct address_info opl3_data[MAX_CARDS]; +#ifdef SUPPORT_UART401_MIDI +static struct address_info mpu_data[MAX_CARDS]; +#endif +static unsigned cards = 0; +static unsigned short *ymfbase[MAX_CARDS]; + +/* ---------------------------------------------------------------------- */ + +#ifdef MODULE +#ifdef SUPPORT_UART401_MIDI +static int mpu_io = 0; +#endif +static int synth_io = 0; +static int io = 0; +static int dma = 0; +static int master_vol = -1; +static int spdif_out = 0; +#ifdef SUPPORT_UART401_MIDI +MODULE_PARM(mpu_io, "i"); +#endif +MODULE_PARM(synth_io, "i"); +MODULE_PARM(io,"i"); +MODULE_PARM(dma,"i"); +MODULE_PARM(master_vol,"i"); +MODULE_PARM(spdif_out,"i"); +#else +#ifdef SUPPORT_UART401_MIDI +static int mpu_io = 0x330; +#endif +static int synth_io = 0x388; +static int io = 0x220; +static int dma = 1; +static int master_vol = 80; +static int spdif_out = 0; +#endif + +/* ---------------------------------------------------------------------- */ + +static int readRegWord( int adr ) { + + if (ymfbase[cards]==NULL) return 0; + + return readw(ymfbase[cards]+adr/2); +} + +static void writeRegWord( int adr, int val ) { + + if (ymfbase[cards]==NULL) return; + + writew((unsigned short)(val&0xffff), ymfbase[cards] + adr/2); + + return; +} + +static int readRegDWord( int adr ) { + + if (ymfbase[cards]==NULL) return 0; + + return (readl(ymfbase[cards]+adr/2)); +} + +static void writeRegDWord( int adr, int val ) { + + if (ymfbase[cards]==NULL) return; + + writel((unsigned int)(val&0xffffffff), ymfbase[cards]+adr/2); + + return; +} + +/* ---------------------------------------------------------------------- */ + +static int checkPrimaryBusy( void ) +{ + int timeout=0; + + while ( timeout++ < YMFSB_AC97TIMEOUT ) + { + if ( (readRegWord(YMFSB_PRISTATUSADR) & 0x8000) == 0x0000 ) + return 0; + } + return -1; +} + +static int writeAc97( int adr, unsigned short val ) +{ + + if ( adr > 0x7f || adr < 0x00 ) return -1; + + if ( checkPrimaryBusy() ) return -1; + +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "AC97 0x%0x = 0x%0x\n",adr,val); +#endif + + writeRegWord( YMFSB_AC97CMDADR, 0x0000 | adr ); + writeRegWord( YMFSB_AC97CMDDATA, val ); + + return 0; +} + +static int checkCodec( struct pci_dev *pcidev ) +{ + u8 tmp8; + + pci_read_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, &tmp8); + if ( tmp8 & 0x03 ) { + pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8&0xfc); + mdelay(YMFSB_RESET_DELAY); + pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8|0x03); + mdelay(YMFSB_RESET_DELAY); + pci_write_config_byte(pcidev, YMFSB_PCIR_DSXGCTRL, tmp8&0xfc); + mdelay(YMFSB_RESET_DELAY); + } + + if ( checkPrimaryBusy() ) return -1; + + return 0; +} + +static int setupLegacyIO( struct pci_dev *pcidev ) +{ + int v; + int sbio=0,mpuio=0,oplio=0,dma=0; + + switch(sb_data[cards].io_base) { + case 0x220: + sbio = 0; + break; + case 0x240: + sbio = 1; + break; + case 0x260: + sbio = 2; + break; + case 0x280: + sbio = 3; + break; + default: + return -1; + break; + } +#ifdef YMF_DEBUG + printk(PFX "set SBPro I/O at 0x%x\n",sb_data[cards].io_base); +#endif + +#ifdef SUPPORT_UART401_MIDI + switch(mpu_data[cards].io_base) { + case 0x330: + mpuio = 0; + break; + case 0x300: + mpuio = 1; + break; + case 0x332: + mpuio = 2; + break; + case 0x334: + mpuio = 3; + break; + default: + mpuio = 0; + break; + } +# ifdef YMF_DEBUG + printk(PFX "set MPU401 I/O at 0x%x\n",mpu_data[cards].io_base); +# endif +#endif + + switch(opl3_data[cards].io_base) { + case 0x388: + oplio = 0; + break; + case 0x398: + oplio = 1; + break; + case 0x3a0: + oplio = 2; + break; + case 0x3a8: + oplio = 3; + break; + default: + return -1; + break; + } +#ifdef YMF_DEBUG + printk(PFX "set OPL3 I/O at 0x%x\n",opl3_data[cards].io_base); +#endif + + dma = sb_data[cards].dma; +#ifdef YMF_DEBUG + printk(PFX "set DMA address at 0x%x\n",sb_data[cards].dma); +#endif + + v = 0x0000 | ((dma<<6)&0x03) | 0x003f; + pci_write_config_word(pcidev, YMFSB_PCIR_LEGCTRL, v); +#ifdef YMF_DEBUG + printk(PFX "LEGCTRL: 0x%x\n",v); +#endif + switch( pcidev->device ) { + case PCI_DEVICE_ID_YMF724: + case PCI_DEVICE_ID_YMF740: + case PCI_DEVICE_ID_YMF724F: + case PCI_DEVICE_ID_YMF740C: + v = 0x8800 | ((mpuio<<4)&0x03) | ((sbio<<2)&0x03) | (oplio&0x03); + pci_write_config_word(pcidev, YMFSB_PCIR_ELEGCTRL, v); +#ifdef YMF_DEBUG + printk(PFX "ELEGCTRL: 0x%x\n",v); +#endif + break; + + case PCI_DEVICE_ID_YMF744: + case PCI_DEVICE_ID_YMF754: + v = 0x8800; + pci_write_config_word(pcidev, YMFSB_PCIR_ELEGCTRL, v); +#ifdef YMF_DEBUG + printk(PFX "ELEGCTRL: 0x%x\n",v); +#endif + pci_write_config_word(pcidev, YMFSB_PCIR_OPLADR, opl3_data[cards].io_base); + pci_write_config_word(pcidev, YMFSB_PCIR_SBADR, sb_data[cards]. +io_base); +#ifdef SUPPORT_UART401_MIDI + pci_write_config_word(pcidev, YMFSB_PCIR_MPUADR, mpu_data[cards].io_base); +#endif + break; + + default: + printk(KERN_ERR PFX "Invalid device ID: %d\n",pcidev->device); + return -1; + break; + } + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static void enableDSP( void ) +{ + writeRegDWord( YMFSB_CONFIG, 0x00000001 ); + return; +} + +static void disableDSP( void ) +{ + int val; + int i; + + val = readRegDWord( YMFSB_CONFIG ); + if ( val ) { + writeRegDWord( YMFSB_CONFIG, 0 ); + } + + i=0; + while( ++i < YMFSB_WORKBITTIMEOUT ) { + val = readRegDWord(YMFSB_STATUS); + if ( (val & 0x00000002) == 0x00000000 ) break; + } + + return; +} + +static int setupInstruction( struct pci_dev *pcidev ) +{ + int i; + int val; + + writeRegDWord( YMFSB_NATIVEDACOUTVOL, 0 ); /* mute dac */ + disableDSP(); + + writeRegDWord( YMFSB_MODE, 0x00010000 ); + + /* DS-XG Software Reset */ + writeRegDWord( YMFSB_MODE, 0x00000000 ); + writeRegDWord( YMFSB_MAPOFREC, 0x00000000 ); + writeRegDWord( YMFSB_MAPOFEFFECT, 0x00000000 ); + writeRegDWord( YMFSB_PLAYCTRLBASE, 0x00000000 ); + writeRegDWord( YMFSB_RECCTRLBASE, 0x00000000 ); + writeRegDWord( YMFSB_EFFCTRLBASE, 0x00000000 ); + + val = readRegWord( YMFSB_GLOBALCTRL ); + writeRegWord( YMFSB_GLOBALCTRL, (val&~0x0007) ); + + /* setup DSP instruction code */ + for ( i=0 ; i<YMFSB_DSPLENGTH ; i+=4 ) { + writeRegDWord( YMFSB_DSPINSTRAM+i, DspInst[i>>2] ); + } + + switch( pcidev->device ) { + case PCI_DEVICE_ID_YMF724: + case PCI_DEVICE_ID_YMF740: + /* setup Control instruction code */ + for ( i=0 ; i<YMFSB_CTRLLENGTH ; i+=4 ) { + writeRegDWord( YMFSB_CTRLINSTRAM+i, CntrlInst[i>>2] ); + } + break; + + case PCI_DEVICE_ID_YMF724F: + case PCI_DEVICE_ID_YMF740C: + case PCI_DEVICE_ID_YMF744: + case PCI_DEVICE_ID_YMF754: + /* setup Control instruction code */ + for ( i=0 ; i<YMFSB_CTRLLENGTH ; i+=4 ) { + writeRegDWord( YMFSB_CTRLINSTRAM+i, CntrlInst1E[i>>2] ); + } + break; + + default: + return -1; + } + + enableDSP(); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static int __init ymf7xx_init(struct pci_dev *pcidev) +{ + unsigned short v; + + /* Read hardware information */ +#ifdef YMF_DEBUG + unsigned int dv; + pci_read_config_word(pcidev, YMFSB_PCIR_VENDORID, &v); + printk(KERN_INFO PFX "Vendor ID = 0x%x\n",v); + pci_read_config_word(pcidev, YMFSB_PCIR_DEVICEID, &v); + printk(KERN_INFO PFX "Device ID = 0x%x\n",v); + pci_read_config_word(pcidev, YMFSB_PCIR_REVISIONID, &v); + printk(KERN_INFO PFX "Revision ID = 0x%x\n",v&0xff); + pci_read_config_dword(pcidev, YMFSB_PCIR_BASEADDR, &dv); + printk(KERN_INFO PFX "Base address = 0x%x\n",dv); + pci_read_config_word(pcidev, YMFSB_PCIR_IRQ, &v); + printk(KERN_INFO PFX "IRQ line = 0x%x\n",v&0xff); +#endif + + /* enables memory space access / bus mastering */ + pci_read_config_word(pcidev, YMFSB_PCIR_CMD, &v); + pci_write_config_word(pcidev, YMFSB_PCIR_CMD, v|0x06); + + /* check codec */ +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "check codec...\n"); +#endif + if (checkCodec(pcidev)) return -1; + + /* setup legacy I/O */ +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "setup legacy I/O...\n"); +#endif + if (setupLegacyIO(pcidev)) return -1; + + /* setup instruction code */ +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "setup instructions...\n"); +#endif + if (setupInstruction(pcidev)) return -1; + + /* AC'97 setup */ +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "setup AC'97...\n"); +#endif + if ( writeAc97(AC97_RESET ,0x0000) ) /* Reset */ + return -1; + if ( writeAc97(AC97_MASTER_VOL_STEREO,0x0000) ) /* Master Volume */ + return -1; + + v = 31*(100-master_vol)/100; + v = (v<<8 | v)&0x7fff; + if ( writeAc97(AC97_PCMOUT_VOL ,v ) ) /* PCM out Volume */ + return -1; + +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "setup Legacy Volume...\n"); +#endif + /* Legacy Audio Output Volume L & R ch */ + writeRegDWord( YMFSB_LEGACYOUTVOL, 0x3fff3fff ); + +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "setup SPDIF output control...\n"); +#endif + /* SPDIF Output control */ + v = spdif_out != 0 ? 0x0001 : 0x0000; + writeRegWord( YMFSB_SPDIFOUTCTRL, v ); + /* no copyright protection, + sample-rate converted, + re-recorded software comercially available (the 1st generation), + original */ + writeRegWord( YMFSB_SPDIFOUTSTATUS, 0x9a04 ); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static void __init ymf7xxsb_attach_sb(struct address_info *hw_config) +{ + if(!sb_dsp_init(hw_config)) + hw_config->slots[0] = -1; +} + +static int __init ymf7xxsb_probe_sb(struct address_info *hw_config) +{ + if (check_region(hw_config->io_base, 16)) + { + printk(KERN_DEBUG PFX "SBPro port 0x%x is already in use\n", + hw_config->io_base); + return 0; + } + return sb_dsp_detect(hw_config, SB_PCI_YAMAHA, 0, NULL); +} + + +static void ymf7xxsb_unload_sb(struct address_info *hw_config, int unload_mpu) +{ + if(hw_config->slots[0]!=-1) + sb_dsp_unload(hw_config, unload_mpu); +} + +/* ---------------------------------------------------------------------- */ + +enum chip_types { + CH_YMF724 = 0, + CH_YMF724F, + CH_YMF740, + CH_YMF740C, + CH_YMF744, + CH_YMF754, +}; + +/* directly indexed by chip_types enum above */ +/* note we keep this a struct to ease adding + * other per-board or per-chip info here */ +struct { + const char *devicename; +} devicetable[] __initdata = +{ + { "YMF724A-E" }, + { "YMF724F" }, + { "YMF740A-B" }, + { "YMF740C" }, + { "YMF744" }, + { "YMF754" }, +}; + +static struct pci_device_id ymf7xxsb_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF724 }, + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF724F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF724F }, + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF740, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF740 }, + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF740C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF740C }, + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF744, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF744 }, + { PCI_VENDOR_ID_YAMAHA, PCI_DEVICE_ID_YMF754, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_YMF754 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ymf7xxsb_pci_tbl); + +static int __init ymf7xxsb_init_one (struct pci_dev *pcidev, const struct pci_device_id *ent) +{ + + const char *devicename; + unsigned long iobase; + + if (cards == MAX_CARDS) { + printk (KERN_DEBUG PFX "maximum number of cards reached\n"); + return -ENODEV; + } + + if ( pcidev->irq == 0 ) return -ENODEV; + iobase = pci_resource_start (pcidev, 0); + if ( iobase == 0x00000000 ) return -ENODEV; + + devicename = devicetable[ent->driver_data].devicename; + + /* remap memory mapped I/O onto kernel virtual memory */ + if ( (ymfbase[cards] = ioremap_nocache(iobase, YMFSB_REGSIZE)) == 0 ) + { + printk(KERN_ERR PFX "ioremap (0x%lx) returns zero\n", iobase); + return -ENODEV; + } + printk(KERN_INFO PFX "found %s at 0x%lx\n", devicename, iobase); +#ifdef YMF_DEBUG + printk(KERN_INFO PFX "remappling to 0x%p\n", ymfbase[cards]); +#endif + + memset (&sb_data[cards], 0, sizeof (struct address_info)); + memset (&opl3_data[cards], 0, sizeof (struct address_info)); +#ifdef SUPPORT_UART401_MIDI + memset (&mpu_data[cards], 0, sizeof (struct address_info)); +#endif + + sb_data[cards].name = YMFSB_CARD_NAME; + opl3_data[cards].name = YMFSB_CARD_NAME; +#ifdef SUPPORT_UART401_MIDI + mpu_data[cards].name = YMFSB_CARD_NAME; +#endif + + sb_data[cards].card_subtype = MDL_YMPCI; + + if ( io == 0 ) io = 0x220; + sb_data[cards].io_base = io; + sb_data[cards].irq = pcidev->irq; + sb_data[cards].dma = dma; + + if ( synth_io == 0 ) synth_io = 0x388; + opl3_data[cards].io_base = synth_io; + opl3_data[cards].irq = -1; + +#ifdef SUPPORT_UART401_MIDI + if ( mpu_io == 0 ) mpu_io = 0x330; + mpu_data[cards].io_base = mpu_io; + mpu_data[cards].irq = -1; +#endif + + if ( ymf7xx_init(pcidev) ) { + printk (KERN_ERR PFX + "Cannot initialize %s, aborting\n", + devicename); + return -ENODEV; + } + + /* register legacy SoundBlaster Pro */ + if (!ymf7xxsb_probe_sb(&sb_data[cards])) { + printk (KERN_ERR PFX + "SB probe at 0x%X failed, aborting\n", + io); + return -ENODEV; + } + ymf7xxsb_attach_sb (&sb_data[cards]); + +#ifdef SUPPORT_UART401_MIDI + /* register legacy MIDI */ + if ( mpu_io > 0 && 0) + { + if (!ymf7xxsb_probe_midi (&mpu_data[cards])) { + printk (KERN_ERR PFX + "MIDI probe @ 0x%X failed, aborting\n", + mpu_io); + ymf7xxsb_unload_sb (&sb_data[cards], 0); + return -ENODEV; + } + ymf7xxsb_attach_midi (&mpu_data[cards]); + } +#endif + + /* register legacy OPL3 */ + + cards++; + return 0; +} + +static struct pci_driver ymf7xxsb_driver = { + name: "ymf7xxsb", + id_table: ymf7xxsb_pci_tbl, + probe: ymf7xxsb_init_one, +}; + +static int __init init_ymf7xxsb_module(void) +{ + int i; + + /* + * Binds us to the sound subsystem + */ + SOUND_LOCK; + + if ( master_vol < 0 ) master_vol = 50; + if ( master_vol > 100 ) master_vol = 100; + + for (i=0 ; i<MAX_CARDS ; i++ ) + ymfbase[i] = NULL; + + i = pci_module_init (&ymf7xxsb_driver); + if (i < 0) { + SOUND_LOCK_END; + return i; + } + + printk (KERN_INFO PFX YMFSB_CARD_NAME " loaded\n"); + + return 0; +} + +static void __exit free_iomaps( void ) +{ + int i; + + for ( i=0 ; i<MAX_CARDS ; i++ ) { + if ( ymfbase[i]!=NULL ) + iounmap(ymfbase[i]); + } + + return; +} + +static void __exit cleanup_ymf7xxsb_module(void) +{ + int i; + + for (i = 0; i < cards; i++) { +#ifdef SUPPORT_UART401_MIDI + ymf7xxsb_unload_sb (&sb_data[i], 0); + ymf7xxsb_unload_midi (&mpu_data[i]); +#else + ymf7xxsb_unload_sb (&sb_data[i], 1); +#endif + } + + free_iomaps(); + + /* + * Final clean up with the sound layer + */ + SOUND_LOCK_END; +} + +MODULE_AUTHOR("Daisuke Nagano, breeze.nagano@nifty.ne.jp"); +MODULE_DESCRIPTION("YMF7xx Legacy Audio Driver"); + +module_init(init_ymf7xxsb_module); +module_exit(cleanup_ymf7xxsb_module); + |