📄 sscape.c
字号:
/* * sound/sscape.c * * Low level driver for Ensoniq Soundscape * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */#include <i386/isa/sound/sound_config.h>#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_SSCAPE)#include <i386/isa/sound/coproc.h>/* * I/O ports */#define MIDI_DATA 0#define MIDI_CTRL 1#define HOST_CTRL 2#define TX_READY 0x02#define RX_READY 0x01#define HOST_DATA 3#define ODIE_ADDR 4#define ODIE_DATA 5/* * Indirect registers */#define GA_INTSTAT_REG 0#define GA_INTENA_REG 1#define GA_DMAA_REG 2#define GA_DMAB_REG 3#define GA_INTCFG_REG 4#define GA_DMACFG_REG 5#define GA_CDCFG_REG 6#define GA_SMCFGA_REG 7#define GA_SMCFGB_REG 8#define GA_HMCTL_REG 9/* * DMA channel identifiers (A and B) */#define SSCAPE_DMA_A 0#define SSCAPE_DMA_B 1#define PORT(name) (devc->base+name)/* * Host commands recognized by the OBP microcode */#define CMD_GEN_HOST_ACK 0x80#define CMD_GEN_MPU_ACK 0x81#define CMD_GET_BOARD_TYPE 0x82#define CMD_SET_CONTROL 0x88#define CMD_GET_CONTROL 0x89#define CMD_SET_MT32 0x96#define CMD_GET_MT32 0x97#define CMD_SET_EXTMIDI 0x9b#define CMD_GET_EXTMIDI 0x9c#define CMD_ACK 0x80typedef struct sscape_info { int base, irq, dma; int ok; /* Properly detected */ int dma_allocated; int my_audiodev; int opened; }sscape_info;static struct sscape_info dev_info ={0};static struct sscape_info *devc = &dev_info;DEFINE_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag);#ifdef REVEAL_SPEA/* Spea and Reveal have assigned interrupt bits differently than Ensoniq */static char valid_interrupts[] ={9, 7, 5, 15};#elsestatic char valid_interrupts[] ={9, 5, 7, 10};#endifstatic unsigned charsscape_read (struct sscape_info *devc, int reg){ unsigned long flags; unsigned char val; DISABLE_INTR (flags); OUTB (reg, PORT (ODIE_ADDR)); val = INB (PORT (ODIE_DATA)); RESTORE_INTR (flags); return val;}static voidsscape_write (struct sscape_info *devc, int reg, int data){ unsigned long flags; DISABLE_INTR (flags); OUTB (reg, PORT (ODIE_ADDR)); OUTB (data, PORT (ODIE_DATA)); RESTORE_INTR (flags);}static voidhost_open (struct sscape_info *devc){ OUTB (0x00, PORT (HOST_CTRL)); /* Put the board to the host mode */}static voidhost_close (struct sscape_info *devc){ OUTB (0x03, PORT (HOST_CTRL)); /* Put the board to the MIDI mode */}static inthost_write (struct sscape_info *devc, unsigned char *data, int count){ unsigned long flags; int i, timeout; DISABLE_INTR (flags); /* * Send the command and data bytes */ for (i = 0; i < count; i++) { for (timeout = 10000; timeout > 0; timeout--) if (INB (PORT (HOST_CTRL)) & TX_READY) break; if (timeout <= 0) { RESTORE_INTR (flags); return 0; } OUTB (data[i], PORT (HOST_DATA)); } RESTORE_INTR (flags); return 1;}static inthost_read (struct sscape_info *devc){ unsigned long flags; int timeout; unsigned char data; DISABLE_INTR (flags); /* * Read a byte */ for (timeout = 10000; timeout > 0; timeout--) if (INB (PORT (HOST_CTRL)) & RX_READY) break; if (timeout <= 0) { RESTORE_INTR (flags); return -1; } data = INB (PORT (HOST_DATA)); RESTORE_INTR (flags); return data;}static inthost_command1 (struct sscape_info *devc, int cmd){ unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); return host_write (devc, buf, 1);}static inthost_command2 (struct sscape_info *devc, int cmd, int parm1){ unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); buf[1] = (unsigned char) (parm1 & 0xff); return host_write (devc, buf, 2);}static inthost_command3 (struct sscape_info *devc, int cmd, int parm1, int parm2){ unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); buf[1] = (unsigned char) (parm1 & 0xff); buf[2] = (unsigned char) (parm2 & 0xff); return host_write (devc, buf, 3);}static voidset_mt32 (struct sscape_info *devc, int value){ host_open (devc); host_command2 (devc, CMD_SET_MT32, value ? 1 : 0); if (host_read (devc) != CMD_ACK) { printk ("SNDSCAPE: Setting MT32 mode failed\n"); } host_close (devc);}static intget_board_type (struct sscape_info *devc){ int tmp; host_open (devc); if (!host_command1 (devc, CMD_GET_BOARD_TYPE)) tmp = -1; else tmp = host_read (devc); host_close (devc); return tmp;}voidsscapeintr (INT_HANDLER_PARMS (irq, dummy)){ unsigned char bits, tmp; static int debug = 0; printk ("sscapeintr(0x%02x)\n", (bits = sscape_read (devc, GA_INTSTAT_REG))); if (SOMEONE_WAITING (sscape_sleeper, sscape_sleep_flag)) { WAKE_UP (sscape_sleeper, sscape_sleep_flag); } if (bits & 0x02) /* Host interface interrupt */ { printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc)); }#if (!defined(EXCLUDE_MPU401) || !defined(EXCLUDE_MPU_EMU)) && !defined(EXCLUDE_MIDI) if (bits & 0x01) { mpuintr (INT_HANDLER_CALL (irq)); if (debug++ > 10) /* Temporary debugging hack */ { sscape_write (devc, GA_INTENA_REG, 0x00); /* Disable all interrupts */ } }#endif /* * Acknowledge interrupts (toggle the interrupt bits) */ tmp = sscape_read (devc, GA_INTENA_REG); sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1));}static voidsscape_enable_intr (struct sscape_info *devc, unsigned intr_bits){ unsigned char temp, orig; temp = orig = sscape_read (devc, GA_INTENA_REG); temp |= intr_bits; temp |= 0x80; /* Master IRQ enable */ if (temp == orig) return; /* No change */ sscape_write (devc, GA_INTENA_REG, temp);}static voidsscape_disable_intr (struct sscape_info *devc, unsigned intr_bits){ unsigned char temp, orig; temp = orig = sscape_read (devc, GA_INTENA_REG); temp &= ~intr_bits; if ((temp & ~0x80) == 0x00) temp = 0x00; /* Master IRQ disable */ if (temp == orig) return; /* No change */ sscape_write (devc, GA_INTENA_REG, temp);}static voiddo_dma (struct sscape_info *devc, int dma_chan, unsigned long buf, int blk_size, int mode){ unsigned char temp; if (dma_chan != SSCAPE_DMA_A) { printk ("SSCAPE: Tried to use DMA channel != A. Why?\n"); return; } DMAbuf_start_dma (devc->my_audiodev, buf, blk_size, mode); temp = devc->dma << 4; /* Setup DMA channel select bits */ if (devc->dma <= 3) temp |= 0x80; /* 8 bit DMA channel */ temp |= 1; /* Trigger DMA */ sscape_write (devc, GA_DMAA_REG, temp); temp &= 0xfe; /* Clear DMA trigger */ sscape_write (devc, GA_DMAA_REG, temp);}static intverify_mpu (struct sscape_info *devc){ /* * The SoundScape board could be in three modes (MPU, 8250 and host). * If the card is not in the MPU mode, enabling the MPU driver will * cause infinite loop (the driver believes that there is always some * received data in the buffer. * * Detect this by looking if there are more than 10 received MIDI bytes * (0x00) in the buffer. */ int i; for (i = 0; i < 10; i++) { if (INB (devc->base + HOST_CTRL) & 0x80) return 1; if (INB (devc->base) != 0x00) return 1; } printk ("SoundScape: The device is not in the MPU-401 mode\n"); return 0;}static intsscape_coproc_open (void *dev_info, int sub_device){ if (sub_device == COPR_MIDI) { set_mt32 (devc, 0); if (!verify_mpu (devc)) return RET_ERROR (EIO); } return 0;}static voidsscape_coproc_close (void *dev_info, int sub_device){ struct sscape_info *devc = dev_info; unsigned long flags; DISABLE_INTR (flags); if (devc->dma_allocated) { sscape_write (devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */#ifndef EXCLUDE_NATIVE_PCM DMAbuf_close_dma (devc->my_audiodev);#endif devc->dma_allocated = 0; } RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag); RESTORE_INTR (flags); return;}static voidsscape_coproc_reset (void *dev_info){}static intsscape_download_boot (struct sscape_info *devc, unsigned char *block, int size, int flag){ unsigned long flags; unsigned char temp; int done, timeout; if (flag & CPF_FIRST) { /* * First block. Have to allocate DMA and to reset the board * before continuing. */ DISABLE_INTR (flags); if (devc->dma_allocated == 0) {#ifndef EXCLUDE_NATIVE_PCM if (DMAbuf_open_dma (devc->my_audiodev) < 0) { RESTORE_INTR (flags); return 0; }#endif devc->dma_allocated = 1; } RESTORE_INTR (flags); sscape_write (devc, GA_HMCTL_REG, (temp = sscape_read (devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ for (timeout = 10000; timeout > 0; timeout--) sscape_read (devc, GA_HMCTL_REG); /* Delay */ /* Take board out of reset */ sscape_write (devc, GA_HMCTL_REG, (temp = sscape_read (devc, GA_HMCTL_REG)) | 0x80); } /* * Transfer one code block using DMA */ memcpy (audio_devs[devc->my_audiodev]->dmap->raw_buf[0], block, size); DISABLE_INTR (flags);/******** INTERRUPTS DISABLED NOW ********/ do_dma (devc, SSCAPE_DMA_A, audio_devs[devc->my_audiodev]->dmap->raw_buf_phys[0], size, DMA_MODE_WRITE); /* * Wait until transfer completes. */ RESET_WAIT_QUEUE (sscape_sleeper, sscape_sleep_flag); done = 0; timeout = 100; while (!done && timeout-- > 0) { int resid; DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); clear_dma_ff (devc->dma); if ((resid = get_dma_residue (devc->dma)) == 0) done = 1; } RESTORE_INTR (flags); if (!done) return 0; if (flag & CPF_LAST) { /* * Take the board out of reset */ OUTB (0x00, PORT (HOST_CTRL)); OUTB (0x00, PORT (MIDI_CTRL)); temp = sscape_read (devc, GA_HMCTL_REG); temp |= 0x40; sscape_write (devc, GA_HMCTL_REG, temp); /* Kickstart the board */ /* * Wait until the ODB wakes up */ DISABLE_INTR (flags); done = 0; timeout = 5 * HZ; while (!done && timeout-- > 0) { DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); if (INB (PORT (HOST_DATA)) == 0xff) /* OBP startup acknowledge */ done = 1; } RESTORE_INTR (flags); if (!done) { printk ("SoundScape: The OBP didn't respond after code download\n"); return 0; } DISABLE_INTR (flags); done = 0; timeout = 5 * HZ; while (!done && timeout-- > 0) { DO_SLEEP (sscape_sleeper, sscape_sleep_flag, 1); if (INB (PORT (HOST_DATA)) == 0xfe) /* Host startup acknowledge */ done = 1; } RESTORE_INTR (flags); if (!done) { printk ("SoundScape: OBP Initialization failed.\n"); return 0; } printk ("SoundScape board of type %d initialized OK\n", get_board_type (devc));#ifdef SSCAPE_DEBUG3 /* * Temporary debugging aid. Print contents of the registers after * downloading the code. */ { int i; for (i = 0; i < 13; i++) printk ("I%d = %02x (new value)\n", i, sscape_read (devc, i));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -