linux/sound/oss/pas2_pcm.c
<<
>>
Prefs
   1/*
   2 * pas2_pcm.c Audio routines for PAS16
   3 *
   4 *
   5 * Copyright (C) by Hannu Savolainen 1993-1997
   6 *
   7 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
   8 * Version 2 (June 1991). See the "COPYING" file distributed with this software
   9 * for more info.
  10 *
  11 *
  12 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
  13 * Alan Cox        : Swatted a double allocation of device bug. Made a few
  14 *                   more things module options.
  15 * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
  16 */
  17
  18#include <linux/init.h>
  19#include <linux/spinlock.h>
  20#include <linux/timex.h>
  21#include "sound_config.h"
  22
  23#include "pas2.h"
  24
  25#ifndef DEB
  26#define DEB(WHAT)
  27#endif
  28
  29#define PAS_PCM_INTRBITS (0x08)
  30/*
  31 * Sample buffer timer interrupt enable
  32 */
  33
  34#define PCM_NON 0
  35#define PCM_DAC 1
  36#define PCM_ADC 2
  37
  38static unsigned long pcm_speed;         /* sampling rate */
  39static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
  40static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
  41static unsigned char pcm_filter;        /* filter FLAG */
  42static unsigned char pcm_mode = PCM_NON;
  43static unsigned long pcm_count;
  44static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
  45static int      pcm_busy;
  46int             pas_audiodev = -1;
  47static int      open_mode;
  48
  49extern spinlock_t pas_lock;
  50
  51static int pcm_set_speed(int arg)
  52{
  53        int foo, tmp;
  54        unsigned long flags;
  55
  56        if (arg == 0)
  57                return pcm_speed;
  58
  59        if (arg > 44100)
  60                arg = 44100;
  61        if (arg < 5000)
  62                arg = 5000;
  63
  64        if (pcm_channels & 2)
  65        {
  66                foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg;
  67                arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo;
  68        }
  69        else
  70        {
  71                foo = (PIT_TICK_RATE + (arg / 2)) / arg;
  72                arg = (PIT_TICK_RATE + (foo / 2)) / foo;
  73        }
  74
  75        pcm_speed = arg;
  76
  77        tmp = pas_read(0x0B8A);
  78
  79        /*
  80         * Set anti-aliasing filters according to sample rate. You really *NEED*
  81         * to enable this feature for all normal recording unless you want to
  82         * experiment with aliasing effects.
  83         * These filters apply to the selected "recording" source.
  84         * I (pfw) don't know the encoding of these 5 bits. The values shown
  85         * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
  86         *
  87         * I cleared bit 5 of these values, since that bit controls the master
  88         * mute flag. (Olav W├Âlfelschneider)
  89         *
  90         */
  91#if !defined NO_AUTO_FILTER_SET
  92        tmp &= 0xe0;
  93        if (pcm_speed >= 2 * 17897)
  94                tmp |= 0x01;
  95        else if (pcm_speed >= 2 * 15909)
  96                tmp |= 0x02;
  97        else if (pcm_speed >= 2 * 11931)
  98                tmp |= 0x09;
  99        else if (pcm_speed >= 2 * 8948)
 100                tmp |= 0x11;
 101        else if (pcm_speed >= 2 * 5965)
 102                tmp |= 0x19;
 103        else if (pcm_speed >= 2 * 2982)
 104                tmp |= 0x04;
 105        pcm_filter = tmp;
 106#endif
 107
 108        spin_lock_irqsave(&pas_lock, flags);
 109
 110        pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
 111        pas_write(0x00 | 0x30 | 0x04, 0x138B);
 112        pas_write(foo & 0xff, 0x1388);
 113        pas_write((foo >> 8) & 0xff, 0x1388);
 114        pas_write(tmp, 0x0B8A);
 115
 116        spin_unlock_irqrestore(&pas_lock, flags);
 117
 118        return pcm_speed;
 119}
 120
 121static int pcm_set_channels(int arg)
 122{
 123
 124        if ((arg != 1) && (arg != 2))
 125                return pcm_channels;
 126
 127        if (arg != pcm_channels)
 128        {
 129                pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
 130
 131                pcm_channels = arg;
 132                pcm_set_speed(pcm_speed);       /* The speed must be reinitialized */
 133        }
 134        return pcm_channels;
 135}
 136
 137static int pcm_set_bits(int arg)
 138{
 139        if (arg == 0)
 140                return pcm_bits;
 141
 142        if ((arg & pcm_bitsok) != arg)
 143                return pcm_bits;
 144
 145        if (arg != pcm_bits)
 146        {
 147                pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
 148
 149                pcm_bits = arg;
 150        }
 151        return pcm_bits;
 152}
 153
 154static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
 155{
 156        int val, ret;
 157        int __user *p = arg;
 158
 159        DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 160
 161        switch (cmd) 
 162        {
 163        case SOUND_PCM_WRITE_RATE:
 164                if (get_user(val, p)) 
 165                        return -EFAULT;
 166                ret = pcm_set_speed(val);
 167                break;
 168
 169        case SOUND_PCM_READ_RATE:
 170                ret = pcm_speed;
 171                break;
 172                
 173        case SNDCTL_DSP_STEREO:
 174                if (get_user(val, p)) 
 175                        return -EFAULT;
 176                ret = pcm_set_channels(val + 1) - 1;
 177                break;
 178
 179        case SOUND_PCM_WRITE_CHANNELS:
 180                if (get_user(val, p)) 
 181                        return -EFAULT;
 182                ret = pcm_set_channels(val);
 183                break;
 184
 185        case SOUND_PCM_READ_CHANNELS:
 186                ret = pcm_channels;
 187                break;
 188
 189        case SNDCTL_DSP_SETFMT:
 190                if (get_user(val, p))
 191                        return -EFAULT;
 192                ret = pcm_set_bits(val);
 193                break;
 194                
 195        case SOUND_PCM_READ_BITS:
 196                ret = pcm_bits;
 197                break;
 198  
 199        default:
 200                return -EINVAL;
 201        }
 202        return put_user(ret, p);
 203}
 204
 205static void pas_audio_reset(int dev)
 206{
 207        DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
 208
 209        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);      /* Disable PCM */
 210}
 211
 212static int pas_audio_open(int dev, int mode)
 213{
 214        int             err;
 215        unsigned long   flags;
 216
 217        DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
 218
 219        spin_lock_irqsave(&pas_lock, flags);
 220        if (pcm_busy)
 221        {
 222                spin_unlock_irqrestore(&pas_lock, flags);
 223                return -EBUSY;
 224        }
 225        pcm_busy = 1;
 226        spin_unlock_irqrestore(&pas_lock, flags);
 227
 228        if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
 229                return err;
 230
 231
 232        pcm_count = 0;
 233        open_mode = mode;
 234
 235        return 0;
 236}
 237
 238static void pas_audio_close(int dev)
 239{
 240        unsigned long   flags;
 241
 242        DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
 243
 244        spin_lock_irqsave(&pas_lock, flags);
 245
 246        pas_audio_reset(dev);
 247        pas_remove_intr(PAS_PCM_INTRBITS);
 248        pcm_mode = PCM_NON;
 249
 250        pcm_busy = 0;
 251        spin_unlock_irqrestore(&pas_lock, flags);
 252}
 253
 254static void pas_audio_output_block(int dev, unsigned long buf, int count,
 255                       int intrflag)
 256{
 257        unsigned long   flags, cnt;
 258
 259        DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
 260
 261        cnt = count;
 262        if (audio_devs[dev]->dmap_out->dma > 3)
 263                cnt >>= 1;
 264
 265        if (audio_devs[dev]->flags & DMA_AUTOMODE &&
 266            intrflag &&
 267            cnt == pcm_count)
 268                return;
 269
 270        spin_lock_irqsave(&pas_lock, flags);
 271
 272        pas_write(pas_read(0xF8A) & ~0x40,
 273                  0xF8A);
 274
 275        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 276
 277        if (audio_devs[dev]->dmap_out->dma > 3)
 278                count >>= 1;
 279
 280        if (count != pcm_count)
 281        {
 282                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
 283                pas_write(0x40 | 0x30 | 0x04, 0x138B);
 284                pas_write(count & 0xff, 0x1389);
 285                pas_write((count >> 8) & 0xff, 0x1389);
 286                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 287
 288                pcm_count = count;
 289        }
 290        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 291#ifdef NO_TRIGGER
 292        pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
 293#endif
 294
 295        pcm_mode = PCM_DAC;
 296
 297        spin_unlock_irqrestore(&pas_lock, flags);
 298}
 299
 300static void pas_audio_start_input(int dev, unsigned long buf, int count,
 301                      int intrflag)
 302{
 303        unsigned long   flags;
 304        int             cnt;
 305
 306        DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
 307
 308        cnt = count;
 309        if (audio_devs[dev]->dmap_out->dma > 3)
 310                cnt >>= 1;
 311
 312        if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
 313            intrflag &&
 314            cnt == pcm_count)
 315                return;
 316
 317        spin_lock_irqsave(&pas_lock, flags);
 318
 319        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 320
 321        if (audio_devs[dev]->dmap_out->dma > 3)
 322                count >>= 1;
 323
 324        if (count != pcm_count)
 325        {
 326                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
 327                pas_write(0x40 | 0x30 | 0x04, 0x138B);
 328                pas_write(count & 0xff, 0x1389);
 329                pas_write((count >> 8) & 0xff, 0x1389);
 330                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 331
 332                pcm_count = count;
 333        }
 334        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 335#ifdef NO_TRIGGER
 336        pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
 337#endif
 338
 339        pcm_mode = PCM_ADC;
 340
 341        spin_unlock_irqrestore(&pas_lock, flags);
 342}
 343
 344#ifndef NO_TRIGGER
 345static void pas_audio_trigger(int dev, int state)
 346{
 347        unsigned long   flags;
 348
 349        spin_lock_irqsave(&pas_lock, flags);
 350        state &= open_mode;
 351
 352        if (state & PCM_ENABLE_OUTPUT)
 353                pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
 354        else if (state & PCM_ENABLE_INPUT)
 355                pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
 356        else
 357                pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
 358
 359        spin_unlock_irqrestore(&pas_lock, flags);
 360}
 361#endif
 362
 363static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
 364{
 365        pas_audio_reset(dev);
 366        return 0;
 367}
 368
 369static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
 370{
 371        pas_audio_reset(dev);
 372        return 0;
 373}
 374
 375static struct audio_driver pas_audio_driver =
 376{
 377        .owner                  = THIS_MODULE,
 378        .open                   = pas_audio_open,
 379        .close                  = pas_audio_close,
 380        .output_block           = pas_audio_output_block,
 381        .start_input            = pas_audio_start_input,
 382        .ioctl                  = pas_audio_ioctl,
 383        .prepare_for_input      = pas_audio_prepare_for_input,
 384        .prepare_for_output     = pas_audio_prepare_for_output,
 385        .halt_io                = pas_audio_reset,
 386        .trigger                = pas_audio_trigger
 387};
 388
 389void __init pas_pcm_init(struct address_info *hw_config)
 390{
 391        DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
 392
 393        pcm_bitsok = 8;
 394        if (pas_read(0xEF8B) & 0x08)
 395                pcm_bitsok |= 16;
 396
 397        pcm_set_speed(DSP_DEFAULT_SPEED);
 398
 399        if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
 400                                        "Pro Audio Spectrum",
 401                                        &pas_audio_driver,
 402                                        sizeof(struct audio_driver),
 403                                        DMA_AUTOMODE,
 404                                        AFMT_U8 | AFMT_S16_LE,
 405                                        NULL,
 406                                        hw_config->dma,
 407                                        hw_config->dma)) < 0)
 408                printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
 409}
 410
 411void pas_pcm_interrupt(unsigned char status, int cause)
 412{
 413        if (cause == 1)
 414        {
 415                /*
 416                 * Halt the PCM first. Otherwise we don't have time to start a new
 417                 * block before the PCM chip proceeds to the next sample
 418                 */
 419
 420                if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
 421                        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
 422
 423                switch (pcm_mode)
 424                {
 425                        case PCM_DAC:
 426                                DMAbuf_outputintr(pas_audiodev, 1);
 427                                break;
 428
 429                        case PCM_ADC:
 430                                DMAbuf_inputintr(pas_audiodev);
 431                                break;
 432
 433                        default:
 434                                printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
 435                }
 436        }
 437}
 438
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.