|  | /* | 
|  | * 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 compatible 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 <linux/config.h> | 
|  | #include <linux/init.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/gameport.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include "sound_config.h" | 
|  |  | 
|  | #include "ad1848.h" | 
|  | #include "sb.h" | 
|  | #include "mpu401.h" | 
|  |  | 
|  | #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE)) | 
|  | #define SUPPORT_JOYSTICK 1 | 
|  | #endif | 
|  |  | 
|  | static int      mad16_conf; | 
|  | static int      mad16_cdsel; | 
|  | static DEFINE_SPINLOCK(lock); | 
|  |  | 
|  | #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;	/* "     "           "    C924 */ | 
|  | static int	debug;  	/* debugging output */ | 
|  |  | 
|  | #ifdef DDB | 
|  | #undef DDB | 
|  | #endif | 
|  | #define DDB(x) do {if (debug) x;} while (0) | 
|  |  | 
|  | static unsigned char mad_read(int port) | 
|  | { | 
|  | unsigned long flags; | 
|  | unsigned char tmp; | 
|  |  | 
|  | spin_lock_irqsave(&lock,flags); | 
|  |  | 
|  | 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); | 
|  | spin_unlock_irqrestore(&lock,flags); | 
|  |  | 
|  | return tmp; | 
|  | } | 
|  |  | 
|  | static void mad_write(int port, int value) | 
|  | { | 
|  | unsigned long   flags; | 
|  |  | 
|  | spin_lock_irqsave(&lock,flags); | 
|  |  | 
|  | 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); | 
|  | spin_unlock_irqrestore(&lock,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) | 
|  | { | 
|  | /* | 
|  | * 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; | 
|  | } | 
|  | /* | 
|  | * 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 > 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 void __init init_c930(struct address_info *hw_config, int base) | 
|  | { | 
|  | unsigned char cfg = 0; | 
|  |  | 
|  | cfg |= (0x0f & mad16_conf); | 
|  |  | 
|  | 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; | 
|  | } | 
|  | cfg |= base << 4; | 
|  | 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; | 
|  |  | 
|  | if(mad16_cdsel & 0x20) | 
|  | mad_write(MC4_PORT, 0x62|cfg);  /* opl4 */ | 
|  | else | 
|  | mad_write(MC4_PORT, 0x52|cfg);  /* opl3 */ | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | unsigned char tmp; | 
|  | unsigned char cs4231_mode = 0; | 
|  |  | 
|  | int ad_flags = 0; | 
|  |  | 
|  | 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 dma = hw_config->dma, dma2 = hw_config->dma2; | 
|  | unsigned char dma2_bit = 0; | 
|  | int base; | 
|  | struct resource *ports; | 
|  |  | 
|  | mad16_osp = hw_config->osp; | 
|  |  | 
|  | switch (hw_config->io_base) { | 
|  | case 0x530: | 
|  | base = 0; | 
|  | break; | 
|  | case 0xe80: | 
|  | base = 1; | 
|  | break; | 
|  | case 0xf40: | 
|  | base = 2; | 
|  | break; | 
|  | case 0x604: | 
|  | base = 3; | 
|  | break; | 
|  | default: | 
|  | printk(KERN_ERR "MAD16/Mozart: Bad WSS base address 0x%x\n", hw_config->io_base); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (dma != 0 && dma != 1 && dma != 3) { | 
|  | printk(KERN_ERR "MSS: Bad DMA %d\n", dma); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *    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; | 
|  |  | 
|  | switch (hw_config->irq) { | 
|  | case 7: | 
|  | bits = 8; | 
|  | break; | 
|  | case 9: | 
|  | bits = 0x10; | 
|  | break; | 
|  | case 10: | 
|  | bits = 0x18; | 
|  | break; | 
|  | case 12: | 
|  | bits = 0x20; | 
|  | break; | 
|  | case 5:	/* Also IRQ5 is possible on C930 */ | 
|  | if (board_type == C930 || c924pnp) { | 
|  | bits = 0x28; | 
|  | break; | 
|  | } | 
|  | default: | 
|  | printk(KERN_ERR "MAD16/Mozart: Bad IRQ %d\n", hw_config->irq); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | ports = request_region(hw_config->io_base + 4, 4, "ad1848"); | 
|  | if (!ports) { | 
|  | printk(KERN_ERR "MSS: I/O port conflict\n"); | 
|  | return 0; | 
|  | } | 
|  | if (!request_region(hw_config->io_base, 4, "mad16 WSS config")) { | 
|  | release_region(hw_config->io_base + 4, 4); | 
|  | printk(KERN_ERR "MSS: I/O port conflict\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (board_type == C930) { | 
|  | init_c930(hw_config, base); | 
|  | goto got_it; | 
|  | } | 
|  |  | 
|  | 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 */ | 
|  | tmp |= base << 4;	/* WSS port select bits */ | 
|  |  | 
|  | /* | 
|  | * Set optional CD-ROM and joystick settings. | 
|  | */ | 
|  |  | 
|  | tmp &= ~0x0f; | 
|  | tmp |= (mad16_conf & 0x0f);	/* CD-ROM and joystick bits */ | 
|  | mad_write(MC1_PORT, tmp); | 
|  |  | 
|  | tmp = mad16_cdsel; | 
|  | 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(ports, &ad_flags, mad16_osp)) | 
|  | goto fail; | 
|  |  | 
|  | 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))); | 
|  | } | 
|  |  | 
|  | got_it: | 
|  | ad_flags = 0; | 
|  | if (!ad1848_detect(ports, &ad_flags, mad16_osp)) | 
|  | goto fail; | 
|  |  | 
|  | if (!wss_init(hw_config)) | 
|  | goto fail; | 
|  |  | 
|  | /* | 
|  | * Set the IRQ and DMA addresses. | 
|  | */ | 
|  |  | 
|  | 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", ports, | 
|  | hw_config->irq, | 
|  | dma, | 
|  | dma2, 0, | 
|  | hw_config->osp, | 
|  | THIS_MODULE); | 
|  | return 1; | 
|  |  | 
|  | fail: | 
|  | release_region(hw_config->io_base + 4, 4); | 
|  | release_region(hw_config->io_base, 4); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int __init probe_mad16_mpu(struct address_info *hw_config) | 
|  | { | 
|  | unsigned char tmp; | 
|  |  | 
|  | 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 (!request_region(hw_config->io_base, 16, "soundblaster")) | 
|  | return 0; | 
|  | if (!sb_dsp_detect(hw_config, 0, 0, NULL)) { | 
|  | release_region(hw_config->io_base, 16); | 
|  | 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_param(mpu_io, int, 0); | 
|  | module_param(mpu_irq, int, 0); | 
|  | module_param(io, int, 0); | 
|  | module_param(dma, int, 0); | 
|  | module_param(dma16, int, 0); | 
|  | module_param(irq, int, 0); | 
|  | module_param(cdtype, int, 0); | 
|  | module_param(cdirq, int, 0); | 
|  | module_param(cdport, int, 0); | 
|  | module_param(cddma, int, 0); | 
|  | module_param(opl4, int, 0); | 
|  | module_param(joystick, bool, 0); | 
|  | module_param(debug, bool, 0644); | 
|  |  | 
|  | 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 | 
|  | }; | 
|  |  | 
|  | #ifdef SUPPORT_JOYSTICK | 
|  |  | 
|  | static struct gameport *gameport; | 
|  |  | 
|  | static int __devinit mad16_register_gameport(int io_port) | 
|  | { | 
|  | if (!request_region(io_port, 1, "mad16 gameport")) { | 
|  | printk(KERN_ERR "mad16: gameport address 0x%#x already in use\n", io_port); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | gameport = gameport_allocate_port(); | 
|  | if (!gameport) { | 
|  | printk(KERN_ERR "mad16: can not allocate memory for gameport\n"); | 
|  | release_region(io_port, 1); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | gameport_set_name(gameport, "MAD16 Gameport"); | 
|  | gameport_set_phys(gameport, "isa%04x/gameport0", io_port); | 
|  | gameport->io = io_port; | 
|  |  | 
|  | gameport_register_port(gameport); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static inline void mad16_unregister_gameport(void) | 
|  | { | 
|  | if (gameport) { | 
|  | /* the gameport was initialized so we must free it up */ | 
|  | gameport_unregister_port(gameport); | 
|  | gameport = NULL; | 
|  | release_region(0x201, 1); | 
|  | } | 
|  | } | 
|  | #else | 
|  | static inline int mad16_register_gameport(int io_port) { return -ENOSYS; } | 
|  | static inline void mad16_unregister_gameport(void) { } | 
|  | #endif | 
|  |  | 
|  | static int __devinit 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"); | 
|  |  | 
|  | 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 (!request_region(MC0_PORT, 12, "mad16")) | 
|  | return -EBUSY; | 
|  |  | 
|  | if (!probe_mad16(&cfg)) { | 
|  | release_region(MC0_PORT, 12); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | cfg_mpu.io_base = mpu_io; | 
|  | cfg_mpu.irq = mpu_irq; | 
|  |  | 
|  | found_mpu = probe_mad16_mpu(&cfg_mpu); | 
|  |  | 
|  | if (joystick) | 
|  | mad16_register_gameport(0x201); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void __exit cleanup_mad16(void) | 
|  | { | 
|  | if (found_mpu) | 
|  | unload_mad16_mpu(&cfg_mpu); | 
|  | mad16_unregister_gameport(); | 
|  | unload_mad16(&cfg); | 
|  | release_region(MC0_PORT, 12); | 
|  | } | 
|  |  | 
|  | module_init(init_mad16); | 
|  | module_exit(cleanup_mad16); | 
|  |  | 
|  | #ifndef MODULE | 
|  | static int __init setup_mad16(char *str) | 
|  | { | 
|  | /* io, irq */ | 
|  | int ints[8]; | 
|  |  | 
|  | 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]; | 
|  | joystick = ints[7]; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | __setup("mad16=", setup_mad16); | 
|  | #endif | 
|  | MODULE_LICENSE("GPL"); |