| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * | 
|  | 3 | * AD1816 lowlevel sound driver for Linux 2.6.0 and above | 
|  | 4 | * | 
|  | 5 | * Copyright (C) 1998-2003 by Thorsten Knabe <linux@thorsten-knabe.de> | 
|  | 6 | * | 
|  | 7 | * Based on the CS4232/AD1848 driver Copyright (C) by Hannu Savolainen 1993-1996 | 
|  | 8 | * | 
|  | 9 | * | 
|  | 10 | * version: 1.5 | 
|  | 11 | * status: beta | 
|  | 12 | * date: 2003/07/15 | 
|  | 13 | * | 
|  | 14 | * Changes: | 
|  | 15 | *	Oleg Drokin: Some cleanup of load/unload functions.	1998/11/24 | 
|  | 16 | * | 
|  | 17 | *	Thorsten Knabe: attach and unload rewritten, | 
|  | 18 | *	some argument checks added				1998/11/30 | 
|  | 19 | * | 
|  | 20 | *	Thorsten Knabe: Buggy isa bridge workaround added	1999/01/16 | 
|  | 21 | * | 
|  | 22 | *	David Moews/Thorsten Knabe: Introduced options | 
|  | 23 | *	parameter. Added slightly modified patch from | 
|  | 24 | *	David Moews to disable dsp audio sources by setting | 
|  | 25 | *	bit 0 of options parameter. This seems to be | 
|  | 26 | *	required by some Aztech/Newcom SC-16 cards.		1999/04/18 | 
|  | 27 | * | 
|  | 28 | *	Christoph Hellwig: Adapted to module_init/module_exit.	2000/03/03 | 
|  | 29 | * | 
|  | 30 | *	Christoph Hellwig: Added isapnp support			2000/03/15 | 
|  | 31 | * | 
|  | 32 | *	Arnaldo Carvalho de Melo: get rid of check_region	2001/10/07 | 
|  | 33 | * | 
|  | 34 | *      Thorsten Knabe: Compiling with CONFIG_PNP enabled | 
|  | 35 | *	works again. It is now possible to use more than one | 
|  | 36 | *	AD1816 sound card. Sample rate now may be changed during | 
|  | 37 | *	playback/capture. printk() uses log levels everywhere. | 
|  | 38 | *	SMP fixes. DMA handling fixes. | 
|  | 39 | *	Other minor code cleanup.				2003/07/15 | 
|  | 40 | * | 
|  | 41 | */ | 
|  | 42 |  | 
|  | 43 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 | #include <linux/module.h> | 
|  | 45 | #include <linux/init.h> | 
|  | 46 | #include <linux/interrupt.h> | 
|  | 47 | #include <linux/isapnp.h> | 
|  | 48 | #include <linux/stddef.h> | 
|  | 49 | #include <linux/spinlock.h> | 
|  | 50 | #include "sound_config.h" | 
|  | 51 |  | 
|  | 52 | #define DEBUGNOISE(x) | 
|  | 53 |  | 
|  | 54 | #define CHECK_FOR_POWER { int timeout=100; \ | 
|  | 55 | while (timeout > 0 && (inb(devc->base)&0x80)!= 0x80) {\ | 
|  | 56 | timeout--; \ | 
|  | 57 | } \ | 
|  | 58 | if (timeout==0) {\ | 
|  | 59 | printk(KERN_WARNING "ad1816: Check for power failed in %s line: %d\n",__FILE__,__LINE__); \ | 
|  | 60 | } \ | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | /* structure to hold device specific information */ | 
|  | 64 | typedef struct | 
|  | 65 | { | 
|  | 66 | int            base;          /* set in attach */ | 
|  | 67 | int            irq; | 
|  | 68 | int            dma_playback; | 
|  | 69 | int            dma_capture; | 
|  | 70 |  | 
|  | 71 | int            opened;         /* open */ | 
|  | 72 | int            speed; | 
|  | 73 | int            channels; | 
|  | 74 | int            audio_format; | 
|  | 75 | int            audio_mode; | 
|  | 76 |  | 
|  | 77 | int            recmask;        /* setup */ | 
|  | 78 | unsigned char  format_bits; | 
|  | 79 | int            supported_devices; | 
|  | 80 | int            supported_rec_devices; | 
|  | 81 | unsigned short levels[SOUND_MIXER_NRDEVICES]; | 
|  | 82 | /* misc */ | 
|  | 83 | struct pnp_dev *pnpdev;	 /* configured via pnp */ | 
|  | 84 | int            dev_no;   /* this is the # in audio_devs and NOT | 
|  | 85 | in ad1816_info */ | 
|  | 86 | spinlock_t	lock; | 
|  | 87 | } ad1816_info; | 
|  | 88 |  | 
|  | 89 | static int nr_ad1816_devs; | 
|  | 90 | static int ad1816_clockfreq = 33000; | 
|  | 91 | static int options; | 
|  | 92 |  | 
|  | 93 | /* supported audio formats */ | 
|  | 94 | static int  ad_format_mask = | 
|  | 95 | AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | AFMT_MU_LAW | AFMT_A_LAW; | 
|  | 96 |  | 
|  | 97 | /* array of device info structures */ | 
|  | 98 | static ad1816_info dev_info[MAX_AUDIO_DEV]; | 
|  | 99 |  | 
|  | 100 |  | 
|  | 101 | /* ------------------------------------------------------------------- */ | 
|  | 102 |  | 
|  | 103 | /* functions for easier access to inderect registers */ | 
|  | 104 |  | 
|  | 105 | static int ad_read (ad1816_info * devc, int reg) | 
|  | 106 | { | 
|  | 107 | int result; | 
|  | 108 |  | 
|  | 109 | CHECK_FOR_POWER; | 
|  | 110 | outb ((unsigned char) (reg & 0x3f), devc->base+0); | 
|  | 111 | result = inb(devc->base+2); | 
|  | 112 | result+= inb(devc->base+3)<<8; | 
|  | 113 | return (result); | 
|  | 114 | } | 
|  | 115 |  | 
|  | 116 |  | 
|  | 117 | static void ad_write (ad1816_info * devc, int reg, int data) | 
|  | 118 | { | 
|  | 119 | CHECK_FOR_POWER; | 
|  | 120 | outb ((unsigned char) (reg & 0xff), devc->base+0); | 
|  | 121 | outb ((unsigned char) (data & 0xff),devc->base+2); | 
|  | 122 | outb ((unsigned char) ((data>>8)&0xff),devc->base+3); | 
|  | 123 | } | 
|  | 124 |  | 
|  | 125 | /* ------------------------------------------------------------------- */ | 
|  | 126 |  | 
|  | 127 | /* function interface required by struct audio_driver */ | 
|  | 128 |  | 
|  | 129 | static void ad1816_halt_input (int dev) | 
|  | 130 | { | 
|  | 131 | unsigned long flags; | 
|  | 132 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 133 | unsigned char buffer; | 
|  | 134 |  | 
|  | 135 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_input called\n")); | 
|  | 136 |  | 
|  | 137 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 138 |  | 
|  | 139 | if(!isa_dma_bridge_buggy) { | 
|  | 140 | disable_dma(audio_devs[dev]->dmap_in->dma); | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | buffer=inb(devc->base+9); | 
|  | 144 | if (buffer & 0x01) { | 
|  | 145 | /* disable capture */ | 
|  | 146 | outb(buffer & ~0x01,devc->base+9); | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | if(!isa_dma_bridge_buggy) { | 
|  | 150 | enable_dma(audio_devs[dev]->dmap_in->dma); | 
|  | 151 | } | 
|  | 152 |  | 
|  | 153 | /* Clear interrupt status */ | 
|  | 154 | outb (~0x40, devc->base+1); | 
|  | 155 |  | 
|  | 156 | devc->audio_mode &= ~PCM_ENABLE_INPUT; | 
|  | 157 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 158 | } | 
|  | 159 |  | 
|  | 160 | static void ad1816_halt_output (int dev) | 
|  | 161 | { | 
|  | 162 | unsigned long  flags; | 
|  | 163 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 164 |  | 
|  | 165 | unsigned char buffer; | 
|  | 166 |  | 
|  | 167 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: halt_output called!\n")); | 
|  | 168 |  | 
|  | 169 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 170 | /* Mute pcm output */ | 
|  | 171 | ad_write(devc, 4, ad_read(devc,4)|0x8080); | 
|  | 172 |  | 
|  | 173 | if(!isa_dma_bridge_buggy) { | 
|  | 174 | disable_dma(audio_devs[dev]->dmap_out->dma); | 
|  | 175 | } | 
|  | 176 |  | 
|  | 177 | buffer=inb(devc->base+8); | 
|  | 178 | if (buffer & 0x01) { | 
|  | 179 | /* disable capture */ | 
|  | 180 | outb(buffer & ~0x01,devc->base+8); | 
|  | 181 | } | 
|  | 182 |  | 
|  | 183 | if(!isa_dma_bridge_buggy) { | 
|  | 184 | enable_dma(audio_devs[dev]->dmap_out->dma); | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 | /* Clear interrupt status */ | 
|  | 188 | outb ((unsigned char)~0x80, devc->base+1); | 
|  | 189 |  | 
|  | 190 | devc->audio_mode &= ~PCM_ENABLE_OUTPUT; | 
|  | 191 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | static void ad1816_output_block (int dev, unsigned long buf, | 
|  | 195 | int count, int intrflag) | 
|  | 196 | { | 
|  | 197 | unsigned long flags; | 
|  | 198 | unsigned long cnt; | 
|  | 199 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 200 |  | 
|  | 201 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: output_block called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); | 
|  | 202 |  | 
|  | 203 | cnt = count/4 - 1; | 
|  | 204 |  | 
|  | 205 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 206 |  | 
|  | 207 | /* set transfer count */ | 
|  | 208 | ad_write (devc, 8, cnt & 0xffff); | 
|  | 209 |  | 
|  | 210 | devc->audio_mode |= PCM_ENABLE_OUTPUT; | 
|  | 211 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 212 | } | 
|  | 213 |  | 
|  | 214 |  | 
|  | 215 | static void ad1816_start_input (int dev, unsigned long buf, int count, | 
|  | 216 | int intrflag) | 
|  | 217 | { | 
|  | 218 | unsigned long flags; | 
|  | 219 | unsigned long  cnt; | 
|  | 220 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 221 |  | 
|  | 222 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: start_input called buf=%ld count=%d flags=%d\n",buf,count,intrflag)); | 
|  | 223 |  | 
|  | 224 | cnt = count/4 - 1; | 
|  | 225 |  | 
|  | 226 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 227 |  | 
|  | 228 | /* set transfer count */ | 
|  | 229 | ad_write (devc, 10, cnt & 0xffff); | 
|  | 230 | devc->audio_mode |= PCM_ENABLE_INPUT; | 
|  | 231 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | static int ad1816_prepare_for_input (int dev, int bsize, int bcount) | 
|  | 235 | { | 
|  | 236 | unsigned long flags; | 
|  | 237 | unsigned int freq; | 
|  | 238 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 239 | unsigned char fmt_bits; | 
|  | 240 |  | 
|  | 241 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_input called: bsize=%d bcount=%d\n",bsize,bcount)); | 
|  | 242 |  | 
|  | 243 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 244 | fmt_bits= (devc->format_bits&0x7)<<3; | 
|  | 245 |  | 
|  | 246 | /* set mono/stereo mode */ | 
|  | 247 | if (devc->channels > 1) { | 
|  | 248 | fmt_bits |=0x4; | 
|  | 249 | } | 
|  | 250 | /* set Mono/Stereo in playback/capture register */ | 
|  | 251 | outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); | 
|  | 252 | outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); | 
|  | 253 |  | 
|  | 254 | freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; | 
|  | 255 |  | 
|  | 256 | /* write playback/capture speeds */ | 
|  | 257 | ad_write (devc, 2, freq & 0xffff); | 
|  | 258 | ad_write (devc, 3, freq & 0xffff); | 
|  | 259 |  | 
|  | 260 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 261 |  | 
|  | 262 | ad1816_halt_input(dev); | 
|  | 263 | return 0; | 
|  | 264 | } | 
|  | 265 |  | 
|  | 266 | static int ad1816_prepare_for_output (int dev, int bsize, int bcount) | 
|  | 267 | { | 
|  | 268 | unsigned long flags; | 
|  | 269 | unsigned int freq; | 
|  | 270 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 271 | unsigned char fmt_bits; | 
|  | 272 |  | 
|  | 273 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: prepare_for_output called: bsize=%d bcount=%d\n",bsize,bcount)); | 
|  | 274 |  | 
|  | 275 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 276 |  | 
|  | 277 | fmt_bits= (devc->format_bits&0x7)<<3; | 
|  | 278 | /* set mono/stereo mode */ | 
|  | 279 | if (devc->channels > 1) { | 
|  | 280 | fmt_bits |=0x4; | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | /* write format bits to playback/capture registers */ | 
|  | 284 | outb( (inb(devc->base+8) & ~0x3C)|fmt_bits, devc->base+8); | 
|  | 285 | outb( (inb(devc->base+9) & ~0x3C)|fmt_bits, devc->base+9); | 
|  | 286 |  | 
|  | 287 | freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; | 
|  | 288 |  | 
|  | 289 | /* write playback/capture speeds */ | 
|  | 290 | ad_write (devc, 2, freq & 0xffff); | 
|  | 291 | ad_write (devc, 3, freq & 0xffff); | 
|  | 292 |  | 
|  | 293 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 294 |  | 
|  | 295 | ad1816_halt_output(dev); | 
|  | 296 | return 0; | 
|  | 297 |  | 
|  | 298 | } | 
|  | 299 |  | 
|  | 300 | static void ad1816_trigger (int dev, int state) | 
|  | 301 | { | 
|  | 302 | unsigned long flags; | 
|  | 303 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 304 |  | 
|  | 305 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: trigger called! (devc=%d,devc->base=%d\n", devc, devc->base)); | 
|  | 306 |  | 
|  | 307 | /* mode may have changed */ | 
|  | 308 |  | 
|  | 309 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 310 |  | 
|  | 311 | /* mask out modes not specified on open call */ | 
|  | 312 | state &= devc->audio_mode; | 
|  | 313 |  | 
|  | 314 | /* setup soundchip to new io-mode */ | 
|  | 315 | if (state & PCM_ENABLE_INPUT) { | 
|  | 316 | /* enable capture */ | 
|  | 317 | outb(inb(devc->base+9)|0x01, devc->base+9); | 
|  | 318 | } else { | 
|  | 319 | /* disable capture */ | 
|  | 320 | outb(inb(devc->base+9)&~0x01, devc->base+9); | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | if (state & PCM_ENABLE_OUTPUT) { | 
|  | 324 | /* enable playback */ | 
|  | 325 | outb(inb(devc->base+8)|0x01, devc->base+8); | 
|  | 326 | /* unmute pcm output */ | 
|  | 327 | ad_write(devc, 4, ad_read(devc,4)&~0x8080); | 
|  | 328 | } else { | 
|  | 329 | /* mute pcm output */ | 
|  | 330 | ad_write(devc, 4, ad_read(devc,4)|0x8080); | 
|  | 331 | /* disable capture */ | 
|  | 332 | outb(inb(devc->base+8)&~0x01, devc->base+8); | 
|  | 333 | } | 
|  | 334 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 335 | } | 
|  | 336 |  | 
|  | 337 |  | 
|  | 338 | /* halt input & output */ | 
|  | 339 | static void ad1816_halt (int dev) | 
|  | 340 | { | 
|  | 341 | ad1816_halt_input(dev); | 
|  | 342 | ad1816_halt_output(dev); | 
|  | 343 | } | 
|  | 344 |  | 
|  | 345 | static void ad1816_reset (int dev) | 
|  | 346 | { | 
|  | 347 | ad1816_halt (dev); | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | /* set playback speed */ | 
|  | 351 | static int ad1816_set_speed (int dev, int arg) | 
|  | 352 | { | 
|  | 353 | unsigned long flags; | 
|  | 354 | unsigned int freq; | 
|  | 355 | int ret; | 
|  | 356 |  | 
|  | 357 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 358 |  | 
|  | 359 | spin_lock_irqsave(&devc->lock, flags); | 
|  | 360 | if (arg == 0) { | 
|  | 361 | ret = devc->speed; | 
|  | 362 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 363 | return ret; | 
|  | 364 | } | 
|  | 365 | /* range checking */ | 
|  | 366 | if (arg < 4000) { | 
|  | 367 | arg = 4000; | 
|  | 368 | } | 
|  | 369 | if (arg > 55000) { | 
|  | 370 | arg = 55000; | 
|  | 371 | } | 
|  | 372 | devc->speed = arg; | 
|  | 373 |  | 
|  | 374 | /* change speed during playback */ | 
|  | 375 | freq=((unsigned int)devc->speed*33000)/ad1816_clockfreq; | 
|  | 376 | /* write playback/capture speeds */ | 
|  | 377 | ad_write (devc, 2, freq & 0xffff); | 
|  | 378 | ad_write (devc, 3, freq & 0xffff); | 
|  | 379 |  | 
|  | 380 | ret = devc->speed; | 
|  | 381 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 382 | return ret; | 
|  | 383 |  | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | static unsigned int ad1816_set_bits (int dev, unsigned int arg) | 
|  | 387 | { | 
|  | 388 | unsigned long flags; | 
|  | 389 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 390 |  | 
|  | 391 | static struct format_tbl { | 
|  | 392 | int             format; | 
|  | 393 | unsigned char   bits; | 
|  | 394 | } format2bits[] = { | 
|  | 395 | { 0, 0 }, | 
|  | 396 | { AFMT_MU_LAW, 1 }, | 
|  | 397 | { AFMT_A_LAW, 3 }, | 
|  | 398 | { AFMT_IMA_ADPCM, 0 }, | 
|  | 399 | { AFMT_U8, 0 }, | 
|  | 400 | { AFMT_S16_LE, 2 }, | 
|  | 401 | { AFMT_S16_BE, 6 }, | 
|  | 402 | { AFMT_S8, 0 }, | 
|  | 403 | { AFMT_U16_LE, 0 }, | 
|  | 404 | { AFMT_U16_BE, 0 } | 
|  | 405 | }; | 
|  | 406 |  | 
|  | 407 | int  i, n = sizeof (format2bits) / sizeof (struct format_tbl); | 
|  | 408 |  | 
|  | 409 | spin_lock_irqsave(&devc->lock, flags); | 
|  | 410 | /* return current format */ | 
|  | 411 | if (arg == 0) { | 
|  | 412 | arg = devc->audio_format; | 
|  | 413 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 414 | return arg; | 
|  | 415 | } | 
|  | 416 | devc->audio_format = arg; | 
|  | 417 |  | 
|  | 418 | /* search matching format bits */ | 
|  | 419 | for (i = 0; i < n; i++) | 
|  | 420 | if (format2bits[i].format == arg) { | 
|  | 421 | devc->format_bits = format2bits[i].bits; | 
|  | 422 | devc->audio_format = arg; | 
|  | 423 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 424 | return arg; | 
|  | 425 | } | 
|  | 426 |  | 
|  | 427 | /* Still hanging here. Something must be terribly wrong */ | 
|  | 428 | devc->format_bits = 0; | 
|  | 429 | devc->audio_format = AFMT_U8; | 
|  | 430 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 431 | return(AFMT_U8); | 
|  | 432 | } | 
|  | 433 |  | 
|  | 434 | static short ad1816_set_channels (int dev, short arg) | 
|  | 435 | { | 
|  | 436 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 437 |  | 
|  | 438 | if (arg != 1 && arg != 2) | 
|  | 439 | return devc->channels; | 
|  | 440 |  | 
|  | 441 | devc->channels = arg; | 
|  | 442 | return arg; | 
|  | 443 | } | 
|  | 444 |  | 
|  | 445 | /* open device */ | 
|  | 446 | static int ad1816_open (int dev, int mode) | 
|  | 447 | { | 
|  | 448 | ad1816_info    *devc = NULL; | 
|  | 449 | unsigned long   flags; | 
|  | 450 |  | 
|  | 451 | /* is device number valid ? */ | 
|  | 452 | if (dev < 0 || dev >= num_audiodevs) | 
|  | 453 | return -(ENXIO); | 
|  | 454 |  | 
|  | 455 | /* get device info of this dev */ | 
|  | 456 | devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 457 |  | 
|  | 458 | /* make check if device already open atomic */ | 
|  | 459 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 460 |  | 
|  | 461 | if (devc->opened) { | 
|  | 462 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 463 | return -(EBUSY); | 
|  | 464 | } | 
|  | 465 |  | 
|  | 466 | /* mark device as open */ | 
|  | 467 | devc->opened = 1; | 
|  | 468 |  | 
|  | 469 | devc->audio_mode = 0; | 
|  | 470 | devc->speed = 8000; | 
|  | 471 | devc->audio_format=AFMT_U8; | 
|  | 472 | devc->channels=1; | 
|  | 473 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 474 | ad1816_reset(devc->dev_no); /* halt all pending output */ | 
|  | 475 | return 0; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | static void ad1816_close (int dev) /* close device */ | 
|  | 479 | { | 
|  | 480 | unsigned long flags; | 
|  | 481 | ad1816_info    *devc = (ad1816_info *) audio_devs[dev]->devc; | 
|  | 482 |  | 
|  | 483 | /* halt all pending output */ | 
|  | 484 | ad1816_reset(devc->dev_no); | 
|  | 485 |  | 
|  | 486 | spin_lock_irqsave(&devc->lock,flags); | 
|  | 487 | devc->opened = 0; | 
|  | 488 | devc->audio_mode = 0; | 
|  | 489 | devc->speed = 8000; | 
|  | 490 | devc->audio_format=AFMT_U8; | 
|  | 491 | devc->format_bits = 0; | 
|  | 492 | spin_unlock_irqrestore(&devc->lock,flags); | 
|  | 493 | } | 
|  | 494 |  | 
|  | 495 |  | 
|  | 496 | /* ------------------------------------------------------------------- */ | 
|  | 497 |  | 
|  | 498 | /* Audio driver structure */ | 
|  | 499 |  | 
|  | 500 | static struct audio_driver ad1816_audio_driver = | 
|  | 501 | { | 
|  | 502 | .owner			= THIS_MODULE, | 
|  | 503 | .open			= ad1816_open, | 
|  | 504 | .close			= ad1816_close, | 
|  | 505 | .output_block		= ad1816_output_block, | 
|  | 506 | .start_input		= ad1816_start_input, | 
|  | 507 | .prepare_for_input	= ad1816_prepare_for_input, | 
|  | 508 | .prepare_for_output	= ad1816_prepare_for_output, | 
|  | 509 | .halt_io		= ad1816_halt, | 
|  | 510 | .halt_input		= ad1816_halt_input, | 
|  | 511 | .halt_output		= ad1816_halt_output, | 
|  | 512 | .trigger		= ad1816_trigger, | 
|  | 513 | .set_speed		= ad1816_set_speed, | 
|  | 514 | .set_bits		= ad1816_set_bits, | 
|  | 515 | .set_channels		= ad1816_set_channels, | 
|  | 516 | }; | 
|  | 517 |  | 
|  | 518 |  | 
|  | 519 | /* ------------------------------------------------------------------- */ | 
|  | 520 |  | 
|  | 521 | /* Interrupt handler */ | 
|  | 522 |  | 
|  | 523 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 524 | static irqreturn_t ad1816_interrupt (int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 525 | { | 
|  | 526 | unsigned char	status; | 
|  | 527 | ad1816_info	*devc = (ad1816_info *)dev_id; | 
|  | 528 |  | 
|  | 529 | if (irq < 0 || irq > 15) { | 
|  | 530 | printk(KERN_WARNING "ad1816: Got bogus interrupt %d\n", irq); | 
|  | 531 | return IRQ_NONE; | 
|  | 532 | } | 
|  | 533 |  | 
|  | 534 | spin_lock(&devc->lock); | 
|  | 535 |  | 
|  | 536 | /* read interrupt register */ | 
|  | 537 | status = inb (devc->base+1); | 
|  | 538 | /* Clear all interrupt  */ | 
|  | 539 | outb (~status, devc->base+1); | 
|  | 540 |  | 
|  | 541 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: Got interrupt subclass %d\n",status)); | 
|  | 542 |  | 
|  | 543 | if (status == 0) { | 
|  | 544 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: interrupt: Got interrupt, but no source.\n")); | 
|  | 545 | spin_unlock(&devc->lock); | 
|  | 546 | return IRQ_NONE; | 
|  | 547 | } | 
|  | 548 |  | 
|  | 549 | if (devc->opened && (devc->audio_mode & PCM_ENABLE_INPUT) && (status&64)) | 
|  | 550 | DMAbuf_inputintr (devc->dev_no); | 
|  | 551 |  | 
|  | 552 | if (devc->opened && (devc->audio_mode & PCM_ENABLE_OUTPUT) && (status & 128)) | 
|  | 553 | DMAbuf_outputintr (devc->dev_no, 1); | 
|  | 554 |  | 
|  | 555 | spin_unlock(&devc->lock); | 
|  | 556 | return IRQ_HANDLED; | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | /* ------------------------------------------------------------------- */ | 
|  | 560 |  | 
|  | 561 | /* Mixer stuff */ | 
|  | 562 |  | 
|  | 563 | struct mixer_def { | 
|  | 564 | unsigned int regno: 7; | 
|  | 565 | unsigned int polarity:1;	/* 0=normal, 1=reversed */ | 
|  | 566 | unsigned int bitpos:4; | 
|  | 567 | unsigned int nbits:4; | 
|  | 568 | }; | 
|  | 569 |  | 
|  | 570 | static char mix_cvt[101] = { | 
|  | 571 | 0, 0, 3, 7,10,13,16,19,21,23,26,28,30,32,34,35,37,39,40,42, | 
|  | 572 | 43,45,46,47,49,50,51,52,53,55,56,57,58,59,60,61,62,63,64,65, | 
|  | 573 | 65,66,67,68,69,70,70,71,72,73,73,74,75,75,76,77,77,78,79,79, | 
|  | 574 | 80,81,81,82,82,83,84,84,85,85,86,86,87,87,88,88,89,89,90,90, | 
|  | 575 | 91,91,92,92,93,93,94,94,95,95,96,96,96,97,97,98,98,98,99,99, | 
|  | 576 | 100 | 
|  | 577 | }; | 
|  | 578 |  | 
|  | 579 | typedef struct mixer_def mixer_ent; | 
|  | 580 |  | 
|  | 581 | /* | 
|  | 582 | * Most of the mixer entries work in backwards. Setting the polarity field | 
|  | 583 | * makes them to work correctly. | 
|  | 584 | * | 
|  | 585 | * The channel numbering used by individual soundcards is not fixed. Some | 
|  | 586 | * cards have assigned different meanings for the AUX1, AUX2 and LINE inputs. | 
|  | 587 | * The current version doesn't try to compensate this. | 
|  | 588 | */ | 
|  | 589 |  | 
|  | 590 | #define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)	\ | 
|  | 591 | {{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}} | 
|  | 592 |  | 
|  | 593 |  | 
| Adrian Bunk | 155542c | 2005-06-25 14:58:53 -0700 | [diff] [blame] | 594 | static mixer_ent mix_devices[SOUND_MIXER_NRDEVICES][2] = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 595 | MIX_ENT(SOUND_MIXER_VOLUME,	14, 1, 8, 5,	14, 1, 0, 5), | 
|  | 596 | MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 597 | MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 598 | MIX_ENT(SOUND_MIXER_SYNTH,	 5, 1, 8, 6,	 5, 1, 0, 6), | 
|  | 599 | MIX_ENT(SOUND_MIXER_PCM,	 4, 1, 8, 6,	 4, 1, 0, 6), | 
|  | 600 | MIX_ENT(SOUND_MIXER_SPEAKER,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 601 | MIX_ENT(SOUND_MIXER_LINE,	18, 1, 8, 5,	18, 1, 0, 5), | 
|  | 602 | MIX_ENT(SOUND_MIXER_MIC,	19, 1, 8, 5,	19, 1, 0, 5), | 
|  | 603 | MIX_ENT(SOUND_MIXER_CD,	 	15, 1, 8, 5,	15, 1, 0, 5), | 
|  | 604 | MIX_ENT(SOUND_MIXER_IMIX,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 605 | MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 606 | MIX_ENT(SOUND_MIXER_RECLEV,	20, 0, 8, 4,	20, 0, 0, 4), | 
|  | 607 | MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 608 | MIX_ENT(SOUND_MIXER_OGAIN,	 0, 0, 0, 0,	 0, 0, 0, 0), | 
|  | 609 | MIX_ENT(SOUND_MIXER_LINE1, 	17, 1, 8, 5,	17, 1, 0, 5), | 
|  | 610 | MIX_ENT(SOUND_MIXER_LINE2,	16, 1, 8, 5,	16, 1, 0, 5), | 
|  | 611 | MIX_ENT(SOUND_MIXER_LINE3,      39, 0, 9, 4,    39, 1, 0, 5) | 
|  | 612 | }; | 
|  | 613 |  | 
|  | 614 |  | 
|  | 615 | static unsigned short default_mixer_levels[SOUND_MIXER_NRDEVICES] = | 
|  | 616 | { | 
|  | 617 | 0x4343,		/* Master Volume */ | 
|  | 618 | 0x3232,		/* Bass */ | 
|  | 619 | 0x3232,		/* Treble */ | 
|  | 620 | 0x0000,		/* FM */ | 
|  | 621 | 0x4343,		/* PCM */ | 
|  | 622 | 0x0000,		/* PC Speaker */ | 
|  | 623 | 0x0000,		/* Ext Line */ | 
|  | 624 | 0x0000,		/* Mic */ | 
|  | 625 | 0x0000,		/* CD */ | 
|  | 626 | 0x0000,		/* Recording monitor */ | 
|  | 627 | 0x0000,		/* SB PCM */ | 
|  | 628 | 0x0000,		/* Recording level */ | 
|  | 629 | 0x0000,		/* Input gain */ | 
|  | 630 | 0x0000,		/* Output gain */ | 
|  | 631 | 0x0000,		/* Line1 */ | 
|  | 632 | 0x0000,		/* Line2 */ | 
|  | 633 | 0x0000		/* Line3 (usually line in)*/ | 
|  | 634 | }; | 
|  | 635 |  | 
|  | 636 | #define LEFT_CHN	0 | 
|  | 637 | #define RIGHT_CHN	1 | 
|  | 638 |  | 
|  | 639 |  | 
|  | 640 |  | 
|  | 641 | static int | 
|  | 642 | ad1816_set_recmask (ad1816_info * devc, int mask) | 
|  | 643 | { | 
|  | 644 | unsigned long 	flags; | 
|  | 645 | unsigned char   recdev; | 
|  | 646 | int             i, n; | 
|  | 647 |  | 
|  | 648 | spin_lock_irqsave(&devc->lock, flags); | 
|  | 649 | mask &= devc->supported_rec_devices; | 
|  | 650 |  | 
|  | 651 | n = 0; | 
|  | 652 | /* Count selected device bits */ | 
|  | 653 | for (i = 0; i < 32; i++) | 
|  | 654 | if (mask & (1 << i)) | 
|  | 655 | n++; | 
|  | 656 |  | 
|  | 657 | if (n == 0) | 
|  | 658 | mask = SOUND_MASK_MIC; | 
|  | 659 | else if (n != 1) { /* Too many devices selected */ | 
|  | 660 | /* Filter out active settings */ | 
|  | 661 | mask &= ~devc->recmask; | 
|  | 662 |  | 
|  | 663 | n = 0; | 
|  | 664 | /* Count selected device bits */ | 
|  | 665 | for (i = 0; i < 32; i++) | 
|  | 666 | if (mask & (1 << i)) | 
|  | 667 | n++; | 
|  | 668 |  | 
|  | 669 | if (n != 1) | 
|  | 670 | mask = SOUND_MASK_MIC; | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | switch (mask) { | 
|  | 674 | case SOUND_MASK_MIC: | 
|  | 675 | recdev = 5; | 
|  | 676 | break; | 
|  | 677 |  | 
|  | 678 | case SOUND_MASK_LINE: | 
|  | 679 | recdev = 0; | 
|  | 680 | break; | 
|  | 681 |  | 
|  | 682 | case SOUND_MASK_CD: | 
|  | 683 | recdev = 2; | 
|  | 684 | break; | 
|  | 685 |  | 
|  | 686 | case SOUND_MASK_LINE1: | 
|  | 687 | recdev = 4; | 
|  | 688 | break; | 
|  | 689 |  | 
|  | 690 | case SOUND_MASK_LINE2: | 
|  | 691 | recdev = 3; | 
|  | 692 | break; | 
|  | 693 |  | 
|  | 694 | case SOUND_MASK_VOLUME: | 
|  | 695 | recdev = 1; | 
|  | 696 | break; | 
|  | 697 |  | 
|  | 698 | default: | 
|  | 699 | mask = SOUND_MASK_MIC; | 
|  | 700 | recdev = 5; | 
|  | 701 | } | 
|  | 702 |  | 
|  | 703 | recdev <<= 4; | 
|  | 704 | ad_write (devc, 20, | 
|  | 705 | (ad_read (devc, 20) & 0x8f8f) | recdev | (recdev<<8)); | 
|  | 706 |  | 
|  | 707 | devc->recmask = mask; | 
|  | 708 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 709 | return mask; | 
|  | 710 | } | 
|  | 711 |  | 
|  | 712 | static void | 
|  | 713 | change_bits (int *regval, int dev, int chn, int newval) | 
|  | 714 | { | 
|  | 715 | unsigned char   mask; | 
|  | 716 | int             shift; | 
|  | 717 |  | 
|  | 718 | /* Reverse polarity*/ | 
|  | 719 |  | 
|  | 720 | if (mix_devices[dev][chn].polarity == 1) | 
|  | 721 | newval = 100 - newval; | 
|  | 722 |  | 
|  | 723 | mask = (1 << mix_devices[dev][chn].nbits) - 1; | 
|  | 724 | shift = mix_devices[dev][chn].bitpos; | 
|  | 725 | /* Scale it */ | 
|  | 726 | newval = (int) ((newval * mask) + 50) / 100; | 
|  | 727 | /* Clear bits */ | 
|  | 728 | *regval &= ~(mask << shift); | 
|  | 729 | /* Set new value */ | 
|  | 730 | *regval |= (newval & mask) << shift; | 
|  | 731 | } | 
|  | 732 |  | 
|  | 733 | static int | 
|  | 734 | ad1816_mixer_get (ad1816_info * devc, int dev) | 
|  | 735 | { | 
|  | 736 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_get called!\n")); | 
|  | 737 |  | 
|  | 738 | /* range check + supported mixer check */ | 
|  | 739 | if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) | 
|  | 740 | return (-(EINVAL)); | 
|  | 741 | if (!((1 << dev) & devc->supported_devices)) | 
|  | 742 | return -(EINVAL); | 
|  | 743 |  | 
|  | 744 | return devc->levels[dev]; | 
|  | 745 | } | 
|  | 746 |  | 
|  | 747 | static int | 
|  | 748 | ad1816_mixer_set (ad1816_info * devc, int dev, int value) | 
|  | 749 | { | 
|  | 750 | int   left = value & 0x000000ff; | 
|  | 751 | int   right = (value & 0x0000ff00) >> 8; | 
|  | 752 | int   retvol; | 
|  | 753 |  | 
|  | 754 | int   regoffs; | 
|  | 755 | int   val; | 
|  | 756 | int   valmute; | 
|  | 757 | unsigned long flags; | 
|  | 758 |  | 
|  | 759 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_set called!\n")); | 
|  | 760 |  | 
|  | 761 | if (dev < 0 || dev >= SOUND_MIXER_NRDEVICES ) | 
|  | 762 | return -(EINVAL); | 
|  | 763 |  | 
|  | 764 | if (left > 100) | 
|  | 765 | left = 100; | 
|  | 766 | if (left < 0) | 
|  | 767 | left = 0; | 
|  | 768 | if (right > 100) | 
|  | 769 | right = 100; | 
|  | 770 | if (right < 0) | 
|  | 771 | right = 0; | 
|  | 772 |  | 
|  | 773 | /* Mono control */ | 
|  | 774 | if (mix_devices[dev][RIGHT_CHN].nbits == 0) | 
|  | 775 | right = left; | 
|  | 776 | retvol = left | (right << 8); | 
|  | 777 |  | 
|  | 778 | /* Scale it */ | 
|  | 779 |  | 
|  | 780 | left = mix_cvt[left]; | 
|  | 781 | right = mix_cvt[right]; | 
|  | 782 |  | 
|  | 783 | /* reject all mixers that are not supported */ | 
|  | 784 | if (!(devc->supported_devices & (1 << dev))) | 
|  | 785 | return -(EINVAL); | 
|  | 786 |  | 
|  | 787 | /* sanity check */ | 
|  | 788 | if (mix_devices[dev][LEFT_CHN].nbits == 0) | 
|  | 789 | return -(EINVAL); | 
|  | 790 | spin_lock_irqsave(&devc->lock, flags); | 
|  | 791 |  | 
|  | 792 | /* keep precise volume internal */ | 
|  | 793 | devc->levels[dev] = retvol; | 
|  | 794 |  | 
|  | 795 | /* Set the left channel */ | 
|  | 796 | regoffs = mix_devices[dev][LEFT_CHN].regno; | 
|  | 797 | val = ad_read (devc, regoffs); | 
|  | 798 | change_bits (&val, dev, LEFT_CHN, left); | 
|  | 799 |  | 
|  | 800 | valmute=val; | 
|  | 801 |  | 
|  | 802 | /* Mute bit masking on some registers */ | 
|  | 803 | if ( regoffs==5 || regoffs==14 || regoffs==15 || | 
|  | 804 | regoffs==16 || regoffs==17 || regoffs==18 || | 
|  | 805 | regoffs==19 || regoffs==39) { | 
|  | 806 | if (left==0) | 
|  | 807 | valmute |= 0x8000; | 
|  | 808 | else | 
|  | 809 | valmute &= ~0x8000; | 
|  | 810 | } | 
|  | 811 | ad_write (devc, regoffs, valmute); /* mute */ | 
|  | 812 |  | 
|  | 813 | /* | 
|  | 814 | * Set the right channel | 
|  | 815 | */ | 
|  | 816 |  | 
|  | 817 | /* Was just a mono channel */ | 
|  | 818 | if (mix_devices[dev][RIGHT_CHN].nbits == 0) { | 
|  | 819 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 820 | return retvol; | 
|  | 821 | } | 
|  | 822 |  | 
|  | 823 | regoffs = mix_devices[dev][RIGHT_CHN].regno; | 
|  | 824 | val = ad_read (devc, regoffs); | 
|  | 825 | change_bits (&val, dev, RIGHT_CHN, right); | 
|  | 826 |  | 
|  | 827 | valmute=val; | 
|  | 828 | if ( regoffs==5 || regoffs==14 || regoffs==15 || | 
|  | 829 | regoffs==16 || regoffs==17 || regoffs==18 || | 
|  | 830 | regoffs==19 || regoffs==39) { | 
|  | 831 | if (right==0) | 
|  | 832 | valmute |= 0x80; | 
|  | 833 | else | 
|  | 834 | valmute &= ~0x80; | 
|  | 835 | } | 
|  | 836 | ad_write (devc, regoffs, valmute); /* mute */ | 
|  | 837 | spin_unlock_irqrestore(&devc->lock, flags); | 
|  | 838 | return retvol; | 
|  | 839 | } | 
|  | 840 |  | 
|  | 841 | #define MIXER_DEVICES ( SOUND_MASK_VOLUME | \ | 
|  | 842 | SOUND_MASK_SYNTH | \ | 
|  | 843 | SOUND_MASK_PCM | \ | 
|  | 844 | SOUND_MASK_LINE | \ | 
|  | 845 | SOUND_MASK_LINE1 | \ | 
|  | 846 | SOUND_MASK_LINE2 | \ | 
|  | 847 | SOUND_MASK_LINE3 | \ | 
|  | 848 | SOUND_MASK_MIC | \ | 
|  | 849 | SOUND_MASK_CD | \ | 
|  | 850 | SOUND_MASK_RECLEV  \ | 
|  | 851 | ) | 
|  | 852 | #define REC_DEVICES ( SOUND_MASK_LINE2 |\ | 
|  | 853 | SOUND_MASK_LINE |\ | 
|  | 854 | SOUND_MASK_LINE1 |\ | 
|  | 855 | SOUND_MASK_MIC |\ | 
|  | 856 | SOUND_MASK_CD |\ | 
|  | 857 | SOUND_MASK_VOLUME \ | 
|  | 858 | ) | 
|  | 859 |  | 
|  | 860 | static void | 
|  | 861 | ad1816_mixer_reset (ad1816_info * devc) | 
|  | 862 | { | 
|  | 863 | int  i; | 
|  | 864 |  | 
|  | 865 | devc->supported_devices = MIXER_DEVICES; | 
|  | 866 |  | 
|  | 867 | devc->supported_rec_devices = REC_DEVICES; | 
|  | 868 |  | 
|  | 869 | for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) | 
|  | 870 | if (devc->supported_devices & (1 << i)) | 
|  | 871 | ad1816_mixer_set (devc, i, default_mixer_levels[i]); | 
|  | 872 | ad1816_set_recmask (devc, SOUND_MASK_MIC); | 
|  | 873 | } | 
|  | 874 |  | 
|  | 875 | static int | 
|  | 876 | ad1816_mixer_ioctl (int dev, unsigned int cmd, void __user * arg) | 
|  | 877 | { | 
|  | 878 | ad1816_info    *devc = mixer_devs[dev]->devc; | 
|  | 879 | int val; | 
|  | 880 | int __user *p = arg; | 
|  | 881 |  | 
|  | 882 | DEBUGNOISE(printk(KERN_DEBUG "ad1816: mixer_ioctl called!\n")); | 
|  | 883 |  | 
|  | 884 | /* Mixer ioctl */ | 
|  | 885 | if (((cmd >> 8) & 0xff) == 'M') { | 
|  | 886 |  | 
|  | 887 | /* set ioctl */ | 
|  | 888 | if (_SIOC_DIR (cmd) & _SIOC_WRITE) { | 
|  | 889 | switch (cmd & 0xff){ | 
|  | 890 | case SOUND_MIXER_RECSRC: | 
|  | 891 |  | 
|  | 892 | if (get_user(val, p)) | 
|  | 893 | return -EFAULT; | 
|  | 894 | val=ad1816_set_recmask (devc, val); | 
|  | 895 | return put_user(val, p); | 
|  | 896 | break; | 
|  | 897 |  | 
|  | 898 | default: | 
|  | 899 | if (get_user(val, p)) | 
|  | 900 | return -EFAULT; | 
|  | 901 | if ((val=ad1816_mixer_set (devc, cmd & 0xff, val))<0) | 
|  | 902 | return val; | 
|  | 903 | else | 
|  | 904 | return put_user(val, p); | 
|  | 905 | } | 
|  | 906 | } else { | 
|  | 907 | /* read ioctl */ | 
|  | 908 | switch (cmd & 0xff) { | 
|  | 909 |  | 
|  | 910 | case SOUND_MIXER_RECSRC: | 
|  | 911 | val=devc->recmask; | 
|  | 912 | return put_user(val, p); | 
|  | 913 | break; | 
|  | 914 |  | 
|  | 915 | case SOUND_MIXER_DEVMASK: | 
|  | 916 | val=devc->supported_devices; | 
|  | 917 | return put_user(val, p); | 
|  | 918 | break; | 
|  | 919 |  | 
|  | 920 | case SOUND_MIXER_STEREODEVS: | 
|  | 921 | val=devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX); | 
|  | 922 | return put_user(val, p); | 
|  | 923 | break; | 
|  | 924 |  | 
|  | 925 | case SOUND_MIXER_RECMASK: | 
|  | 926 | val=devc->supported_rec_devices; | 
|  | 927 | return put_user(val, p); | 
|  | 928 | break; | 
|  | 929 |  | 
|  | 930 | case SOUND_MIXER_CAPS: | 
|  | 931 | val=SOUND_CAP_EXCL_INPUT; | 
|  | 932 | return put_user(val, p); | 
|  | 933 | break; | 
|  | 934 |  | 
|  | 935 | default: | 
|  | 936 | if ((val=ad1816_mixer_get (devc, cmd & 0xff))<0) | 
|  | 937 | return val; | 
|  | 938 | else | 
|  | 939 | return put_user(val, p); | 
|  | 940 | } | 
|  | 941 | } | 
|  | 942 | } else | 
|  | 943 | /* not for mixer */ | 
|  | 944 | return -(EINVAL); | 
|  | 945 | } | 
|  | 946 |  | 
|  | 947 | /* ------------------------------------------------------------------- */ | 
|  | 948 |  | 
|  | 949 | /* Mixer structure */ | 
|  | 950 |  | 
|  | 951 | static struct mixer_operations ad1816_mixer_operations = { | 
|  | 952 | .owner	= THIS_MODULE, | 
|  | 953 | .id	= "AD1816", | 
|  | 954 | .name	= "AD1816 Mixer", | 
|  | 955 | .ioctl	= ad1816_mixer_ioctl | 
|  | 956 | }; | 
|  | 957 |  | 
|  | 958 |  | 
|  | 959 | /* ------------------------------------------------------------------- */ | 
|  | 960 |  | 
|  | 961 | /* stuff for card recognition, init and unloading PNP ...*/ | 
|  | 962 |  | 
|  | 963 |  | 
|  | 964 | /* check if AD1816 present at specified hw_config and register device with OS | 
|  | 965 | * return 1 if initialization was successful, 0 otherwise | 
|  | 966 | */ | 
|  | 967 | static int __init ad1816_init_card (struct address_info *hw_config, | 
|  | 968 | struct pnp_dev *pnp) | 
|  | 969 | { | 
|  | 970 | ad1816_info    *devc = NULL; | 
|  | 971 | int tmp; | 
|  | 972 | int oss_devno = -1; | 
|  | 973 |  | 
|  | 974 | printk(KERN_INFO "ad1816: initializing card: io=0x%x, irq=%d, dma=%d, " | 
|  | 975 | "dma2=%d, clockfreq=%d, options=%d isadmabug=%d " | 
|  | 976 | "%s\n", | 
|  | 977 | hw_config->io_base, | 
|  | 978 | hw_config->irq, | 
|  | 979 | hw_config->dma, | 
|  | 980 | hw_config->dma2, | 
|  | 981 | ad1816_clockfreq, | 
|  | 982 | options, | 
|  | 983 | isa_dma_bridge_buggy, | 
|  | 984 | pnp?"(PNP)":""); | 
|  | 985 |  | 
|  | 986 | /* ad1816_info structure remaining ? */ | 
|  | 987 | if (nr_ad1816_devs >= MAX_AUDIO_DEV) { | 
|  | 988 | printk(KERN_WARNING "ad1816: no more ad1816_info structures " | 
|  | 989 | "left\n"); | 
|  | 990 | goto out; | 
|  | 991 | } | 
|  | 992 |  | 
|  | 993 | devc = &dev_info[nr_ad1816_devs]; | 
|  | 994 | devc->base = hw_config->io_base; | 
|  | 995 | devc->irq = hw_config->irq; | 
|  | 996 | devc->dma_playback=hw_config->dma; | 
|  | 997 | devc->dma_capture=hw_config->dma2; | 
|  | 998 | devc->opened = 0; | 
|  | 999 | devc->pnpdev = pnp; | 
|  | 1000 | spin_lock_init(&devc->lock); | 
|  | 1001 |  | 
|  | 1002 | if (!request_region(devc->base, 16, "AD1816 Sound")) { | 
|  | 1003 | printk(KERN_WARNING "ad1816: I/O port 0x%03x not free\n", | 
|  | 1004 | devc->base); | 
|  | 1005 | goto out; | 
|  | 1006 | } | 
|  | 1007 |  | 
|  | 1008 | printk(KERN_INFO "ad1816: Examining AD1816 at address 0x%03x.\n", | 
|  | 1009 | devc->base); | 
|  | 1010 |  | 
|  | 1011 |  | 
|  | 1012 | /* tests for ad1816 */ | 
|  | 1013 | /* base+0: bit 1 must be set but not 255 */ | 
|  | 1014 | tmp=inb(devc->base); | 
|  | 1015 | if ( (tmp&0x80)==0 || tmp==255 ) { | 
|  | 1016 | printk (KERN_INFO "ad1816: Chip is not an AD1816 or chip " | 
|  | 1017 | "is not active (Test 0)\n"); | 
|  | 1018 | goto out_release_region; | 
|  | 1019 | } | 
|  | 1020 |  | 
|  | 1021 | /* writes to ireg 8 are copied to ireg 9 */ | 
|  | 1022 | ad_write(devc,8,12345); | 
|  | 1023 | if (ad_read(devc,9)!=12345) { | 
|  | 1024 | printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 1)\n"); | 
|  | 1025 | goto out_release_region; | 
|  | 1026 | } | 
|  | 1027 |  | 
|  | 1028 | /* writes to ireg 8 are copied to ireg 9 */ | 
|  | 1029 | ad_write(devc,8,54321); | 
|  | 1030 | if (ad_read(devc,9)!=54321) { | 
|  | 1031 | printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 2)\n"); | 
|  | 1032 | goto out_release_region; | 
|  | 1033 | } | 
|  | 1034 |  | 
|  | 1035 | /* writes to ireg 10 are copied to ireg 11 */ | 
|  | 1036 | ad_write(devc,10,54321); | 
|  | 1037 | if (ad_read(devc,11)!=54321) { | 
|  | 1038 | printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 3)\n"); | 
|  | 1039 | goto out_release_region; | 
|  | 1040 | } | 
|  | 1041 |  | 
|  | 1042 | /* writes to ireg 10 are copied to ireg 11 */ | 
|  | 1043 | ad_write(devc,10,12345); | 
|  | 1044 | if (ad_read(devc,11)!=12345) { | 
|  | 1045 | printk (KERN_INFO "ad1816: Chip is not an AD1816 (Test 4)\n"); | 
|  | 1046 | goto out_release_region; | 
|  | 1047 | } | 
|  | 1048 |  | 
|  | 1049 | /* bit in base +1 cannot be set to 1 */ | 
|  | 1050 | tmp=inb(devc->base+1); | 
|  | 1051 | outb(0xff,devc->base+1); | 
|  | 1052 | if (inb(devc->base+1)!=tmp) { | 
|  | 1053 | printk(KERN_INFO "ad1816: Chip is not an AD1816 (Test 5)\n"); | 
|  | 1054 | goto out_release_region; | 
|  | 1055 | } | 
|  | 1056 |  | 
|  | 1057 | printk(KERN_INFO "ad1816: AD1816 (version %d) successfully detected!\n", | 
|  | 1058 | ad_read(devc,45)); | 
|  | 1059 |  | 
|  | 1060 | /* disable all interrupts */ | 
|  | 1061 | ad_write(devc,1,0); | 
|  | 1062 |  | 
|  | 1063 | /* Clear pending interrupts */ | 
|  | 1064 | outb (0, devc->base+1); | 
|  | 1065 |  | 
|  | 1066 | /* allocate irq */ | 
|  | 1067 | if (devc->irq < 0 || devc->irq > 15) | 
|  | 1068 | goto out_release_region; | 
|  | 1069 | if (request_irq(devc->irq, ad1816_interrupt,0, | 
|  | 1070 | "SoundPort", devc) < 0)	{ | 
|  | 1071 | printk(KERN_WARNING "ad1816: IRQ in use\n"); | 
|  | 1072 | goto out_release_region; | 
|  | 1073 | } | 
|  | 1074 |  | 
|  | 1075 | /* DMA stuff */ | 
|  | 1076 | if (sound_alloc_dma (devc->dma_playback, "Sound System")) { | 
|  | 1077 | printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", | 
|  | 1078 | devc->dma_playback); | 
|  | 1079 | goto out_free_irq; | 
|  | 1080 | } | 
|  | 1081 |  | 
|  | 1082 | if ( devc->dma_capture >= 0 && | 
|  | 1083 | devc->dma_capture != devc->dma_playback) { | 
|  | 1084 | if (sound_alloc_dma(devc->dma_capture, | 
|  | 1085 | "Sound System (capture)")) { | 
|  | 1086 | printk(KERN_WARNING "ad1816: Can't allocate DMA%d\n", | 
|  | 1087 | devc->dma_capture); | 
|  | 1088 | goto out_free_dma; | 
|  | 1089 | } | 
|  | 1090 | devc->audio_mode=DMA_AUTOMODE|DMA_DUPLEX; | 
|  | 1091 | } else { | 
|  | 1092 | printk(KERN_WARNING "ad1816: Only one DMA channel " | 
|  | 1093 | "available/configured. No duplex operation possible\n"); | 
|  | 1094 | devc->audio_mode=DMA_AUTOMODE; | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | conf_printf2 ("AD1816 audio driver", | 
|  | 1098 | devc->base, devc->irq, devc->dma_playback, | 
|  | 1099 | devc->dma_capture); | 
|  | 1100 |  | 
|  | 1101 | /* register device */ | 
|  | 1102 | if ((oss_devno = sound_install_audiodrv (AUDIO_DRIVER_VERSION, | 
|  | 1103 | "AD1816 audio driver", | 
|  | 1104 | &ad1816_audio_driver, | 
|  | 1105 | sizeof (struct audio_driver), | 
|  | 1106 | devc->audio_mode, | 
|  | 1107 | ad_format_mask, | 
|  | 1108 | devc, | 
|  | 1109 | devc->dma_playback, | 
|  | 1110 | devc->dma_capture)) < 0) { | 
|  | 1111 | printk(KERN_WARNING "ad1816: Can't install sound driver\n"); | 
|  | 1112 | goto out_free_dma_2; | 
|  | 1113 | } | 
|  | 1114 |  | 
|  | 1115 |  | 
|  | 1116 | ad_write(devc,32,0x80f0); /* sound system mode */ | 
|  | 1117 | if (options&1) { | 
|  | 1118 | ad_write(devc,33,0); /* disable all audiosources for dsp */ | 
|  | 1119 | } else { | 
|  | 1120 | ad_write(devc,33,0x03f8); /* enable all audiosources for dsp */ | 
|  | 1121 | } | 
|  | 1122 | ad_write(devc,4,0x8080);  /* default values for volumes (muted)*/ | 
|  | 1123 | ad_write(devc,5,0x8080); | 
|  | 1124 | ad_write(devc,6,0x8080); | 
|  | 1125 | ad_write(devc,7,0x8080); | 
|  | 1126 | ad_write(devc,15,0x8888); | 
|  | 1127 | ad_write(devc,16,0x8888); | 
|  | 1128 | ad_write(devc,17,0x8888); | 
|  | 1129 | ad_write(devc,18,0x8888); | 
|  | 1130 | ad_write(devc,19,0xc888); /* +20db mic active */ | 
|  | 1131 | ad_write(devc,14,0x0000); /* Master volume unmuted */ | 
|  | 1132 | ad_write(devc,39,0x009f); /* 3D effect on 0% phone out muted */ | 
|  | 1133 | ad_write(devc,44,0x0080); /* everything on power, 3d enabled for d/a */ | 
|  | 1134 | outb(0x10,devc->base+8); /* set dma mode */ | 
|  | 1135 | outb(0x10,devc->base+9); | 
|  | 1136 |  | 
|  | 1137 | /* enable capture + playback interrupt */ | 
|  | 1138 | ad_write(devc,1,0xc000); | 
|  | 1139 |  | 
|  | 1140 | /* set mixer defaults */ | 
|  | 1141 | ad1816_mixer_reset (devc); | 
|  | 1142 |  | 
|  | 1143 | /* register mixer */ | 
|  | 1144 | if ((audio_devs[oss_devno]->mixer_dev=sound_install_mixer( | 
|  | 1145 | MIXER_DRIVER_VERSION, | 
|  | 1146 | "AD1816 audio driver", | 
|  | 1147 | &ad1816_mixer_operations, | 
|  | 1148 | sizeof (struct mixer_operations), | 
|  | 1149 | devc)) < 0) { | 
|  | 1150 | printk(KERN_WARNING "Can't install mixer\n"); | 
|  | 1151 | } | 
|  | 1152 | /* make ad1816_info active */ | 
|  | 1153 | nr_ad1816_devs++; | 
|  | 1154 | printk(KERN_INFO "ad1816: card successfully installed!\n"); | 
|  | 1155 | return 1; | 
|  | 1156 | /* error handling */ | 
|  | 1157 | out_free_dma_2: | 
|  | 1158 | if (devc->dma_capture >= 0 && devc->dma_capture != devc->dma_playback) | 
|  | 1159 | sound_free_dma(devc->dma_capture); | 
|  | 1160 | out_free_dma: | 
|  | 1161 | sound_free_dma(devc->dma_playback); | 
|  | 1162 | out_free_irq: | 
|  | 1163 | free_irq(devc->irq, devc); | 
|  | 1164 | out_release_region: | 
|  | 1165 | release_region(devc->base, 16); | 
|  | 1166 | out: | 
|  | 1167 | return 0; | 
|  | 1168 | } | 
|  | 1169 |  | 
|  | 1170 | static void __exit unload_card(ad1816_info *devc) | 
|  | 1171 | { | 
|  | 1172 | int  mixer, dev = 0; | 
|  | 1173 |  | 
|  | 1174 | if (devc != NULL) { | 
|  | 1175 | printk("ad1816: Unloading card at address 0x%03x\n",devc->base); | 
|  | 1176 |  | 
|  | 1177 | dev = devc->dev_no; | 
|  | 1178 | mixer = audio_devs[dev]->mixer_dev; | 
|  | 1179 |  | 
|  | 1180 | /* unreg mixer*/ | 
|  | 1181 | if(mixer>=0) { | 
|  | 1182 | sound_unload_mixerdev(mixer); | 
|  | 1183 | } | 
|  | 1184 | /* unreg audiodev */ | 
|  | 1185 | sound_unload_audiodev(dev); | 
|  | 1186 |  | 
|  | 1187 | /* free dma channels */ | 
|  | 1188 | if (devc->dma_capture>=0 && | 
|  | 1189 | devc->dma_capture != devc->dma_playback) { | 
|  | 1190 | sound_free_dma(devc->dma_capture); | 
|  | 1191 | } | 
|  | 1192 | sound_free_dma (devc->dma_playback); | 
|  | 1193 | /* free irq */ | 
|  | 1194 | free_irq(devc->irq, devc); | 
|  | 1195 | /* free io */ | 
|  | 1196 | release_region (devc->base, 16); | 
|  | 1197 | #ifdef __ISAPNP__ | 
|  | 1198 | if (devc->pnpdev) { | 
|  | 1199 | pnp_disable_dev(devc->pnpdev); | 
|  | 1200 | pnp_device_detach(devc->pnpdev); | 
|  | 1201 | } | 
|  | 1202 | #endif | 
|  | 1203 |  | 
|  | 1204 | } else | 
|  | 1205 | printk(KERN_WARNING "ad1816: no device/card specified\n"); | 
|  | 1206 | } | 
|  | 1207 |  | 
|  | 1208 | static int __initdata io = -1; | 
|  | 1209 | static int __initdata irq = -1; | 
|  | 1210 | static int __initdata dma = -1; | 
|  | 1211 | static int __initdata dma2 = -1; | 
|  | 1212 |  | 
|  | 1213 | #ifdef __ISAPNP__ | 
|  | 1214 | /* use isapnp for configuration */ | 
|  | 1215 | static int isapnp	= 1; | 
|  | 1216 | static int isapnpjump; | 
|  | 1217 | module_param(isapnp, bool, 0); | 
|  | 1218 | module_param(isapnpjump, int, 0); | 
|  | 1219 | #endif | 
|  | 1220 |  | 
|  | 1221 | module_param(io, int, 0); | 
|  | 1222 | module_param(irq, int, 0); | 
|  | 1223 | module_param(dma, int, 0); | 
|  | 1224 | module_param(dma2, int, 0); | 
|  | 1225 | module_param(ad1816_clockfreq, int, 0); | 
|  | 1226 | module_param(options, int, 0); | 
|  | 1227 |  | 
|  | 1228 | #ifdef __ISAPNP__ | 
|  | 1229 | static struct { | 
|  | 1230 | unsigned short card_vendor, card_device; | 
|  | 1231 | unsigned short vendor; | 
|  | 1232 | unsigned short function; | 
|  | 1233 | struct ad1816_data *data; | 
|  | 1234 | } isapnp_ad1816_list[] __initdata = { | 
|  | 1235 | {	ISAPNP_ANY_ID, ISAPNP_ANY_ID, | 
|  | 1236 | ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7150), | 
|  | 1237 | NULL }, | 
|  | 1238 | {	ISAPNP_ANY_ID, ISAPNP_ANY_ID, | 
|  | 1239 | ISAPNP_VENDOR('A','D','S'), ISAPNP_FUNCTION(0x7180), | 
|  | 1240 | NULL }, | 
|  | 1241 | {0} | 
|  | 1242 | }; | 
|  | 1243 |  | 
|  | 1244 | MODULE_DEVICE_TABLE(isapnp, isapnp_ad1816_list); | 
|  | 1245 |  | 
|  | 1246 |  | 
|  | 1247 | static void __init ad1816_config_pnp_card(struct pnp_card *card, | 
|  | 1248 | unsigned short vendor, | 
|  | 1249 | unsigned short function) | 
|  | 1250 | { | 
|  | 1251 | struct address_info cfg; | 
|  | 1252 | struct pnp_dev *card_dev = pnp_find_dev(card, vendor, function, NULL); | 
|  | 1253 | if (!card_dev) return; | 
|  | 1254 | if (pnp_device_attach(card_dev) < 0) { | 
|  | 1255 | printk(KERN_WARNING "ad1816: Failed to attach PnP device\n"); | 
|  | 1256 | return; | 
|  | 1257 | } | 
|  | 1258 | if (pnp_activate_dev(card_dev) < 0) { | 
|  | 1259 | printk(KERN_WARNING "ad1816: Failed to activate PnP device\n"); | 
|  | 1260 | pnp_device_detach(card_dev); | 
|  | 1261 | return; | 
|  | 1262 | } | 
|  | 1263 | cfg.io_base = pnp_port_start(card_dev, 2); | 
|  | 1264 | cfg.irq = pnp_irq(card_dev, 0); | 
|  | 1265 | cfg.dma = pnp_irq(card_dev, 0); | 
|  | 1266 | cfg.dma2 = pnp_irq(card_dev, 1); | 
|  | 1267 | if (!ad1816_init_card(&cfg, card_dev)) { | 
|  | 1268 | pnp_disable_dev(card_dev); | 
|  | 1269 | pnp_device_detach(card_dev); | 
|  | 1270 | } | 
|  | 1271 | } | 
|  | 1272 |  | 
|  | 1273 | static void __init ad1816_config_pnp_cards(void) | 
|  | 1274 | { | 
|  | 1275 | int nr_pnp_cfg; | 
|  | 1276 | int i; | 
|  | 1277 |  | 
|  | 1278 | /* Count entries in isapnp_ad1816_list */ | 
|  | 1279 | for (nr_pnp_cfg = 0; isapnp_ad1816_list[nr_pnp_cfg].card_vendor != 0; | 
|  | 1280 | nr_pnp_cfg++); | 
|  | 1281 | /* Check and adjust isapnpjump */ | 
|  | 1282 | if( isapnpjump < 0 || isapnpjump >= nr_pnp_cfg) { | 
|  | 1283 | printk(KERN_WARNING | 
|  | 1284 | "ad1816: Valid range for isapnpjump is 0-%d. " | 
|  | 1285 | "Adjusted to 0.\n", nr_pnp_cfg-1); | 
|  | 1286 | isapnpjump = 0; | 
|  | 1287 | } | 
|  | 1288 | for (i = isapnpjump; isapnp_ad1816_list[i].card_vendor != 0; i++) { | 
|  | 1289 | struct pnp_card *card = NULL; | 
|  | 1290 | /* iterate over all pnp cards */ | 
|  | 1291 | while ((card = pnp_find_card(isapnp_ad1816_list[i].card_vendor, | 
|  | 1292 | isapnp_ad1816_list[i].card_device, card))) | 
|  | 1293 | ad1816_config_pnp_card(card, | 
|  | 1294 | isapnp_ad1816_list[i].vendor, | 
|  | 1295 | isapnp_ad1816_list[i].function); | 
|  | 1296 | } | 
|  | 1297 | } | 
|  | 1298 | #endif | 
|  | 1299 |  | 
|  | 1300 | /* module initialization */ | 
|  | 1301 | static int __init init_ad1816(void) | 
|  | 1302 | { | 
|  | 1303 | printk(KERN_INFO "ad1816: AD1816 sounddriver " | 
|  | 1304 | "Copyright (C) 1998-2003 by Thorsten Knabe and " | 
|  | 1305 | "others\n"); | 
|  | 1306 | #ifdef AD1816_CLOCK | 
|  | 1307 | /* set ad1816_clockfreq if set during compilation */ | 
|  | 1308 | ad1816_clockfreq=AD1816_CLOCK; | 
|  | 1309 | #endif | 
|  | 1310 | if (ad1816_clockfreq<5000 || ad1816_clockfreq>100000) { | 
|  | 1311 | ad1816_clockfreq=33000; | 
|  | 1312 | } | 
|  | 1313 |  | 
|  | 1314 | #ifdef __ISAPNP__ | 
|  | 1315 | /* configure PnP cards */ | 
|  | 1316 | if(isapnp) ad1816_config_pnp_cards(); | 
|  | 1317 | #endif | 
|  | 1318 | /* configure card by module params */ | 
|  | 1319 | if (io != -1 && irq != -1 && dma != -1) { | 
|  | 1320 | struct address_info cfg; | 
|  | 1321 | cfg.io_base = io; | 
|  | 1322 | cfg.irq = irq; | 
|  | 1323 | cfg.dma = dma; | 
|  | 1324 | cfg.dma2 = dma2; | 
|  | 1325 | ad1816_init_card(&cfg, NULL); | 
|  | 1326 | } | 
|  | 1327 | if (nr_ad1816_devs <= 0) | 
|  | 1328 | return -ENODEV; | 
|  | 1329 | return 0; | 
|  | 1330 | } | 
|  | 1331 |  | 
|  | 1332 | /* module cleanup */ | 
|  | 1333 | static void __exit cleanup_ad1816 (void) | 
|  | 1334 | { | 
|  | 1335 | int          i; | 
|  | 1336 | ad1816_info  *devc = NULL; | 
|  | 1337 |  | 
|  | 1338 | /* remove any soundcard */ | 
|  | 1339 | for (i = 0;  i < nr_ad1816_devs; i++) { | 
|  | 1340 | devc = &dev_info[i]; | 
|  | 1341 | unload_card(devc); | 
|  | 1342 | } | 
|  | 1343 | nr_ad1816_devs=0; | 
|  | 1344 | printk(KERN_INFO "ad1816: driver unloaded!\n"); | 
|  | 1345 | } | 
|  | 1346 |  | 
|  | 1347 | module_init(init_ad1816); | 
|  | 1348 | module_exit(cleanup_ad1816); | 
|  | 1349 |  | 
|  | 1350 | #ifndef MODULE | 
|  | 1351 | /* kernel command line parameter evaluation */ | 
|  | 1352 | static int __init setup_ad1816(char *str) | 
|  | 1353 | { | 
|  | 1354 | /* io, irq, dma, dma2 */ | 
|  | 1355 | int ints[5]; | 
|  | 1356 |  | 
|  | 1357 | str = get_options(str, ARRAY_SIZE(ints), ints); | 
|  | 1358 |  | 
|  | 1359 | io	= ints[1]; | 
|  | 1360 | irq	= ints[2]; | 
|  | 1361 | dma	= ints[3]; | 
|  | 1362 | dma2	= ints[4]; | 
|  | 1363 | return 1; | 
|  | 1364 | } | 
|  | 1365 |  | 
|  | 1366 | __setup("ad1816=", setup_ad1816); | 
|  | 1367 | #endif | 
|  | 1368 | MODULE_LICENSE("GPL"); |