patch-2.2.16 linux/drivers/sound/ymf_sb.c

Next file: linux/drivers/video/atyfb.c
Previous file: linux/drivers/sound/vidc_audio.c
Back to the patch index
Back to the overall index

diff -urN v2.2.15/linux/drivers/sound/ymf_sb.c linux/drivers/sound/ymf_sb.c
@@ -0,0 +1,893 @@
+/*
+  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>
+
+  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/version.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+#include "sound_config.h"
+#include "soundmodule.h"
+#include "sb.h"
+#include "ac97.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);
+		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);
+}
+
+
+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);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int __init ymf7xxsb_install (struct pci_dev *pcidev)
+{
+	struct {
+		unsigned short deviceid;
+		char           *devicename;
+	} devicetable[] = 
+	{
+		{ PCI_DEVICE_ID_YMF724,  "YMF724A-E" },
+		{ PCI_DEVICE_ID_YMF724F, "YMF724F" },
+		{ PCI_DEVICE_ID_YMF740,  "YMF740A-B" },
+		{ PCI_DEVICE_ID_YMF740C, "YMF740C" },
+		{ PCI_DEVICE_ID_YMF744,  "YMF744" },
+		{ PCI_DEVICE_ID_YMF754,  "YMF754" },
+	};
+
+	char		*devicename = "unknown";
+	int		i;
+	unsigned long   iobase;
+
+	if ( pcidev->irq == 0 ) return -1;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13)
+	iobase = pcidev->base_address[0]&PCI_BASE_ADDRESS_MEM_MASK;
+#else
+	iobase = pcidev->resource[0].start&PCI_BASE_ADDRESS_MEM_MASK;
+#endif
+	if ( iobase == 0x00000000 ) return -1;
+
+	for ( i=0 ; i<sizeof(devicetable) / sizeof(devicetable[0]); i++ )
+	{
+		if (devicetable[i].deviceid == pcidev->device)
+		{
+			devicename = devicetable[i].devicename;
+			break;
+		}
+	}
+
+	/* 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 -1;
+	}
+	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 -1;
+	}
+
+	/* 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 -1;
+	}
+	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 -1;
+		}
+		ymf7xxsb_attach_midi (&mpu_data[cards]);
+	}
+#endif
+
+	/* register legacy OPL3 */
+
+	cards++;	
+	return 0;
+}
+
+static int __init probe_ymf7xxsb (void)
+{
+	struct pci_dev *pcidev = NULL;
+	int i;
+
+	for (i=0 ; i<MAX_CARDS ; i++ )
+		ymfbase[i] = NULL;
+
+	while ( pcidev == NULL && (
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF724, pcidev)) ||
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF724F,pcidev)) ||
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF740, pcidev)) ||
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF740C,pcidev)) ||
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF744, pcidev)) ||
+	       (pcidev = pci_find_device (PCI_VENDOR_ID_YAMAHA,
+					  PCI_DEVICE_ID_YMF754, pcidev)))) {
+		  if (ymf7xxsb_install (pcidev) != 0) {
+			  printk (KERN_ERR PFX "audio init failed\n");
+			  return -1;
+		  }
+
+		  if (cards == MAX_CARDS) {
+		  	  printk (KERN_DEBUG PFX "maximum number of cards reached\n");
+			  break;
+		  }
+	}
+
+	return 0;
+}
+
+static void free_iomaps( void )
+{
+	int i;
+
+	for ( i=0 ; i<MAX_CARDS ; i++ ) {
+		if ( ymfbase[i]!=NULL )
+			iounmap(ymfbase[i]);
+	}
+
+	return;
+}
+
+static int __init init_ymf7xxsb_module(void)
+{
+	if ( master_vol < 0 ) master_vol  = 50;
+	if ( master_vol > 100 ) master_vol = 100;
+
+	if (!pci_present ()) {
+		printk (KERN_DEBUG PFX "PCI not present, exiting\n");
+		return -ENODEV;
+	}
+
+	if (probe_ymf7xxsb() != 0) {
+		printk(KERN_ERR PFX "probe failed, aborting\n");
+		/* XXX unload cards registered so far, if any */
+		free_iomaps();
+		return -ENODEV;
+	}
+
+	if (cards == 0) {
+		printk(KERN_DEBUG PFX "No chips found, aborting\n");
+		free_iomaps();
+		return -ENODEV;
+	}
+
+	printk (KERN_INFO PFX YMFSB_CARD_NAME " loaded\n");
+	
+	/*
+	 *	Binds us to the sound subsystem	
+	 */
+	SOUND_LOCK;
+	return 0;
+}
+
+static void 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;
+}
+
+#ifdef MODULE
+
+MODULE_AUTHOR("Daisuke Nagano, breeze.nagano@nifty.ne.jp");
+MODULE_DESCRIPTION("YMF7xx Legacy Audio Driver");
+
+int init_module(void)
+{
+	return init_ymf7xxsb_module();
+}
+
+void cleanup_module(void)
+{
+	cleanup_ymf7xxsb_module();
+}
+
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)