[funini.com] -> [kei@sodan] -> Kernel Reading

root/sound/oss/pas2_pcm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcm_set_speed
  2. pcm_set_channels
  3. pcm_set_bits
  4. pas_audio_ioctl
  5. pas_audio_reset
  6. pas_audio_open
  7. pas_audio_close
  8. pas_audio_output_block
  9. pas_audio_start_input
  10. pas_audio_trigger
  11. pas_audio_prepare_for_input
  12. pas_audio_prepare_for_output
  13. pas_pcm_init
  14. pas_pcm_interrupt

/*
 * pas2_pcm.c Audio routines for PAS16
 *
 *
 * Copyright (C) by Hannu Savolainen 1993-1997
 *
 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
 * Version 2 (June 1991). See the "COPYING" file distributed with this software
 * for more info.
 *
 *
 * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
 * Alan Cox        : Swatted a double allocation of device bug. Made a few
 *                   more things module options.
 * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
 */

#include <linux/init.h>
#include <linux/spinlock.h>
#include <asm/timex.h>
#include "sound_config.h"

#include "pas2.h"

#ifndef DEB
#define DEB(WHAT)
#endif

#define PAS_PCM_INTRBITS (0x08)
/*
 * Sample buffer timer interrupt enable
 */

#define PCM_NON 0
#define PCM_DAC 1
#define PCM_ADC 2

static unsigned long pcm_speed;         /* sampling rate */
static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
static unsigned char pcm_filter;        /* filter FLAG */
static unsigned char pcm_mode = PCM_NON;
static unsigned long pcm_count;
static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
static int      pcm_busy;
int             pas_audiodev = -1;
static int      open_mode;

extern spinlock_t pas_lock;

static int pcm_set_speed(int arg)
{
        int foo, tmp;
        unsigned long flags;

        if (arg == 0)
                return pcm_speed;

        if (arg > 44100)
                arg = 44100;
        if (arg < 5000)
                arg = 5000;

        if (pcm_channels & 2)
        {
                foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
                arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
        }
        else
        {
                foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
                arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
        }

        pcm_speed = arg;

        tmp = pas_read(0x0B8A);

        /*
         * Set anti-aliasing filters according to sample rate. You really *NEED*
         * to enable this feature for all normal recording unless you want to
         * experiment with aliasing effects.
         * These filters apply to the selected "recording" source.
         * I (pfw) don't know the encoding of these 5 bits. The values shown
         * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
         *
         * I cleared bit 5 of these values, since that bit controls the master
         * mute flag. (Olav Wölfelschneider)
         *
         */
#if !defined NO_AUTO_FILTER_SET
        tmp &= 0xe0;
        if (pcm_speed >= 2 * 17897)
                tmp |= 0x01;
        else if (pcm_speed >= 2 * 15909)
                tmp |= 0x02;
        else if (pcm_speed >= 2 * 11931)
                tmp |= 0x09;
        else if (pcm_speed >= 2 * 8948)
                tmp |= 0x11;
        else if (pcm_speed >= 2 * 5965)
                tmp |= 0x19;
        else if (pcm_speed >= 2 * 2982)
                tmp |= 0x04;
        pcm_filter = tmp;
#endif

        spin_lock_irqsave(&pas_lock, flags);

        pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
        pas_write(0x00 | 0x30 | 0x04, 0x138B);
        pas_write(foo & 0xff, 0x1388);
        pas_write((foo >> 8) & 0xff, 0x1388);
        pas_write(tmp, 0x0B8A);

        spin_unlock_irqrestore(&pas_lock, flags);

        return pcm_speed;
}

static int pcm_set_channels(int arg)
{

        if ((arg != 1) && (arg != 2))
                return pcm_channels;

        if (arg != pcm_channels)
        {
                pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);

                pcm_channels = arg;
                pcm_set_speed(pcm_speed);       /* The speed must be reinitialized */
        }
        return pcm_channels;
}

static int pcm_set_bits(int arg)
{
        if (arg == 0)
                return pcm_bits;

        if ((arg & pcm_bitsok) != arg)
                return pcm_bits;

        if (arg != pcm_bits)
        {
                pas_write(pas_read(0x8389) ^ 0x04, 0x8389);

                pcm_bits = arg;
        }
        return pcm_bits;
}

static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
{
        int val, ret;
        int __user *p = arg;

        DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));

        switch (cmd) 
        {
        case SOUND_PCM_WRITE_RATE:
                if (get_user(val, p)) 
                        return -EFAULT;
                ret = pcm_set_speed(val);
                break;

        case SOUND_PCM_READ_RATE:
                ret = pcm_speed;
                break;
                
        case SNDCTL_DSP_STEREO:
                if (get_user(val, p)) 
                        return -EFAULT;
                ret = pcm_set_channels(val + 1) - 1;
                break;

        case SOUND_PCM_WRITE_CHANNELS:
                if (get_user(val, p)) 
                        return -EFAULT;
                ret = pcm_set_channels(val);
                break;

        case SOUND_PCM_READ_CHANNELS:
                ret = pcm_channels;
                break;

        case SNDCTL_DSP_SETFMT:
                if (get_user(val, p))
                        return -EFAULT;
                ret = pcm_set_bits(val);
                break;
                
        case SOUND_PCM_READ_BITS:
                ret = pcm_bits;
                break;
  
        default:
                return -EINVAL;
        }
        return put_user(ret, p);
}

static void pas_audio_reset(int dev)
{
        DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));

        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);      /* Disable PCM */
}

static int pas_audio_open(int dev, int mode)
{
        int             err;
        unsigned long   flags;

        DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));

        spin_lock_irqsave(&pas_lock, flags);
        if (pcm_busy)
        {
                spin_unlock_irqrestore(&pas_lock, flags);
                return -EBUSY;
        }
        pcm_busy = 1;
        spin_unlock_irqrestore(&pas_lock, flags);

        if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
                return err;


        pcm_count = 0;
        open_mode = mode;

        return 0;
}

static void pas_audio_close(int dev)
{
        unsigned long   flags;

        DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));

        spin_lock_irqsave(&pas_lock, flags);

        pas_audio_reset(dev);
        pas_remove_intr(PAS_PCM_INTRBITS);
        pcm_mode = PCM_NON;

        pcm_busy = 0;
        spin_unlock_irqrestore(&pas_lock, flags);
}

static void pas_audio_output_block(int dev, unsigned long buf, int count,
                       int intrflag)
{
        unsigned long   flags, cnt;

        DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));

        cnt = count;
        if (audio_devs[dev]->dmap_out->dma > 3)
                cnt >>= 1;

        if (audio_devs[dev]->flags & DMA_AUTOMODE &&
            intrflag &&
            cnt == pcm_count)
                return;

        spin_lock_irqsave(&pas_lock, flags);

        pas_write(pas_read(0xF8A) & ~0x40,
                  0xF8A);

        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */

        if (audio_devs[dev]->dmap_out->dma > 3)
                count >>= 1;

        if (count != pcm_count)
        {
                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
                pas_write(0x40 | 0x30 | 0x04, 0x138B);
                pas_write(count & 0xff, 0x1389);
                pas_write((count >> 8) & 0xff, 0x1389);
                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);

                pcm_count = count;
        }
        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
        pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
#endif

        pcm_mode = PCM_DAC;

        spin_unlock_irqrestore(&pas_lock, flags);
}

static void pas_audio_start_input(int dev, unsigned long buf, int count,
                      int intrflag)
{
        unsigned long   flags;
        int             cnt;

        DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));

        cnt = count;
        if (audio_devs[dev]->dmap_out->dma > 3)
                cnt >>= 1;

        if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
            intrflag &&
            cnt == pcm_count)
                return;

        spin_lock_irqsave(&pas_lock, flags);

        /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */

        if (audio_devs[dev]->dmap_out->dma > 3)
                count >>= 1;

        if (count != pcm_count)
        {
                pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
                pas_write(0x40 | 0x30 | 0x04, 0x138B);
                pas_write(count & 0xff, 0x1389);
                pas_write((count >> 8) & 0xff, 0x1389);
                pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);

                pcm_count = count;
        }
        pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
#ifdef NO_TRIGGER
        pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
#endif

        pcm_mode = PCM_ADC;

        spin_unlock_irqrestore(&pas_lock, flags);
}

#ifndef NO_TRIGGER
static void pas_audio_trigger(int dev, int state)
{
        unsigned long   flags;

        spin_lock_irqsave(&pas_lock, flags);
        state &= open_mode;

        if (state & PCM_ENABLE_OUTPUT)
                pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
        else if (state & PCM_ENABLE_INPUT)
                pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
        else
                pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);

        spin_unlock_irqrestore(&pas_lock, flags);
}
#endif

static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
{
        pas_audio_reset(dev);
        return 0;
}

static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
{
        pas_audio_reset(dev);
        return 0;
}

static struct audio_driver pas_audio_driver =
{
        .owner                  = THIS_MODULE,
        .open                   = pas_audio_open,
        .close                  = pas_audio_close,
        .output_block           = pas_audio_output_block,
        .start_input            = pas_audio_start_input,
        .ioctl                  = pas_audio_ioctl,
        .prepare_for_input      = pas_audio_prepare_for_input,
        .prepare_for_output     = pas_audio_prepare_for_output,
        .halt_io                = pas_audio_reset,
        .trigger                = pas_audio_trigger
};

void __init pas_pcm_init(struct address_info *hw_config)
{
        DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));

        pcm_bitsok = 8;
        if (pas_read(0xEF8B) & 0x08)
                pcm_bitsok |= 16;

        pcm_set_speed(DSP_DEFAULT_SPEED);

        if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
                                        "Pro Audio Spectrum",
                                        &pas_audio_driver,
                                        sizeof(struct audio_driver),
                                        DMA_AUTOMODE,
                                        AFMT_U8 | AFMT_S16_LE,
                                        NULL,
                                        hw_config->dma,
                                        hw_config->dma)) < 0)
                printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
}

void pas_pcm_interrupt(unsigned char status, int cause)
{
        if (cause == 1)
        {
                /*
                 * Halt the PCM first. Otherwise we don't have time to start a new
                 * block before the PCM chip proceeds to the next sample
                 */

                if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
                        pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);

                switch (pcm_mode)
                {
                        case PCM_DAC:
                                DMAbuf_outputintr(pas_audiodev, 1);
                                break;

                        case PCM_ADC:
                                DMAbuf_inputintr(pas_audiodev);
                                break;

                        default:
                                printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
                }
        }
}

/* [<][>][^][v][top][bottom][index][help] */

[funini.com] -> [kei@sodan] -> Kernel Reading