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