1 2 3#include <linux/init.h> 4#include <linux/module.h> 5 6#undef SB_OK 7 8#include "sound_config.h" 9 10#include "ad1848.h" 11#include "mpu401.h" 12 13#ifdef SB_OK 14#include "sb.h" 15static int sb_initialized = 0; 16#endif 17 18static int kilroy_was_here = 0; /* Don't detect twice */ 19static int mpu_initialized = 0; 20 21static int *opl3sa_osp = NULL; 22 23static unsigned char opl3sa_read(int addr) 24{ 25 unsigned long flags; 26 unsigned char tmp; 27 28 save_flags(flags); 29 cli(); 30 outb((0x1d), 0xf86); /* password */ 31 outb(((unsigned char) addr), 0xf86); /* address */ 32 tmp = inb(0xf87); /* data */ 33 restore_flags(flags); 34 35 return tmp; 36} 37 38static void opl3sa_write(int addr, int data) 39{ 40 unsigned long flags; 41 42 save_flags(flags); 43 cli(); 44 outb((0x1d), 0xf86); /* password */ 45 outb(((unsigned char) addr), 0xf86); /* address */ 46 outb(((unsigned char) data), 0xf87); /* data */ 47 restore_flags(flags); 48} 49 50static int __init opl3sa_detect(void) 51{ 52 int tmp; 53 54 if (((tmp = opl3sa_read(0x01)) & 0xc4) != 0x04) 55 { 56 DDB(printk("OPL3-SA detect error 1 (%x)\n", opl3sa_read(0x01))); 57 /* return 0; */ 58 } 59 60 /* 61 * Check that the password feature has any effect 62 */ 63 64 if (inb(0xf87) == tmp) 65 { 66 DDB(printk("OPL3-SA detect failed 2 (%x/%x)\n", tmp, inb(0xf87))); 67 return 0; 68 } 69 tmp = (opl3sa_read(0x04) & 0xe0) >> 5; 70 71 if (tmp != 0 && tmp != 1) 72 { 73 DDB(printk("OPL3-SA detect failed 3 (%d)\n", tmp)); 74 return 0; 75 } 76 DDB(printk("OPL3-SA mode %x detected\n", tmp)); 77 78 opl3sa_write(0x01, 0x00); /* Disable MSS */ 79 opl3sa_write(0x02, 0x00); /* Disable SB */ 80 opl3sa_write(0x03, 0x00); /* Disable MPU */ 81 82 return 1; 83} 84 85/* 86 * Probe and attach routines for the Windows Sound System mode of 87 * OPL3-SA 88 */ 89 90static int __init probe_opl3sa_wss(struct address_info *hw_config) 91{ 92 int ret; 93 unsigned char tmp = 0x24; /* WSS enable */ 94 95 if (check_region(0xf86, 2)) /* Control port is busy */ 96 return 0; 97 /* 98 * Check if the IO port returns valid signature. The original MS Sound 99 * system returns 0x04 while some cards (OPL3-SA for example) 100 * return 0x00. 101 */ 102 103 if (check_region(hw_config->io_base, 8)) 104 { 105 printk(KERN_ERR "OPL3-SA: MSS I/O port conflict (%x)\n", hw_config->io_base); 106 return 0; 107 } 108 opl3sa_osp = hw_config->osp; 109 110 if (!opl3sa_detect()) 111 { 112 printk(KERN_ERR "OSS: OPL3-SA chip not found\n"); 113 return 0; 114 } 115 116 switch (hw_config->io_base) 117 { 118 case 0x530: 119 tmp |= 0x00; 120 break; 121 case 0xe80: 122 tmp |= 0x08; 123 break; 124 case 0xf40: 125 tmp |= 0x10; 126 break; 127 case 0x604: 128 tmp |= 0x18; 129 break; 130 default: 131 printk(KERN_ERR "OSS: Unsupported OPL3-SA/WSS base %x\n", hw_config->io_base); 132 return 0; 133 } 134 135 opl3sa_write(0x01, tmp); /* WSS setup register */ 136 kilroy_was_here = 1; 137 138 ret = probe_ms_sound(hw_config); 139 if (ret) 140 request_region(0xf86, 2, "OPL3-SA"); 141 142 return ret; 143} 144 145static void __init attach_opl3sa_wss(struct address_info *hw_config) 146{ 147 int nm = num_mixers; 148 149 attach_ms_sound(hw_config, THIS_MODULE); 150 if (num_mixers > nm) /* A mixer was installed */ 151 { 152 AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD); 153 AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH); 154 AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE); 155 } 156} 157 158 159static int __init probe_opl3sa_mpu(struct address_info *hw_config) 160{ 161 unsigned char conf; 162 static signed char irq_bits[] = { 163 -1, -1, -1, -1, -1, 1, -1, 2, -1, 3, 4 164 }; 165 166 if (!kilroy_was_here) 167 return 0; /* OPL3-SA has not been detected earlier */ 168 169 if (mpu_initialized) 170 { 171 DDB(printk("OPL3-SA: MPU mode already initialized\n")); 172 return 0; 173 } 174 if (hw_config->irq > 10) 175 { 176 printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); 177 return 0; 178 } 179 if (irq_bits[hw_config->irq] == -1) 180 { 181 printk(KERN_ERR "OPL3-SA: Bad MPU IRQ %d\n", hw_config->irq); 182 return 0; 183 } 184 switch (hw_config->io_base) 185 { 186 case 0x330: 187 conf = 0x00; 188 break; 189 case 0x332: 190 conf = 0x20; 191 break; 192 case 0x334: 193 conf = 0x40; 194 break; 195 case 0x300: 196 conf = 0x60; 197 break; 198 default: 199 return 0; /* Invalid port */ 200 } 201 202 conf |= 0x83; /* MPU & OPL3 (synth) & game port enable */ 203 conf |= irq_bits[hw_config->irq] << 2; 204 205 opl3sa_write(0x03, conf); 206 207 mpu_initialized = 1; 208 hw_config->name = "OPL3-SA (MPU401)"; 209 210 return probe_uart401(hw_config, THIS_MODULE); 211} 212 213static void __exit unload_opl3sa_wss(struct address_info *hw_config) 214{ 215 int dma2 = hw_config->dma2; 216 217 if (dma2 == -1) 218 dma2 = hw_config->dma; 219 220 release_region(0xf86, 2); 221 release_region(hw_config->io_base, 4); 222 223 ad1848_unload(hw_config->io_base + 4, 224 hw_config->irq, 225 hw_config->dma, 226 dma2, 227 0); 228 sound_unload_audiodev(hw_config->slots[0]); 229} 230 231static inline void __exit unload_opl3sa_mpu(struct address_info *hw_config) 232{ 233 unload_uart401(hw_config); 234} 235 236#ifdef SB_OK 237static inline void __exit unload_opl3sa_sb(struct address_info *hw_config) 238{ 239 sb_dsp_unload(hw_config); 240} 241#endif 242 243static int found_mpu; 244 245static struct address_info cfg; 246static struct address_info cfg_mpu; 247 248static int __initdata io = -1; 249static int __initdata irq = -1; 250static int __initdata dma = -1; 251static int __initdata dma2 = -1; 252static int __initdata mpu_io = -1; 253static int __initdata mpu_irq = -1; 254 255MODULE_PARM(io,"i"); 256MODULE_PARM(irq,"i"); 257MODULE_PARM(dma,"i"); 258MODULE_PARM(dma2,"i"); 259MODULE_PARM(mpu_io,"i"); 260MODULE_PARM(mpu_irq,"i"); 261 262static int __init init_opl3sa(void) 263{ 264 if (io == -1 || irq == -1 || dma == -1) { 265 printk(KERN_ERR "opl3sa: dma, irq and io must be set.\n"); 266 return -EINVAL; 267 } 268 269 cfg.io_base = io; 270 cfg.irq = irq; 271 cfg.dma = dma; 272 cfg.dma2 = dma2; 273 274 cfg_mpu.io_base = mpu_io; 275 cfg_mpu.irq = mpu_irq; 276 277 if (probe_opl3sa_wss(&cfg) == 0) 278 return -ENODEV; 279 280 found_mpu=probe_opl3sa_mpu(&cfg_mpu); 281 282 attach_opl3sa_wss(&cfg); 283 return 0; 284} 285 286static void __exit cleanup_opl3sa(void) 287{ 288 if(found_mpu) 289 unload_opl3sa_mpu(&cfg_mpu); 290 unload_opl3sa_wss(&cfg); 291} 292 293module_init(init_opl3sa); 294module_exit(cleanup_opl3sa); 295 296#ifndef MODULE 297static int __init setup_opl3sa(char *str) 298{ 299 /* io, irq, dma, dma2, mpu_io, mpu_irq */ 300 int ints[7]; 301 302 str = get_options(str, ARRAY_SIZE(ints), ints); 303 304 io = ints[1]; 305 irq = ints[2]; 306 dma = ints[3]; 307 dma2 = ints[4]; 308 mpu_io = ints[5]; 309 mpu_irq = ints[6]; 310 311 return 1; 312} 313 314__setup("opl3sa=", setup_opl3sa); 315#endif 316MODULE_LICENSE("GPL"); 317