1/* 2 * sound/oss/pas2_card.c 3 * 4 * Detection routine for the Pro Audio Spectrum cards. 5 */ 6 7#include <linux/init.h> 8#include <linux/interrupt.h> 9#include <linux/module.h> 10#include <linux/spinlock.h> 11#include "sound_config.h" 12 13#include "pas2.h" 14#include "sb.h" 15 16static unsigned char dma_bits[] = { 17 4, 1, 2, 3, 0, 5, 6, 7 18}; 19 20static unsigned char irq_bits[] = { 21 0, 0, 1, 2, 3, 4, 5, 6, 0, 1, 7, 8, 9, 0, 10, 11 22}; 23 24static unsigned char sb_irq_bits[] = { 25 0x00, 0x00, 0x08, 0x10, 0x00, 0x18, 0x00, 0x20, 26 0x00, 0x08, 0x28, 0x30, 0x38, 0, 0 27}; 28 29static unsigned char sb_dma_bits[] = { 30 0x00, 0x40, 0x80, 0xC0, 0, 0, 0, 0 31}; 32 33/* 34 * The Address Translation code is used to convert I/O register addresses to 35 * be relative to the given base -register 36 */ 37 38int pas_translate_code = 0; 39static int pas_intr_mask; 40static int pas_irq; 41static int pas_sb_base; 42DEFINE_SPINLOCK(pas_lock); 43#ifndef CONFIG_PAS_JOYSTICK 44static bool joystick; 45#else 46static bool joystick = 1; 47#endif 48#ifdef SYMPHONY_PAS 49static bool symphony = 1; 50#else 51static bool symphony; 52#endif 53#ifdef BROKEN_BUS_CLOCK 54static bool broken_bus_clock = 1; 55#else 56static bool broken_bus_clock; 57#endif 58 59static struct address_info cfg; 60static struct address_info cfg2; 61 62char pas_model = 0; 63static char *pas_model_names[] = { 64 "", 65 "Pro AudioSpectrum+", 66 "CDPC", 67 "Pro AudioSpectrum 16", 68 "Pro AudioSpectrum 16D" 69}; 70 71/* 72 * pas_read() and pas_write() are equivalents of inb and outb 73 * These routines perform the I/O address translation required 74 * to support other than the default base address 75 */ 76 77unsigned char pas_read(int ioaddr) 78{ 79 return inb(ioaddr + pas_translate_code); 80} 81 82void pas_write(unsigned char data, int ioaddr) 83{ 84 outb((data), ioaddr + pas_translate_code); 85} 86 87/******************* Begin of the Interrupt Handler ********************/ 88 89static irqreturn_t pasintr(int irq, void *dev_id) 90{ 91 int status; 92 93 status = pas_read(0x0B89); 94 pas_write(status, 0x0B89); /* Clear interrupt */ 95 96 if (status & 0x08) 97 { 98 pas_pcm_interrupt(status, 1); 99 status &= ~0x08; 100 } 101 if (status & 0x10) 102 { 103 pas_midi_interrupt(); 104 status &= ~0x10; 105 } 106 return IRQ_HANDLED; 107} 108 109int pas_set_intr(int mask) 110{ 111 if (!mask) 112 return 0; 113 114 pas_intr_mask |= mask; 115 116 pas_write(pas_intr_mask, 0x0B8B); 117 return 0; 118} 119 120int pas_remove_intr(int mask) 121{ 122 if (!mask) 123 return 0; 124 125 pas_intr_mask &= ~mask; 126 pas_write(pas_intr_mask, 0x0B8B); 127 128 return 0; 129} 130 131/******************* End of the Interrupt handler **********************/ 132 133/******************* Begin of the Initialization Code ******************/ 134 135static int __init config_pas_hw(struct address_info *hw_config) 136{ 137 char ok = 1; 138 unsigned int_ptrs; /* scsi/sound interrupt pointers */ 139 140 pas_irq = hw_config->irq; 141 142 pas_write(0x00, 0x0B8B); 143 pas_write(0x36, 0x138B); 144 pas_write(0x36, 0x1388); 145 pas_write(0, 0x1388); 146 pas_write(0x74, 0x138B); 147 pas_write(0x74, 0x1389); 148 pas_write(0, 0x1389); 149 150 pas_write(0x80 | 0x40 | 0x20 | 1, 0x0B8A); 151 pas_write(0x80 | 0x20 | 0x10 | 0x08 | 0x01, 0xF8A); 152 pas_write(0x01 | 0x02 | 0x04 | 0x10 /* 153 * | 154 * 0x80 155 */ , 0xB88); 156 157 pas_write(0x80 | (joystick ? 0x40 : 0), 0xF388); 158 159 if (pas_irq < 0 || pas_irq > 15) 160 { 161 printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); 162 hw_config->irq=-1; 163 ok = 0; 164 } 165 else 166 { 167 int_ptrs = pas_read(0xF38A); 168 int_ptrs = (int_ptrs & 0xf0) | irq_bits[pas_irq]; 169 pas_write(int_ptrs, 0xF38A); 170 if (!irq_bits[pas_irq]) 171 { 172 printk(KERN_ERR "PAS16: Invalid IRQ %d", pas_irq); 173 hw_config->irq=-1; 174 ok = 0; 175 } 176 else 177 { 178 if (request_irq(pas_irq, pasintr, 0, "PAS16",hw_config) < 0) { 179 printk(KERN_ERR "PAS16: Cannot allocate IRQ %d\n",pas_irq); 180 hw_config->irq=-1; 181 ok = 0; 182 } 183 } 184 } 185 186 if (hw_config->dma < 0 || hw_config->dma > 7) 187 { 188 printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); 189 hw_config->dma=-1; 190 ok = 0; 191 } 192 else 193 { 194 pas_write(dma_bits[hw_config->dma], 0xF389); 195 if (!dma_bits[hw_config->dma]) 196 { 197 printk(KERN_ERR "PAS16: Invalid DMA selection %d", hw_config->dma); 198 hw_config->dma=-1; 199 ok = 0; 200 } 201 else 202 { 203 if (sound_alloc_dma(hw_config->dma, "PAS16")) 204 { 205 printk(KERN_ERR "pas2_card.c: Can't allocate DMA channel\n"); 206 hw_config->dma=-1; 207 ok = 0; 208 } 209 } 210 } 211 212 /* 213 * This fixes the timing problems of the PAS due to the Symphony chipset 214 * as per Media Vision. Only define this if your PAS doesn't work correctly. 215 */ 216 217 if(symphony) 218 { 219 outb((0x05), 0xa8); 220 outb((0x60), 0xa9); 221 } 222 223 if(broken_bus_clock) 224 pas_write(0x01 | 0x10 | 0x20 | 0x04, 0x8388); 225 else 226 /* 227 * pas_write(0x01, 0x8388); 228 */ 229 pas_write(0x01 | 0x10 | 0x20, 0x8388); 230 231 pas_write(0x18, 0x838A); /* ??? */ 232 pas_write(0x20 | 0x01, 0x0B8A); /* Mute off, filter = 17.897 kHz */ 233 pas_write(8, 0xBF8A); 234 235 mix_write(0x80 | 5, 0x078B); 236 mix_write(5, 0x078B); 237 238 { 239 struct address_info *sb_config; 240 241 sb_config = &cfg2; 242 if (sb_config->io_base) 243 { 244 unsigned char irq_dma; 245 246 /* 247 * Turn on Sound Blaster compatibility 248 * bit 1 = SB emulation 249 * bit 0 = MPU401 emulation (CDPC only :-( ) 250 */ 251 252 pas_write(0x02, 0xF788); 253 254 /* 255 * "Emulation address" 256 */ 257 258 pas_write((sb_config->io_base >> 4) & 0x0f, 0xF789); 259 pas_sb_base = sb_config->io_base; 260 261 if (!sb_dma_bits[sb_config->dma]) 262 printk(KERN_ERR "PAS16 Warning: Invalid SB DMA %d\n\n", sb_config->dma); 263 264 if (!sb_irq_bits[sb_config->irq]) 265 printk(KERN_ERR "PAS16 Warning: Invalid SB IRQ %d\n\n", sb_config->irq); 266 267 irq_dma = sb_dma_bits[sb_config->dma] | 268 sb_irq_bits[sb_config->irq]; 269 270 pas_write(irq_dma, 0xFB8A); 271 } 272 else 273 pas_write(0x00, 0xF788); 274 } 275 276 if (!ok) 277 printk(KERN_WARNING "PAS16: Driver not enabled\n"); 278 279 return ok; 280} 281 282static int __init detect_pas_hw(struct address_info *hw_config) 283{ 284 unsigned char board_id, foo; 285 286 /* 287 * WARNING: Setting an option like W:1 or so that disables warm boot reset 288 * of the card will screw up this detect code something fierce. Adding code 289 * to handle this means possibly interfering with other cards on the bus if 290 * you have something on base port 0x388. SO be forewarned. 291 */ 292 293 outb((0xBC), 0x9A01); /* Activate first board */ 294 outb((hw_config->io_base >> 2), 0x9A01); /* Set base address */ 295 pas_translate_code = hw_config->io_base - 0x388; 296 pas_write(1, 0xBF88); /* Select one wait states */ 297 298 board_id = pas_read(0x0B8B); 299 300 if (board_id == 0xff) 301 return 0; 302 303 /* 304 * We probably have a PAS-series board, now check for a PAS16-series board 305 * by trying to change the board revision bits. PAS16-series hardware won't 306 * let you do this - the bits are read-only. 307 */ 308 309 foo = board_id ^ 0xe0; 310 311 pas_write(foo, 0x0B8B); 312 foo = pas_read(0x0B8B); 313 pas_write(board_id, 0x0B8B); 314 315 if (board_id != foo) 316 return 0; 317 318 pas_model = pas_read(0xFF88); 319 320 return pas_model; 321} 322 323static void __init attach_pas_card(struct address_info *hw_config) 324{ 325 pas_irq = hw_config->irq; 326 327 if (detect_pas_hw(hw_config)) 328 { 329 330 if ((pas_model = pas_read(0xFF88))) 331 { 332 char temp[100]; 333 334 if (pas_model < 0 || 335 pas_model >= ARRAY_SIZE(pas_model_names)) { 336 printk(KERN_ERR "pas2 unrecognized model.\n"); 337 return; 338 } 339 sprintf(temp, 340 "%s rev %d", pas_model_names[(int) pas_model], 341 pas_read(0x2789)); 342 conf_printf(temp, hw_config); 343 } 344 if (config_pas_hw(hw_config)) 345 { 346 pas_pcm_init(hw_config); 347 pas_midi_init(); 348 pas_init_mixer(); 349 } 350 } 351} 352 353static inline int __init probe_pas(struct address_info *hw_config) 354{ 355 return detect_pas_hw(hw_config); 356} 357 358static void __exit unload_pas(struct address_info *hw_config) 359{ 360 extern int pas_audiodev; 361 extern int pas2_mididev; 362 363 if (hw_config->dma>0) 364 sound_free_dma(hw_config->dma); 365 if (hw_config->irq>0) 366 free_irq(hw_config->irq, hw_config); 367 368 if(pas_audiodev!=-1) 369 sound_unload_mixerdev(audio_devs[pas_audiodev]->mixer_dev); 370 if(pas2_mididev!=-1) 371 sound_unload_mididev(pas2_mididev); 372 if(pas_audiodev!=-1) 373 sound_unload_audiodev(pas_audiodev); 374} 375 376static int __initdata io = -1; 377static int __initdata irq = -1; 378static int __initdata dma = -1; 379static int __initdata dma16 = -1; /* Set this for modules that need it */ 380 381static int __initdata sb_io = 0; 382static int __initdata sb_irq = -1; 383static int __initdata sb_dma = -1; 384static int __initdata sb_dma16 = -1; 385 386module_param(io, int, 0); 387module_param(irq, int, 0); 388module_param(dma, int, 0); 389module_param(dma16, int, 0); 390 391module_param(sb_io, int, 0); 392module_param(sb_irq, int, 0); 393module_param(sb_dma, int, 0); 394module_param(sb_dma16, int, 0); 395 396module_param(joystick, bool, 0); 397module_param(symphony, bool, 0); 398module_param(broken_bus_clock, bool, 0); 399 400MODULE_LICENSE("GPL"); 401 402static int __init init_pas2(void) 403{ 404 printk(KERN_INFO "Pro Audio Spectrum driver Copyright (C) by Hannu Savolainen 1993-1996\n"); 405 406 cfg.io_base = io; 407 cfg.irq = irq; 408 cfg.dma = dma; 409 cfg.dma2 = dma16; 410 411 cfg2.io_base = sb_io; 412 cfg2.irq = sb_irq; 413 cfg2.dma = sb_dma; 414 cfg2.dma2 = sb_dma16; 415 416 if (cfg.io_base == -1 || cfg.dma == -1 || cfg.irq == -1) { 417 printk(KERN_INFO "I/O, IRQ, DMA and type are mandatory\n"); 418 return -EINVAL; 419 } 420 421 if (!probe_pas(&cfg)) 422 return -ENODEV; 423 attach_pas_card(&cfg); 424 425 return 0; 426} 427 428static void __exit cleanup_pas2(void) 429{ 430 unload_pas(&cfg); 431} 432 433module_init(init_pas2); 434module_exit(cleanup_pas2); 435 436#ifndef MODULE 437static int __init setup_pas2(char *str) 438{ 439 /* io, irq, dma, dma2, sb_io, sb_irq, sb_dma, sb_dma2 */ 440 int ints[9]; 441 442 str = get_options(str, ARRAY_SIZE(ints), ints); 443 444 io = ints[1]; 445 irq = ints[2]; 446 dma = ints[3]; 447 dma16 = ints[4]; 448 449 sb_io = ints[5]; 450 sb_irq = ints[6]; 451 sb_dma = ints[7]; 452 sb_dma16 = ints[8]; 453 454 return 1; 455} 456 457__setup("pas2=", setup_pas2); 458#endif 459