1 /* 2 * linux/sound/oss/dmasound/dmasound_q40.c 3 * 4 * Q40 DMA Sound Driver 5 * 6 * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits 7 * prior to 28/01/2001 8 * 9 * 28/01/2001 [0.1] Iain Sandoe 10 * - added versioning 11 * - put in and populated the hardware_afmts field. 12 * [0.2] - put in SNDCTL_DSP_GETCAPS value. 13 * [0.3] - put in default hard/soft settings. 14 */ 15 16 17 #include <linux/module.h> 18 #include <linux/init.h> 19 #include <linux/slab.h> 20 #include <linux/soundcard.h> 21 #include <linux/interrupt.h> 22 23 #include <asm/uaccess.h> 24 #include <asm/q40ints.h> 25 #include <asm/q40_master.h> 26 27 #include "dmasound.h" 28 29 #define DMASOUND_Q40_REVISION 0 30 #define DMASOUND_Q40_EDITION 3 31 32 static int expand_bal; /* Balance factor for expanding (not volume!) */ 33 static int expand_data; /* Data for expanding */ 34 35 36 /*** Low level stuff *********************************************************/ 37 38 39 static void *Q40Alloc(unsigned int size, gfp_t flags); 40 static void Q40Free(void *, unsigned int); 41 static int Q40IrqInit(void); 42 #ifdef MODULE 43 static void Q40IrqCleanUp(void); 44 #endif 45 static void Q40Silence(void); 46 static void Q40Init(void); 47 static int Q40SetFormat(int format); 48 static int Q40SetVolume(int volume); 49 static void Q40PlayNextFrame(int index); 50 static void Q40Play(void); 51 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy); 52 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy); 53 static void Q40Interrupt(void); 54 55 56 /*** Mid level stuff *********************************************************/ 57 58 59 60 /* userCount, frameUsed, frameLeft == byte counts */ 61 static ssize_t q40_ct_law(const u_char __user *userPtr, size_t userCount, 62 u_char frame[], ssize_t *frameUsed, 63 ssize_t frameLeft) 64 { 65 char *table = dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8; 66 ssize_t count, used; 67 u_char *p = (u_char *) &frame[*frameUsed]; 68 69 used = count = min_t(size_t, userCount, frameLeft); 70 if (copy_from_user(p,userPtr,count)) 71 return -EFAULT; 72 while (count > 0) { 73 *p = table[*p]+128; 74 p++; 75 count--; 76 } 77 *frameUsed += used ; 78 return used; 79 } 80 81 82 static ssize_t q40_ct_s8(const u_char __user *userPtr, size_t userCount, 83 u_char frame[], ssize_t *frameUsed, 84 ssize_t frameLeft) 85 { 86 ssize_t count, used; 87 u_char *p = (u_char *) &frame[*frameUsed]; 88 89 used = count = min_t(size_t, userCount, frameLeft); 90 if (copy_from_user(p,userPtr,count)) 91 return -EFAULT; 92 while (count > 0) { 93 *p = *p + 128; 94 p++; 95 count--; 96 } 97 *frameUsed += used; 98 return used; 99 } 100 101 static ssize_t q40_ct_u8(const u_char __user *userPtr, size_t userCount, 102 u_char frame[], ssize_t *frameUsed, 103 ssize_t frameLeft) 104 { 105 ssize_t count, used; 106 u_char *p = (u_char *) &frame[*frameUsed]; 107 108 used = count = min_t(size_t, userCount, frameLeft); 109 if (copy_from_user(p,userPtr,count)) 110 return -EFAULT; 111 *frameUsed += used; 112 return used; 113 } 114 115 116 /* a bit too complicated to optimise right now ..*/ 117 static ssize_t q40_ctx_law(const u_char __user *userPtr, size_t userCount, 118 u_char frame[], ssize_t *frameUsed, 119 ssize_t frameLeft) 120 { 121 unsigned char *table = (unsigned char *) 122 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); 123 unsigned int data = expand_data; 124 u_char *p = (u_char *) &frame[*frameUsed]; 125 int bal = expand_bal; 126 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 127 int utotal, ftotal; 128 129 ftotal = frameLeft; 130 utotal = userCount; 131 while (frameLeft) { 132 u_char c; 133 if (bal < 0) { 134 if (userCount == 0) 135 break; 136 if (get_user(c, userPtr++)) 137 return -EFAULT; 138 data = table[c]; 139 data += 0x80; 140 userCount--; 141 bal += hSpeed; 142 } 143 *p++ = data; 144 frameLeft--; 145 bal -= sSpeed; 146 } 147 expand_bal = bal; 148 expand_data = data; 149 *frameUsed += (ftotal - frameLeft); 150 utotal -= userCount; 151 return utotal; 152 } 153 154 155 static ssize_t q40_ctx_s8(const u_char __user *userPtr, size_t userCount, 156 u_char frame[], ssize_t *frameUsed, 157 ssize_t frameLeft) 158 { 159 u_char *p = (u_char *) &frame[*frameUsed]; 160 unsigned int data = expand_data; 161 int bal = expand_bal; 162 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 163 int utotal, ftotal; 164 165 166 ftotal = frameLeft; 167 utotal = userCount; 168 while (frameLeft) { 169 u_char c; 170 if (bal < 0) { 171 if (userCount == 0) 172 break; 173 if (get_user(c, userPtr++)) 174 return -EFAULT; 175 data = c ; 176 data += 0x80; 177 userCount--; 178 bal += hSpeed; 179 } 180 *p++ = data; 181 frameLeft--; 182 bal -= sSpeed; 183 } 184 expand_bal = bal; 185 expand_data = data; 186 *frameUsed += (ftotal - frameLeft); 187 utotal -= userCount; 188 return utotal; 189 } 190 191 192 static ssize_t q40_ctx_u8(const u_char __user *userPtr, size_t userCount, 193 u_char frame[], ssize_t *frameUsed, 194 ssize_t frameLeft) 195 { 196 u_char *p = (u_char *) &frame[*frameUsed]; 197 unsigned int data = expand_data; 198 int bal = expand_bal; 199 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 200 int utotal, ftotal; 201 202 ftotal = frameLeft; 203 utotal = userCount; 204 while (frameLeft) { 205 u_char c; 206 if (bal < 0) { 207 if (userCount == 0) 208 break; 209 if (get_user(c, userPtr++)) 210 return -EFAULT; 211 data = c ; 212 userCount--; 213 bal += hSpeed; 214 } 215 *p++ = data; 216 frameLeft--; 217 bal -= sSpeed; 218 } 219 expand_bal = bal; 220 expand_data = data; 221 *frameUsed += (ftotal - frameLeft) ; 222 utotal -= userCount; 223 return utotal; 224 } 225 226 /* compressing versions */ 227 static ssize_t q40_ctc_law(const u_char __user *userPtr, size_t userCount, 228 u_char frame[], ssize_t *frameUsed, 229 ssize_t frameLeft) 230 { 231 unsigned char *table = (unsigned char *) 232 (dmasound.soft.format == AFMT_MU_LAW ? dmasound_ulaw2dma8: dmasound_alaw2dma8); 233 unsigned int data = expand_data; 234 u_char *p = (u_char *) &frame[*frameUsed]; 235 int bal = expand_bal; 236 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 237 int utotal, ftotal; 238 239 ftotal = frameLeft; 240 utotal = userCount; 241 while (frameLeft) { 242 u_char c; 243 while(bal<0) { 244 if (userCount == 0) 245 goto lout; 246 if (!(bal<(-hSpeed))) { 247 if (get_user(c, userPtr)) 248 return -EFAULT; 249 data = 0x80 + table[c]; 250 } 251 userPtr++; 252 userCount--; 253 bal += hSpeed; 254 } 255 *p++ = data; 256 frameLeft--; 257 bal -= sSpeed; 258 } 259 lout: 260 expand_bal = bal; 261 expand_data = data; 262 *frameUsed += (ftotal - frameLeft); 263 utotal -= userCount; 264 return utotal; 265 } 266 267 268 static ssize_t q40_ctc_s8(const u_char __user *userPtr, size_t userCount, 269 u_char frame[], ssize_t *frameUsed, 270 ssize_t frameLeft) 271 { 272 u_char *p = (u_char *) &frame[*frameUsed]; 273 unsigned int data = expand_data; 274 int bal = expand_bal; 275 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 276 int utotal, ftotal; 277 278 ftotal = frameLeft; 279 utotal = userCount; 280 while (frameLeft) { 281 u_char c; 282 while (bal < 0) { 283 if (userCount == 0) 284 goto lout; 285 if (!(bal<(-hSpeed))) { 286 if (get_user(c, userPtr)) 287 return -EFAULT; 288 data = c + 0x80; 289 } 290 userPtr++; 291 userCount--; 292 bal += hSpeed; 293 } 294 *p++ = data; 295 frameLeft--; 296 bal -= sSpeed; 297 } 298 lout: 299 expand_bal = bal; 300 expand_data = data; 301 *frameUsed += (ftotal - frameLeft); 302 utotal -= userCount; 303 return utotal; 304 } 305 306 307 static ssize_t q40_ctc_u8(const u_char __user *userPtr, size_t userCount, 308 u_char frame[], ssize_t *frameUsed, 309 ssize_t frameLeft) 310 { 311 u_char *p = (u_char *) &frame[*frameUsed]; 312 unsigned int data = expand_data; 313 int bal = expand_bal; 314 int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed; 315 int utotal, ftotal; 316 317 ftotal = frameLeft; 318 utotal = userCount; 319 while (frameLeft) { 320 u_char c; 321 while (bal < 0) { 322 if (userCount == 0) 323 goto lout; 324 if (!(bal<(-hSpeed))) { 325 if (get_user(c, userPtr)) 326 return -EFAULT; 327 data = c ; 328 } 329 userPtr++; 330 userCount--; 331 bal += hSpeed; 332 } 333 *p++ = data; 334 frameLeft--; 335 bal -= sSpeed; 336 } 337 lout: 338 expand_bal = bal; 339 expand_data = data; 340 *frameUsed += (ftotal - frameLeft) ; 341 utotal -= userCount; 342 return utotal; 343 } 344 345 346 static TRANS transQ40Normal = { 347 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL 348 }; 349 350 static TRANS transQ40Expanding = { 351 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL 352 }; 353 354 static TRANS transQ40Compressing = { 355 q40_ctc_law, q40_ctc_law, q40_ctc_s8, q40_ctc_u8, NULL, NULL, NULL, NULL 356 }; 357 358 359 /*** Low level stuff *********************************************************/ 360 361 static void *Q40Alloc(unsigned int size, gfp_t flags) 362 { 363 return kmalloc(size, flags); /* change to vmalloc */ 364 } 365 366 static void Q40Free(void *ptr, unsigned int size) 367 { 368 kfree(ptr); 369 } 370 371 static int __init Q40IrqInit(void) 372 { 373 /* Register interrupt handler. */ 374 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, 375 "DMA sound", Q40Interrupt); 376 377 return(1); 378 } 379 380 381 #ifdef MODULE 382 static void Q40IrqCleanUp(void) 383 { 384 master_outb(0,SAMPLE_ENABLE_REG); 385 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); 386 } 387 #endif /* MODULE */ 388 389 390 static void Q40Silence(void) 391 { 392 master_outb(0,SAMPLE_ENABLE_REG); 393 *DAC_LEFT=*DAC_RIGHT=127; 394 } 395 396 static char *q40_pp; 397 static unsigned int q40_sc; 398 399 static void Q40PlayNextFrame(int index) 400 { 401 u_char *start; 402 u_long size; 403 u_char speed; 404 405 /* used by Q40Play() if all doubts whether there really is something 406 * to be played are already wiped out. 407 */ 408 start = write_sq.buffers[write_sq.front]; 409 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); 410 411 q40_pp=start; 412 q40_sc=size; 413 414 write_sq.front = (write_sq.front+1) % write_sq.max_count; 415 write_sq.active++; 416 417 speed=(dmasound.hard.speed==10000 ? 0 : 1); 418 419 master_outb( 0,SAMPLE_ENABLE_REG); 420 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); 421 if (dmasound.soft.stereo) 422 request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, 423 "Q40 sound", Q40Interrupt); 424 else 425 request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, 426 "Q40 sound", Q40Interrupt); 427 428 master_outb( speed, SAMPLE_RATE_REG); 429 master_outb( 1,SAMPLE_CLEAR_REG); 430 master_outb( 1,SAMPLE_ENABLE_REG); 431 } 432 433 static void Q40Play(void) 434 { 435 unsigned long flags; 436 437 if (write_sq.active || write_sq.count<=0 ) { 438 /* There's already a frame loaded */ 439 return; 440 } 441 442 /* nothing in the queue */ 443 if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { 444 /* hmmm, the only existing frame is not 445 * yet filled and we're not syncing? 446 */ 447 return; 448 } 449 spin_lock_irqsave(&dmasound.lock, flags); 450 Q40PlayNextFrame(1); 451 spin_unlock_irqrestore(&dmasound.lock, flags); 452 } 453 454 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy) 455 { 456 spin_lock(&dmasound.lock); 457 if (q40_sc>1){ 458 *DAC_LEFT=*q40_pp++; 459 *DAC_RIGHT=*q40_pp++; 460 q40_sc -=2; 461 master_outb(1,SAMPLE_CLEAR_REG); 462 }else Q40Interrupt(); 463 spin_unlock(&dmasound.lock); 464 return IRQ_HANDLED; 465 } 466 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy) 467 { 468 spin_lock(&dmasound.lock); 469 if (q40_sc>0){ 470 *DAC_LEFT=*q40_pp; 471 *DAC_RIGHT=*q40_pp++; 472 q40_sc --; 473 master_outb(1,SAMPLE_CLEAR_REG); 474 }else Q40Interrupt(); 475 spin_unlock(&dmasound.lock); 476 return IRQ_HANDLED; 477 } 478 static void Q40Interrupt(void) 479 { 480 if (!write_sq.active) { 481 /* playing was interrupted and sq_reset() has already cleared 482 * the sq variables, so better don't do anything here. 483 */ 484 WAKE_UP(write_sq.sync_queue); 485 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ 486 goto exit; 487 } else write_sq.active=0; 488 write_sq.count--; 489 Q40Play(); 490 491 if (q40_sc<2) 492 { /* there was nothing to play, disable irq */ 493 master_outb(0,SAMPLE_ENABLE_REG); 494 *DAC_LEFT=*DAC_RIGHT=127; 495 } 496 WAKE_UP(write_sq.action_queue); 497 498 exit: 499 master_outb(1,SAMPLE_CLEAR_REG); 500 } 501 502 503 static void Q40Init(void) 504 { 505 int i, idx; 506 const int freq[] = {10000, 20000}; 507 508 /* search a frequency that fits into the allowed error range */ 509 510 idx = -1; 511 for (i = 0; i < 2; i++) 512 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) 513 idx = i; 514 515 dmasound.hard = dmasound.soft; 516 /*sound.hard.stereo=1;*/ /* no longer true */ 517 dmasound.hard.size=8; 518 519 if (idx > -1) { 520 dmasound.soft.speed = freq[idx]; 521 dmasound.trans_write = &transQ40Normal; 522 } else 523 dmasound.trans_write = &transQ40Expanding; 524 525 Q40Silence(); 526 527 if (dmasound.hard.speed > 20200) { 528 /* squeeze the sound, we do that */ 529 dmasound.hard.speed = 20000; 530 dmasound.trans_write = &transQ40Compressing; 531 } else if (dmasound.hard.speed > 10000) { 532 dmasound.hard.speed = 20000; 533 } else { 534 dmasound.hard.speed = 10000; 535 } 536 expand_bal = -dmasound.soft.speed; 537 } 538 539 540 static int Q40SetFormat(int format) 541 { 542 /* Q40 sound supports only 8bit modes */ 543 544 switch (format) { 545 case AFMT_QUERY: 546 return(dmasound.soft.format); 547 case AFMT_MU_LAW: 548 case AFMT_A_LAW: 549 case AFMT_S8: 550 case AFMT_U8: 551 break; 552 default: 553 format = AFMT_S8; 554 } 555 556 dmasound.soft.format = format; 557 dmasound.soft.size = 8; 558 if (dmasound.minDev == SND_DEV_DSP) { 559 dmasound.dsp.format = format; 560 dmasound.dsp.size = 8; 561 } 562 Q40Init(); 563 564 return(format); 565 } 566 567 static int Q40SetVolume(int volume) 568 { 569 return 0; 570 } 571 572 573 /*** Machine definitions *****************************************************/ 574 575 static SETTINGS def_hard = { 576 .format = AFMT_U8, 577 .stereo = 0, 578 .size = 8, 579 .speed = 10000 580 } ; 581 582 static SETTINGS def_soft = { 583 .format = AFMT_U8, 584 .stereo = 0, 585 .size = 8, 586 .speed = 8000 587 } ; 588 589 static MACHINE machQ40 = { 590 .name = "Q40", 591 .name2 = "Q40", 592 .owner = THIS_MODULE, 593 .dma_alloc = Q40Alloc, 594 .dma_free = Q40Free, 595 .irqinit = Q40IrqInit, 596 #ifdef MODULE 597 .irqcleanup = Q40IrqCleanUp, 598 #endif /* MODULE */ 599 .init = Q40Init, 600 .silence = Q40Silence, 601 .setFormat = Q40SetFormat, 602 .setVolume = Q40SetVolume, 603 .play = Q40Play, 604 .min_dsp_speed = 10000, 605 .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION), 606 .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */ 607 .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */ 608 }; 609 610 611 /*** Config & Setup **********************************************************/ 612 613 614 static int __init dmasound_q40_init(void) 615 { 616 if (MACH_IS_Q40) { 617 dmasound.mach = machQ40; 618 dmasound.mach.default_hard = def_hard ; 619 dmasound.mach.default_soft = def_soft ; 620 return dmasound_init(); 621 } else 622 return -ENODEV; 623 } 624 625 static void __exit dmasound_q40_cleanup(void) 626 { 627 dmasound_deinit(); 628 } 629 630 module_init(dmasound_q40_init); 631 module_exit(dmasound_q40_cleanup); 632 633 MODULE_DESCRIPTION("Q40/Q60 sound driver"); 634 MODULE_LICENSE("GPL"); 635