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#define PAS_PCM_INTRBITS (0x08)
  26/*
  27 * Sample buffer timer interrupt enable
  28 */
  29
  30#define PCM_NON 0
  31#define PCM_DAC 1
  32#define PCM_ADC 2
  33
  34static unsigned long pcm_speed;         /* sampling rate */
  35static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
  36static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
  37static unsigned char pcm_filter;        /* filter FLAG */
  38static unsigned char pcm_mode = PCM_NON;
  39static unsigned long pcm_count;
  40static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
  41static int      pcm_busy;
  42int             pas_audiodev = -1;
  43static int      open_mode;
  44
  45extern spinlock_t pas_lock;
  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 = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg;
  63                arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo;
  64        }
  65        else
  66        {
  67                foo = (PIT_TICK_RATE + (arg / 2)) / arg;
  68                arg = (PIT_TICK_RATE + (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        spin_lock_irqsave(&pas_lock, flags);
 105
 106        pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
 107        pas_write(0x00 | 0x30 | 0x04, 0x138B);
 108        pas_write(foo & 0xff, 0x1388);
 109        pas_write((foo >> 8) & 0xff, 0x1388);
 110        pas_write(tmp, 0x0B8A);
 111
 112        spin_unlock_irqrestore(&pas_lock, flags);
 113
 114        return pcm_speed;
 115}
 116
 117static int pcm_set_channels(int arg)
 118{
 119
 120        if ((arg != 1) && (arg != 2))
 121                return pcm_channels;
 122
 123        if (arg != pcm_channels)
 124        {
 125                pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
 126
 127                pcm_channels = arg;
 128                pcm_set_speed(pcm_speed);       /* The speed must be reinitialized */
 129        }
 130        return pcm_channels;
 131}
 132
 133static int pcm_set_bits(int arg)
 134{
 135        if (arg == 0)
 136                return pcm_bits;
 137
 138        if ((arg & pcm_bitsok) != arg)
 139                return pcm_bits;
 140
 141        if (arg != pcm_bits)
 142        {
 143                pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
 144
 145                pcm_bits = arg;
 146        }
 147        return pcm_bits;
 148}
 149
 150static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
 151{
 152        int val, ret;
 153        int __user *p = arg;
 154
 155        switch (cmd) 
 156        {
 157        case SOUND_PCM_WRITE_RATE:
 158                if (get_user(val, p)) 
 159                        return -EFAULT;
 160                ret = pcm_set_speed(val);
 161                break;
 162
 163        case SOUND_PCM_READ_RATE:
 164                ret = pcm_speed;
 165                break;
 166                
 167        case SNDCTL_DSP_STEREO:
 168                if (get_user(val, p)) 
 169                        return -EFAULT;
 170                ret = pcm_set_channels(val + 1) - 1;
 171                break;
 172
 173        case SOUND_PCM_WRITE_CHANNELS:
 174                if (get_user(val, p)) 
 175                        return -EFAULT;
 176                ret = pcm_set_channels(val);
 177                break;
 178
 179        case SOUND_PCM_READ_CHANNELS:
 180                ret = pcm_channels;
 181                break;
 182
 183        case SNDCTL_DSP_SETFMT:
 184                if (get_user(val, p))
 185                        return -EFAULT;
 186                ret = pcm_set_bits(val);
 187                break;
 188                
 189        case SOUND_PCM_READ_BITS:
 190                ret = pcm_bits;
 191                break;
 192  
 193        default:
 194                return -EINVAL;
 195        }
 196        return put_user(ret, p);
 197}
 198
 199static void pas_audio_reset(int dev)
 200{
 201        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);      /* Disable PCM */
 202}
 203
 204static int pas_audio_open(int dev, int mode)
 205{
 206        int             err;
 207        unsigned long   flags;
 208
 209        spin_lock_irqsave(&pas_lock, flags);
 210        if (pcm_busy)
 211        {
 212                spin_unlock_irqrestore(&pas_lock, flags);
 213                return -EBUSY;
 214        }
 215        pcm_busy = 1;
 216        spin_unlock_irqrestore(&pas_lock, flags);
 217
 218        if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
 219                return err;
 220
 221
 222        pcm_count = 0;
 223        open_mode = mode;
 224
 225        return 0;
 226}
 227
 228static void pas_audio_close(int dev)
 229{
 230        unsigned long   flags;
 231
 232        spin_lock_irqsave(&pas_lock, flags);
 233
 234        pas_audio_reset(dev);
 235        pas_remove_intr(PAS_PCM_INTRBITS);
 236        pcm_mode = PCM_NON;
 237
 238        pcm_busy = 0;
 239        spin_unlock_irqrestore(&pas_lock, flags);
 240}
 241
 242static void pas_audio_output_block(int dev, unsigned long buf, int count,
 243                       int intrflag)
 244{
 245        unsigned long   flags, cnt;
 246
 247        cnt = count;
 248        if (audio_devs[dev]->dmap_out->dma > 3)
 249                cnt >>= 1;
 250
 251        if (audio_devs[dev]->flags & DMA_AUTOMODE &&
 252            intrflag &&
 253            cnt == pcm_count)
 254                return;
 255
 256        spin_lock_irqsave(&pas_lock, flags);
 257
 258        pas_write(pas_read(0xF8A) & ~0x40,
 259                  0xF8A);
 260
 261        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 262
 263        if (audio_devs[dev]->dmap_out->dma > 3)
 264                count >>= 1;
 265
 266        if (count != pcm_count)
 267        {
 268                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
 269                pas_write(0x40 | 0x30 | 0x04, 0x138B);
 270                pas_write(count & 0xff, 0x1389);
 271                pas_write((count >> 8) & 0xff, 0x1389);
 272                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 273
 274                pcm_count = count;
 275        }
 276        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 277#ifdef NO_TRIGGER
 278        pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
 279#endif
 280
 281        pcm_mode = PCM_DAC;
 282
 283        spin_unlock_irqrestore(&pas_lock, flags);
 284}
 285
 286static void pas_audio_start_input(int dev, unsigned long buf, int count,
 287                      int intrflag)
 288{
 289        unsigned long   flags;
 290        int             cnt;
 291
 292        cnt = count;
 293        if (audio_devs[dev]->dmap_out->dma > 3)
 294                cnt >>= 1;
 295
 296        if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
 297            intrflag &&
 298            cnt == pcm_count)
 299                return;
 300
 301        spin_lock_irqsave(&pas_lock, flags);
 302
 303        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 304
 305        if (audio_devs[dev]->dmap_out->dma > 3)
 306                count >>= 1;
 307
 308        if (count != pcm_count)
 309        {
 310                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
 311                pas_write(0x40 | 0x30 | 0x04, 0x138B);
 312                pas_write(count & 0xff, 0x1389);
 313                pas_write((count >> 8) & 0xff, 0x1389);
 314                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
 315
 316                pcm_count = count;
 317        }
 318        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
 319#ifdef NO_TRIGGER
 320        pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
 321#endif
 322
 323        pcm_mode = PCM_ADC;
 324
 325        spin_unlock_irqrestore(&pas_lock, flags);
 326}
 327
 328#ifndef NO_TRIGGER
 329static void pas_audio_trigger(int dev, int state)
 330{
 331        unsigned long   flags;
 332
 333        spin_lock_irqsave(&pas_lock, flags);
 334        state &= open_mode;
 335
 336        if (state & PCM_ENABLE_OUTPUT)
 337                pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
 338        else if (state & PCM_ENABLE_INPUT)
 339                pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
 340        else
 341                pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
 342
 343        spin_unlock_irqrestore(&pas_lock, flags);
 344}
 345#endif
 346
 347static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
 348{
 349        pas_audio_reset(dev);
 350        return 0;
 351}
 352
 353static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
 354{
 355        pas_audio_reset(dev);
 356        return 0;
 357}
 358
 359static struct audio_driver pas_audio_driver =
 360{
 361        .owner                  = THIS_MODULE,
 362        .open                   = pas_audio_open,
 363        .close                  = pas_audio_close,
 364        .output_block           = pas_audio_output_block,
 365        .start_input            = pas_audio_start_input,
 366        .ioctl                  = pas_audio_ioctl,
 367        .prepare_for_input      = pas_audio_prepare_for_input,
 368        .prepare_for_output     = pas_audio_prepare_for_output,
 369        .halt_io                = pas_audio_reset,
 370        .trigger                = pas_audio_trigger
 371};
 372
 373void __init pas_pcm_init(struct address_info *hw_config)
 374{
 375        pcm_bitsok = 8;
 376        if (pas_read(0xEF8B) & 0x08)
 377                pcm_bitsok |= 16;
 378
 379        pcm_set_speed(DSP_DEFAULT_SPEED);
 380
 381        if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
 382                                        "Pro Audio Spectrum",
 383                                        &pas_audio_driver,
 384                                        sizeof(struct audio_driver),
 385                                        DMA_AUTOMODE,
 386                                        AFMT_U8 | AFMT_S16_LE,
 387                                        NULL,
 388                                        hw_config->dma,
 389                                        hw_config->dma)) < 0)
 390                printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
 391}
 392
 393void pas_pcm_interrupt(unsigned char status, int cause)
 394{
 395        if (cause == 1)
 396        {
 397                /*
 398                 * Halt the PCM first. Otherwise we don't have time to start a new
 399                 * block before the PCM chip proceeds to the next sample
 400                 */
 401
 402                if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
 403                        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
 404
 405                switch (pcm_mode)
 406                {
 407                        case PCM_DAC:
 408                                DMAbuf_outputintr(pas_audiodev, 1);
 409                                break;
 410
 411                        case PCM_ADC:
 412                                DMAbuf_inputintr(pas_audiodev);
 413                                break;
 414
 415                        default:
 416                                printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
 417                }
 418        }
 419}
 420