URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [sound/] [pas2_pcm.c] - Rev 1765
Compare with Previous | Blame | View Log
#define _PAS2_PCM_C_ /* * sound/pas2_pcm.c * * The low level driver for the Pro Audio Spectrum ADC/DAC. */ #include <linux/config.h> #include "sound_config.h" #if defined(CONFIG_PAS) && defined(CONFIG_AUDIO) #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 = 0; /* 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 = 0; /* filter FLAG */ static unsigned char pcm_mode = PCM_NON; static unsigned long pcm_count = 0; static unsigned short pcm_bitsok = 8; /* mask of OK bits */ static int pcm_busy = 0; int pas_audiodev = 0; static int open_mode = 0; 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 = (596590 + (arg / 2)) / arg; arg = 596590 / foo; } else { foo = (1193180 + (arg / 2)) / arg; arg = 1193180 / 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 save_flags (flags); cli (); 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); restore_flags (flags); return pcm_speed; } 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; } 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, caddr_t arg, int local) { 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 (local) return pcm_set_speed ((int) arg); return snd_ioctl_return ((int *) arg, pcm_set_speed (get_user ((int *) arg))); break; case SOUND_PCM_READ_RATE: if (local) return pcm_speed; return snd_ioctl_return ((int *) arg, pcm_speed); break; case SNDCTL_DSP_STEREO: if (local) return pcm_set_channels ((int) arg + 1) - 1; return snd_ioctl_return ((int *) arg, pcm_set_channels (get_user ((int *) arg) + 1) - 1); break; case SOUND_PCM_WRITE_CHANNELS: if (local) return pcm_set_channels ((int) arg); return snd_ioctl_return ((int *) arg, pcm_set_channels (get_user ((int *) arg))); break; case SOUND_PCM_READ_CHANNELS: if (local) return pcm_channels; return snd_ioctl_return ((int *) arg, pcm_channels); break; case SNDCTL_DSP_SETFMT: if (local) return pcm_set_bits ((int) arg); return snd_ioctl_return ((int *) arg, pcm_set_bits (get_user ((int *) arg))); break; case SOUND_PCM_READ_BITS: if (local) return pcm_bits; return snd_ioctl_return ((int *) arg, pcm_bits); case SOUND_PCM_WRITE_FILTER: /* * NOT YET IMPLEMENTED */ if (get_user ((int *) arg) > 1) return -(EINVAL); pcm_filter = get_user ((int *) arg); break; case SOUND_PCM_READ_FILTER: return snd_ioctl_return ((int *) arg, pcm_filter); break; default: return -(EINVAL); } return -(EINVAL); } 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); } 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)); save_flags (flags); cli (); if (pcm_busy) { restore_flags (flags); return -(EBUSY); } pcm_busy = 1; restore_flags (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")); save_flags (flags); cli (); pas_audio_reset (dev); pas_remove_intr (PAS_PCM_INTRBITS); pcm_mode = PCM_NON; pcm_busy = 0; restore_flags (flags); } static void pas_audio_output_block (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { 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]->dmachan1 > 3) cnt >>= 1; if (audio_devs[dev]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ save_flags (flags); cli (); pas_write (pas_read (0xF8A) & ~0x40, 0xF8A); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); if (audio_devs[dev]->dmachan1 > 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; restore_flags (flags); } static void pas_audio_start_input (int dev, unsigned long buf, int count, int intrflag, int restart_dma) { 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]->dmachan1 > 3) cnt >>= 1; if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && intrflag && cnt == pcm_count) return; /* * Auto mode on. No need to react */ save_flags (flags); cli (); if (restart_dma) DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); if (audio_devs[dev]->dmachan1 > 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; restore_flags (flags); } #ifndef NO_TRIGGER static void pas_audio_trigger (int dev, int state) { unsigned long flags; save_flags (flags); cli (); 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); restore_flags (flags); } #endif static int pas_audio_prepare_for_input (int dev, int bsize, int bcount) { return 0; } static int pas_audio_prepare_for_output (int dev, int bsize, int bcount) { return 0; } static struct audio_driver pas_audio_driver = { pas_audio_open, pas_audio_close, pas_audio_output_block, pas_audio_start_input, pas_audio_ioctl, pas_audio_prepare_for_input, pas_audio_prepare_for_output, pas_audio_reset, pas_audio_reset, NULL, NULL, NULL, NULL, pas_audio_trigger }; static struct audio_operations pas_audio_operations = { "Pro Audio Spectrum", DMA_AUTOMODE, AFMT_U8 | AFMT_S16_LE, NULL, &pas_audio_driver }; void 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 (num_audiodevs < MAX_AUDIO_DEV) { audio_devs[pas_audiodev = num_audiodevs++] = &pas_audio_operations; audio_devs[pas_audiodev]->dmachan1 = hw_config->dma; audio_devs[pas_audiodev]->buffsize = DSP_BUFFSIZE; } else printk ("PAS2: Too many PCM devices available\n"); } void pas_pcm_interrupt (unsigned char status, int cause) { if (cause == 1) /* * PCM buffer done */ { /* * 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 ("PAS: Unexpected PCM interrupt\n"); } } } #endif