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