123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- * 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 <linux/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");
- }
- }
- }
|