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