📄 sscape.c
字号:
/* * sound/sscape.c * * Low level driver for Ensoniq SoundScape * * * 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) * Sergey Smitienko : ensoniq p'n'p support * Christoph Hellwig : adapted to module_init/module_exit * Bartlomiej Zolnierkiewicz : added __init to attach_sscape() * Chris Rankin : Specify that this module owns the coprocessor * Arnaldo C. de Melo : added missing restore_flags in sscape_pnp_upload_file */#include <linux/init.h>#include <linux/module.h>#include "sound_config.h"#include "sound_firmware.h"#include <linux/types.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/ctype.h>#include <linux/stddef.h>#include <linux/kmod.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/segment.h>#include <linux/wait.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/wrapper.h>#include "coproc.h"#include "ad1848.h"#include "mpu401.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 /* Old firmware only */#define CMD_GET_CONTROL 0x89 /* Old firmware only */#define CTL_MASTER_VOL 0#define CTL_MIC_MODE 2#define CTL_SYNTH_VOL 4#define CTL_WAVE_VOL 7#define CMD_SET_EXTMIDI 0x8a#define CMD_GET_EXTMIDI 0x8b#define CMD_SET_MT32 0x8c#define CMD_GET_MT32 0x8d#define CMD_ACK 0x80#define IC_ODIE 1#define IC_OPUS 2typedef struct sscape_info{ int base, irq, dma; int codec, codec_irq; /* required to setup pnp cards*/ int codec_type; int ic_type; char* raw_buf; unsigned long raw_buf_phys; int buffsize; /* -------------------------- */ int ok; /* Properly detected */ int failed; int dma_allocated; int codec_audiodev; int opened; int *osp; int my_audiodev;} sscape_info;static struct sscape_info adev_info = { 0};static struct sscape_info *devc = &adev_info;static int sscape_mididev = -1;/* Some older cards have assigned interrupt bits differently than new ones */static char valid_interrupts_old[] = { 9, 7, 5, 15};static char valid_interrupts_new[] = { 9, 5, 7, 10};static char *valid_interrupts = valid_interrupts_new;/* * See the bottom of the driver. This can be set by spea =0/1. */ #ifdef REVEAL_SPEAstatic char old_hardware = 1;#elsestatic char old_hardware = 0;#endifstatic void sleep(unsigned howlong){ current->state = TASK_INTERRUPTIBLE; schedule_timeout(howlong);}static unsigned char sscape_read(struct sscape_info *devc, int reg){ unsigned long flags; unsigned char val; save_flags(flags); cli(); outb(reg, PORT(ODIE_ADDR)); val = inb(PORT(ODIE_DATA)); restore_flags(flags); return val;}static void sscape_write(struct sscape_info *devc, int reg, int data){ unsigned long flags; save_flags(flags); cli(); outb(reg, PORT(ODIE_ADDR)); outb(data, PORT(ODIE_DATA)); restore_flags(flags);}static unsigned char sscape_pnp_read_codec(sscape_info* devc, unsigned char reg){ unsigned char res; unsigned long flags; save_flags(flags); cli(); outb( reg, devc -> codec); res = inb (devc -> codec + 1); restore_flags(flags); return res;}static void sscape_pnp_write_codec(sscape_info* devc, unsigned char reg, unsigned char data){ unsigned long flags; save_flags(flags); cli(); outb( reg, devc -> codec); outb( data, devc -> codec + 1); restore_flags(flags);}static void host_open(struct sscape_info *devc){ outb((0x00), PORT(HOST_CTRL)); /* Put the board to the host mode */}static void host_close(struct sscape_info *devc){ outb((0x03), PORT(HOST_CTRL)); /* Put the board to the MIDI mode */}static int host_write(struct sscape_info *devc, unsigned char *data, int count){ unsigned long flags; int i, timeout_val; save_flags(flags); cli(); /* * Send the command and data bytes */ for (i = 0; i < count; i++) { for (timeout_val = 10000; timeout_val > 0; timeout_val--) if (inb(PORT(HOST_CTRL)) & TX_READY) break; if (timeout_val <= 0) { restore_flags(flags); return 0; } outb(data[i], PORT(HOST_DATA)); } restore_flags(flags); return 1;}static int host_read(struct sscape_info *devc){ unsigned long flags; int timeout_val; unsigned char data; save_flags(flags); cli(); /* * Read a byte */ for (timeout_val = 10000; timeout_val > 0; timeout_val--) if (inb(PORT(HOST_CTRL)) & RX_READY) break; if (timeout_val <= 0) { restore_flags(flags); return -1; } data = inb(PORT(HOST_DATA)); restore_flags(flags); return data;}#if 0 /* unused */static int host_command1(struct sscape_info *devc, int cmd){ unsigned char buf[10]; buf[0] = (unsigned char) (cmd & 0xff); return host_write(devc, buf, 1);}#endif /* unused */static int host_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 int host_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 void set_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 void set_control(struct sscape_info *devc, int ctrl, int value){ host_open(devc); host_command3(devc, CMD_SET_CONTROL, ctrl, value); if (host_read(devc) != CMD_ACK) { /* printk( "SNDSCAPE: Setting control (%d) failed\n", ctrl); */ } host_close(devc);}static void do_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(KERN_WARNING "soundscape: Tried to use DMA channel != A. Why?\n"); return; } audio_devs[devc->codec_audiodev]->flags &= ~DMA_AUTOMODE; DMAbuf_start_dma(devc->codec_audiodev, buf, blk_size, mode); audio_devs[devc->codec_audiodev]->flags |= DMA_AUTOMODE; 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 int verify_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(KERN_WARNING "SoundScape: The device is not in the MPU-401 mode\n"); return 0;}static int sscape_coproc_open(void *dev_info, int sub_device){ if (sub_device == COPR_MIDI) { set_mt32(devc, 0); if (!verify_mpu(devc)) return -EIO; } return 0;}static void sscape_coproc_close(void *dev_info, int sub_device){ struct sscape_info *devc = dev_info; unsigned long flags; save_flags(flags); cli(); if (devc->dma_allocated) { sscape_write(devc, GA_DMAA_REG, 0x20); /* DMA channel disabled */ devc->dma_allocated = 0; } restore_flags(flags); return;}static void sscape_coproc_reset(void *dev_info){}static int sscape_download_boot(struct sscape_info *devc, unsigned char *block, int size, int flag){ unsigned long flags; unsigned char temp; volatile int done, timeout_val; static unsigned char codec_dma_bits = 0; if (flag & CPF_FIRST) { /* * First block. Have to allocate DMA and to reset the board * before continuing. */ save_flags(flags); cli(); codec_dma_bits = sscape_read(devc, GA_CDCFG_REG); if (devc->dma_allocated == 0) devc->dma_allocated = 1; restore_flags(flags); sscape_write(devc, GA_HMCTL_REG, (temp = sscape_read(devc, GA_HMCTL_REG)) & 0x3f); /*Reset */ for (timeout_val = 10000; timeout_val > 0; timeout_val--) 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 */ if (audio_devs[devc->codec_audiodev]->dmap_out->raw_buf == NULL) { printk(KERN_WARNING "soundscape: DMA buffer not available\n"); return 0; } memcpy(audio_devs[devc->codec_audiodev]->dmap_out->raw_buf, block, size); save_flags(flags); cli(); /******** INTERRUPTS DISABLED NOW ********/ do_dma(devc, SSCAPE_DMA_A, audio_devs[devc->codec_audiodev]->dmap_out->raw_buf_phys, size, DMA_MODE_WRITE); /* * Wait until transfer completes. */ done = 0; timeout_val = 30; while (!done && timeout_val-- > 0) { int resid; if (HZ / 50) sleep(HZ / 50); clear_dma_ff(devc->dma); if ((resid = get_dma_residue(devc->dma)) == 0) done = 1; } restore_flags(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 */ save_flags(flags); cli(); done = 0; timeout_val = 5 * HZ; while (!done && timeout_val-- > 0) { unsigned char x; sleep(1); x = inb(PORT(HOST_DATA)); if (x == 0xff || x == 0xfe) /* OBP startup acknowledge */ { DDB(printk("Soundscape: Acknowledge = %x\n", x));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -