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