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 32static int expand_bal; /* Balance factor for expanding (not volume!) */ 33static int expand_data; /* Data for expanding */ 34 35 36/*** Low level stuff *********************************************************/ 37 38 39static void *Q40Alloc(unsigned int size, gfp_t flags); 40static void Q40Free(void *, unsigned int); 41static int Q40IrqInit(void); 42#ifdef MODULE 43static void Q40IrqCleanUp(void); 44#endif 45static void Q40Silence(void); 46static void Q40Init(void); 47static int Q40SetFormat(int format); 48static int Q40SetVolume(int volume); 49static void Q40PlayNextFrame(int index); 50static void Q40Play(void); 51static irqreturn_t Q40StereoInterrupt(int irq, void *dummy); 52static irqreturn_t Q40MonoInterrupt(int irq, void *dummy); 53static void Q40Interrupt(void); 54 55 56/*** Mid level stuff *********************************************************/ 57 58 59 60/* userCount, frameUsed, frameLeft == byte counts */ 61static 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 82static 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 101static 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 ..*/ 117static 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 155static 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 192static 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 */ 227static 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 268static 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 307static 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 346static TRANS transQ40Normal = { 347 q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL 348}; 349 350static TRANS transQ40Expanding = { 351 q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL 352}; 353 354static 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 361static void *Q40Alloc(unsigned int size, gfp_t flags) 362{ 363 return kmalloc(size, flags); /* change to vmalloc */ 364} 365 366static void Q40Free(void *ptr, unsigned int size) 367{ 368 kfree(ptr); 369} 370 371static int __init Q40IrqInit(void) 372{ 373 /* Register interrupt handler. */ 374 if (request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, 375 "DMA sound", Q40Interrupt)) 376 return 0; 377 378 return(1); 379} 380 381 382#ifdef MODULE 383static void Q40IrqCleanUp(void) 384{ 385 master_outb(0,SAMPLE_ENABLE_REG); 386 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); 387} 388#endif /* MODULE */ 389 390 391static void Q40Silence(void) 392{ 393 master_outb(0,SAMPLE_ENABLE_REG); 394 *DAC_LEFT=*DAC_RIGHT=127; 395} 396 397static char *q40_pp; 398static unsigned int q40_sc; 399 400static void Q40PlayNextFrame(int index) 401{ 402 u_char *start; 403 u_long size; 404 u_char speed; 405 int error; 406 407 /* used by Q40Play() if all doubts whether there really is something 408 * to be played are already wiped out. 409 */ 410 start = write_sq.buffers[write_sq.front]; 411 size = (write_sq.count == index ? write_sq.rear_size : write_sq.block_size); 412 413 q40_pp=start; 414 q40_sc=size; 415 416 write_sq.front = (write_sq.front+1) % write_sq.max_count; 417 write_sq.active++; 418 419 speed=(dmasound.hard.speed==10000 ? 0 : 1); 420 421 master_outb( 0,SAMPLE_ENABLE_REG); 422 free_irq(Q40_IRQ_SAMPLE, Q40Interrupt); 423 if (dmasound.soft.stereo) 424 error = request_irq(Q40_IRQ_SAMPLE, Q40StereoInterrupt, 0, 425 "Q40 sound", Q40Interrupt); 426 else 427 error = request_irq(Q40_IRQ_SAMPLE, Q40MonoInterrupt, 0, 428 "Q40 sound", Q40Interrupt); 429 if (error && printk_ratelimit()) 430 pr_err("Couldn't register sound interrupt\n"); 431 432 master_outb( speed, SAMPLE_RATE_REG); 433 master_outb( 1,SAMPLE_CLEAR_REG); 434 master_outb( 1,SAMPLE_ENABLE_REG); 435} 436 437static void Q40Play(void) 438{ 439 unsigned long flags; 440 441 if (write_sq.active || write_sq.count<=0 ) { 442 /* There's already a frame loaded */ 443 return; 444 } 445 446 /* nothing in the queue */ 447 if (write_sq.count <= 1 && write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { 448 /* hmmm, the only existing frame is not 449 * yet filled and we're not syncing? 450 */ 451 return; 452 } 453 spin_lock_irqsave(&dmasound.lock, flags); 454 Q40PlayNextFrame(1); 455 spin_unlock_irqrestore(&dmasound.lock, flags); 456} 457 458static irqreturn_t Q40StereoInterrupt(int irq, void *dummy) 459{ 460 spin_lock(&dmasound.lock); 461 if (q40_sc>1){ 462 *DAC_LEFT=*q40_pp++; 463 *DAC_RIGHT=*q40_pp++; 464 q40_sc -=2; 465 master_outb(1,SAMPLE_CLEAR_REG); 466 }else Q40Interrupt(); 467 spin_unlock(&dmasound.lock); 468 return IRQ_HANDLED; 469} 470static irqreturn_t Q40MonoInterrupt(int irq, void *dummy) 471{ 472 spin_lock(&dmasound.lock); 473 if (q40_sc>0){ 474 *DAC_LEFT=*q40_pp; 475 *DAC_RIGHT=*q40_pp++; 476 q40_sc --; 477 master_outb(1,SAMPLE_CLEAR_REG); 478 }else Q40Interrupt(); 479 spin_unlock(&dmasound.lock); 480 return IRQ_HANDLED; 481} 482static void Q40Interrupt(void) 483{ 484 if (!write_sq.active) { 485 /* playing was interrupted and sq_reset() has already cleared 486 * the sq variables, so better don't do anything here. 487 */ 488 WAKE_UP(write_sq.sync_queue); 489 master_outb(0,SAMPLE_ENABLE_REG); /* better safe */ 490 goto exit; 491 } else write_sq.active=0; 492 write_sq.count--; 493 Q40Play(); 494 495 if (q40_sc<2) 496 { /* there was nothing to play, disable irq */ 497 master_outb(0,SAMPLE_ENABLE_REG); 498 *DAC_LEFT=*DAC_RIGHT=127; 499 } 500 WAKE_UP(write_sq.action_queue); 501 502 exit: 503 master_outb(1,SAMPLE_CLEAR_REG); 504} 505 506 507static void Q40Init(void) 508{ 509 int i, idx; 510 const int freq[] = {10000, 20000}; 511 512 /* search a frequency that fits into the allowed error range */ 513 514 idx = -1; 515 for (i = 0; i < 2; i++) 516 if ((100 * abs(dmasound.soft.speed - freq[i]) / freq[i]) <= catchRadius) 517 idx = i; 518 519 dmasound.hard = dmasound.soft; 520 /*sound.hard.stereo=1;*/ /* no longer true */ 521 dmasound.hard.size=8; 522 523 if (idx > -1) { 524 dmasound.soft.speed = freq[idx]; 525 dmasound.trans_write = &transQ40Normal; 526 } else 527 dmasound.trans_write = &transQ40Expanding; 528 529 Q40Silence(); 530 531 if (dmasound.hard.speed > 20200) { 532 /* squeeze the sound, we do that */ 533 dmasound.hard.speed = 20000; 534 dmasound.trans_write = &transQ40Compressing; 535 } else if (dmasound.hard.speed > 10000) { 536 dmasound.hard.speed = 20000; 537 } else { 538 dmasound.hard.speed = 10000; 539 } 540 expand_bal = -dmasound.soft.speed; 541} 542 543 544static int Q40SetFormat(int format) 545{ 546 /* Q40 sound supports only 8bit modes */ 547 548 switch (format) { 549 case AFMT_QUERY: 550 return(dmasound.soft.format); 551 case AFMT_MU_LAW: 552 case AFMT_A_LAW: 553 case AFMT_S8: 554 case AFMT_U8: 555 break; 556 default: 557 format = AFMT_S8; 558 } 559 560 dmasound.soft.format = format; 561 dmasound.soft.size = 8; 562 if (dmasound.minDev == SND_DEV_DSP) { 563 dmasound.dsp.format = format; 564 dmasound.dsp.size = 8; 565 } 566 Q40Init(); 567 568 return(format); 569} 570 571static int Q40SetVolume(int volume) 572{ 573 return 0; 574} 575 576 577/*** Machine definitions *****************************************************/ 578 579static SETTINGS def_hard = { 580 .format = AFMT_U8, 581 .stereo = 0, 582 .size = 8, 583 .speed = 10000 584} ; 585 586static SETTINGS def_soft = { 587 .format = AFMT_U8, 588 .stereo = 0, 589 .size = 8, 590 .speed = 8000 591} ; 592 593static MACHINE machQ40 = { 594 .name = "Q40", 595 .name2 = "Q40", 596 .owner = THIS_MODULE, 597 .dma_alloc = Q40Alloc, 598 .dma_free = Q40Free, 599 .irqinit = Q40IrqInit, 600#ifdef MODULE 601 .irqcleanup = Q40IrqCleanUp, 602#endif /* MODULE */ 603 .init = Q40Init, 604 .silence = Q40Silence, 605 .setFormat = Q40SetFormat, 606 .setVolume = Q40SetVolume, 607 .play = Q40Play, 608 .min_dsp_speed = 10000, 609 .version = ((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION), 610 .hardware_afmts = AFMT_U8, /* h'ware-supported formats *only* here */ 611 .capabilities = DSP_CAP_BATCH /* As per SNDCTL_DSP_GETCAPS */ 612}; 613 614 615/*** Config & Setup **********************************************************/ 616 617 618static int __init dmasound_q40_init(void) 619{ 620 if (MACH_IS_Q40) { 621 dmasound.mach = machQ40; 622 dmasound.mach.default_hard = def_hard ; 623 dmasound.mach.default_soft = def_soft ; 624 return dmasound_init(); 625 } else 626 return -ENODEV; 627} 628 629static void __exit dmasound_q40_cleanup(void) 630{ 631 dmasound_deinit(); 632} 633 634module_init(dmasound_q40_init); 635module_exit(dmasound_q40_cleanup); 636 637MODULE_DESCRIPTION("Q40/Q60 sound driver"); 638MODULE_LICENSE("GPL"); 639