root/sound/oss/dmasound/dmasound_q40.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. q40_ct_law
  2. q40_ct_s8
  3. q40_ct_u8
  4. q40_ctx_law
  5. q40_ctx_s8
  6. q40_ctx_u8
  7. q40_ctc_law
  8. q40_ctc_s8
  9. q40_ctc_u8
  10. Q40Alloc
  11. Q40Free
  12. Q40IrqInit
  13. Q40IrqCleanUp
  14. Q40Silence
  15. Q40PlayNextFrame
  16. Q40Play
  17. Q40StereoInterrupt
  18. Q40MonoInterrupt
  19. Q40Interrupt
  20. Q40Init
  21. Q40SetFormat
  22. Q40SetVolume
  23. dmasound_q40_init
  24. dmasound_q40_cleanup

   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 
  33 static int expand_bal;  /* Balance factor for expanding (not volume!) */
  34 static int expand_data; /* Data for expanding */
  35 
  36 
  37 /*** Low level stuff *********************************************************/
  38 
  39 
  40 static void *Q40Alloc(unsigned int size, gfp_t flags);
  41 static void Q40Free(void *, unsigned int);
  42 static int Q40IrqInit(void);
  43 #ifdef MODULE
  44 static void Q40IrqCleanUp(void);
  45 #endif
  46 static void Q40Silence(void);
  47 static void Q40Init(void);
  48 static int Q40SetFormat(int format);
  49 static int Q40SetVolume(int volume);
  50 static void Q40PlayNextFrame(int index);
  51 static void Q40Play(void);
  52 static irqreturn_t Q40StereoInterrupt(int irq, void *dummy);
  53 static irqreturn_t Q40MonoInterrupt(int irq, void *dummy);
  54 static void Q40Interrupt(void);
  55 
  56 
  57 /*** Mid level stuff *********************************************************/
  58 
  59 
  60 
  61 /* userCount, frameUsed, frameLeft == byte counts */
  62 static 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 
  83 static 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 
 102 static 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 ..*/
 118 static 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 
 156 static 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 
 193 static 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 */
 228 static 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 
 269 static 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 
 308 static 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 
 347 static TRANS transQ40Normal = {
 348         q40_ct_law, q40_ct_law, q40_ct_s8, q40_ct_u8, NULL, NULL, NULL, NULL
 349 };
 350 
 351 static TRANS transQ40Expanding = {
 352         q40_ctx_law, q40_ctx_law, q40_ctx_s8, q40_ctx_u8, NULL, NULL, NULL, NULL
 353 };
 354 
 355 static 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 static void *Q40Alloc(unsigned int size, gfp_t flags)
 363 {
 364          return kmalloc(size, flags); /* change to vmalloc */
 365 }
 366 
 367 static void Q40Free(void *ptr, unsigned int size)
 368 {
 369         kfree(ptr);
 370 }
 371 
 372 static 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
 384 static void Q40IrqCleanUp(void)
 385 {
 386         master_outb(0,SAMPLE_ENABLE_REG);
 387         free_irq(Q40_IRQ_SAMPLE, Q40Interrupt);
 388 }
 389 #endif /* MODULE */
 390 
 391 
 392 static void Q40Silence(void)
 393 {
 394         master_outb(0,SAMPLE_ENABLE_REG);
 395         *DAC_LEFT=*DAC_RIGHT=127;
 396 }
 397 
 398 static char *q40_pp;
 399 static unsigned int q40_sc;
 400 
 401 static 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 
 438 static 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 
 459 static 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 }
 471 static 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 }
 483 static 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 
 508 static 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 
 545 static 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 
 572 static int Q40SetVolume(int volume)
 573 {
 574     return 0;
 575 }
 576 
 577 
 578 /*** Machine definitions *****************************************************/
 579 
 580 static SETTINGS def_hard = {
 581         .format = AFMT_U8,
 582         .stereo = 0,
 583         .size   = 8,
 584         .speed  = 10000
 585 } ;
 586 
 587 static SETTINGS def_soft = {
 588         .format = AFMT_U8,
 589         .stereo = 0,
 590         .size   = 8,
 591         .speed  = 8000
 592 } ;
 593 
 594 static 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 
 619 static 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 
 630 static void __exit dmasound_q40_cleanup(void)
 631 {
 632         dmasound_deinit();
 633 }
 634 
 635 module_init(dmasound_q40_init);
 636 module_exit(dmasound_q40_cleanup);
 637 
 638 MODULE_DESCRIPTION("Q40/Q60 sound driver");
 639 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */