linux/sound/oss/dmasound/dmasound_q40.c
<<
>>
Prefs
   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