📄 ad1848.c
字号:
/* * sound/ad1848.c * * Driver for Microsoft Sound System/Windows Sound System (mss) * -compatible boards. This includes: * * AD1848, CS4248, CS423x, OPTi931, Yamaha OPL/SAx and many others. * * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 * * 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. * * Full data sheets in PDF format for the MSS-compatible chips * are available at * * http://www.crystal.com/ for the CS42XX series, or * http://www.opti.com/ for the OPTi931 */#include <i386/isa/snd/sound.h>#if NPCM > 0/* * board-specific include files */#include <i386/isa/snd/mss.h>/* * prototypes for procedures exported in the device descriptor */static int mss_probe(struct isa_device *dev);static int mss_attach(struct isa_device *dev);static d_open_t mss_open;static d_close_t mss_close;static d_ioctl_t mss_ioctl;static irq_proc_t mss_intr;static irq_proc_t opti931_intr;static snd_callback_t mss_callback; /* * prototypes for local functions */static void mss_reinit(snddev_info *d);static int AD_WAIT_INIT(snddev_info *d, int x);static int mss_mixer_set(snddev_info *d, int dev, int value);static int mss_set_recsrc(snddev_info *d, int mask);static void ad1848_mixer_reset(snddev_info *d);static void opti_write(int io_base, u_char reg, u_char data);static u_char opti_read(int io_base, u_char reg);static void ad_write(snddev_info *d, int reg, u_char data);static void ad_write_cnt(snddev_info *d, int reg, u_short data);static int ad_read(snddev_info *d, int reg);/* * device descriptors for the boards supported by this module. */snddev_info mss_op_desc = { "mss", SNDCARD_MSS, mss_probe, mss_attach, mss_open, mss_close, NULL /* mss_read */, NULL /* mss_write */, mss_ioctl, sndselect /* mss_select */, mss_intr, mss_callback , DSP_BUFFSIZE, /* bufsize */ AFMT_STEREO | AFMT_U8 | AFMT_S16_LE | AFMT_MU_LAW | AFMT_A_LAW, /* audio formats */ /* * the enhanced boards also have AFMT_IMA_ADPCM | AFMT_S16_BE * but we do not use these modes. */} ;/* * mss_probe() is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * * The base address supplied in the device refers to the old MSS * specs where the four 4 registers in io space contain configuration * information. Some boards (as an example, early MSS boards) * has such a block of registers, whereas others (generally CS42xx) * do not. In order to distinguish between the two and do not have * to supply two separate probe routines, the flags entry in isa_device * has a bit to mark this. * */static intmss_probe(struct isa_device *dev){ u_char tmp; int irq = ffs(dev->id_irq) - 1; bzero(&pcm_info[dev->id_unit], sizeof(pcm_info[dev->id_unit]) ); if (dev->id_iobase == -1) { dev->id_iobase = 0x530; BVDDB(printf("mss_probe: no address supplied, try default 0x%x\n", dev->id_iobase)); } if (snd_conflict(dev->id_iobase)) return 0 ; if ( !(dev->id_flags & DV_F_TRUE_MSS) ) /* Has no IRQ/DMA registers */ goto mss_probe_end; /* * Check if the IO port returns valid signature. The original MS * Sound system returns 0x04 while some cards * (AudioTriX Pro for example) return 0x00 or 0x0f. */ tmp = inb(dev->id_iobase + 3); if (tmp == 0xff) { /* Bus float */ BVDDB(printf("I/O address inactive (%x), try pseudo_mss\n", tmp)); dev->id_flags &= ~DV_F_TRUE_MSS ; goto mss_probe_end; } tmp &= 0x3f ; if (tmp != 0x04 && tmp != 0x0f && tmp != 0x00) { BVDDB(printf("No MSS signature detected on port 0x%x (0x%x)\n", dev->id_iobase, inb(dev->id_iobase + 3))); return 0; } if (irq > 11) { printf("MSS: Bad IRQ %d\n", irq); return 0; } if (dev->id_drq != 0 && dev->id_drq != 1 && dev->id_drq != 3) { printf("MSS: Bad DMA %d\n", dev->id_drq); return 0; } if (inb(dev->id_iobase + 3) & 0x80) { /* 8-bit board: only drq1/3 and irq7/9 */ if (dev->id_drq == 0) { printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); return 0; } if (irq != 7 && irq != 9) { printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); return 0; } }mss_probe_end: return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */}/* * the address passed as io_base for mss_attach is also the old * MSS base address (e.g. 0x530). The codec is four locations ahead. * Note that the attach routine for PnP devices might support * device-specific initializations. */static intmss_attach(struct isa_device *dev){ snddev_info *d = &(pcm_info[dev->id_unit]); printf("mss_attach <%s>%d at 0x%x irq %d dma %d:%d flags 0x%x\n", d->name, dev->id_unit, d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags); dev->id_alive = 8 ; /* number of io ports */ /* should be already set but just in case... */ if ( dev->id_flags & DV_F_TRUE_MSS ) { /* has IRQ/DMA registers, set IRQ and DMA addr */ static char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20 }; static char dma_bits[4] = { 1, 2, 0, 3 }; char bits ; if (d->irq == -1 || (bits = interrupt_bits[d->irq]) == -1) { dev->id_irq = 0 ; /* makk invalid irq */ return 0 ; } outb(dev->id_iobase, bits | 0x40); /* config port */ if ((inb(dev->id_iobase + 3) & 0x40) == 0) /* version port */ printf("[IRQ Conflict?]"); /* Write IRQ+DMA setup */ if ( ! FULL_DUPLEX(d) ) /* single chan dma */ outb(dev->id_iobase, bits | dma_bits[d->dbuf_out.chan]); else { if (d->dbuf_out.chan == 0 && d->dbuf_in.chan == 1) bits |= 5 ; else if (d->dbuf_out.chan == 1 && d->dbuf_in.chan == 0) bits |= 6 ; else if (d->dbuf_out.chan == 3 && d->dbuf_in.chan == 0) bits |= 7 ; else { printf("invalid dual dma config %d:%d\n", d->dbuf_out.chan, d->dbuf_in.chan); dev->id_irq = 0 ; dev->id_alive = 0 ; /* this makes attach fail. */ return 0 ; } outb(dev->id_iobase, bits ); } } if (1) { /* machine-specific code for the libretto */ /* * XXX should probably read before writing */ u_char r6, r9; outb( 0x370, 6 /* dma config */ ); outb( 0x371, 0xa9 /* config: DMA-B for rec, DMA-A for play */); r6 = inb( 0x371 /* read */ ); outb( 0x370, 0xa /* version */ ); r9 = inb( 0x371 /* read */ ); DEB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) /* * yamaha - set volume to max */ outb( 0x370, 7 /* volume left */ ); outb( 0x371, 0 /* max level */ ); outb( 0x370, 8 /* volume right */ ); outb( 0x371, 0 /* max level */ ); } if ( FULL_DUPLEX(d) ) d->audio_fmt |= AFMT_FULLDUPLEX ; if (d->bd_id == MD_YM0020) { DDB(printf("setting up yamaha registers\n")); outb(0x370, 6 /* dma config */ ) ; if (FULL_DUPLEX(d)) outb(0x371, 0xa9 ); /* use both dma chans */ else outb(0x371, 0x8b ); /* use low dma chan */ } mss_reinit(d); ad1848_mixer_reset(d); return 0;}static intmss_open(dev_t dev, int flags, int mode, struct proc * p){ int unit; snddev_info *d; u_long s; dev = minor(dev); unit = dev >> 4 ; dev &= 0xf ; d = &pcm_info[unit] ; s = spltty(); /* * This was meant to support up to 2 open descriptors for the * some device, and check proper device usage on open. * Unfortunately, the kernel will trap all close() calls but * the last one, with the consequence that we cannot really * keep track of which channels are busy. * So, the correct tests cannot be done :( and we must rely * on the locks on concurrent operations of the same type and * on some approximate tests... */ if (dev == SND_DEV_AUDIO) d->flags |= SND_F_BUSY_AUDIO ; else if (dev == SND_DEV_DSP) d->flags |= SND_F_BUSY_DSP ; else if (dev == SND_DEV_DSP16) d->flags |= SND_F_BUSY_DSP16 ; if ( d->flags & SND_F_BUSY ) splx(s); /* device was already set, no need to reinit */ else { /* * device was idle. Do the necessary initialization, * but no need keep interrupts blocked. * will not get them */ splx(s); d->play_speed = d->rec_speed = DSP_DEFAULT_SPEED ; d->flags |= SND_F_BUSY ; d->wsel.si_pid = 0; d->wsel.si_flags = 0; d->rsel.si_pid = 0; d->rsel.si_flags = 0; d->dbuf_out.total = d->dbuf_out.prev_total = 0 ; d->dbuf_in.total = d->dbuf_in.prev_total = 0 ; if (flags & O_NONBLOCK) d->flags |= SND_F_NBIO ; switch (dev) { default : case SND_DEV_AUDIO : d->play_fmt = d->rec_fmt = AFMT_MU_LAW ; break ; case SND_DEV_DSP : d->play_fmt = d->rec_fmt = AFMT_U8 ; break ; case SND_DEV_DSP16 : d->play_fmt = d->rec_fmt = AFMT_S16_LE ; break; } ask_init(d); /* and reset buffers... */ } return 0 ;}static intmss_close(dev_t dev, int flags, int mode, struct proc * p){ int unit; snddev_info *d; u_long s; dev = minor(dev); unit = dev >> 4 ; dev &= 0xf; d = &pcm_info[unit] ; /* * We will only get a single close call when the last reference * to the device is gone. But we must handle ourselves references * through different devices. */ s = spltty(); if (dev == SND_DEV_AUDIO) d->flags &= ~SND_F_BUSY_AUDIO ; else if (dev == SND_DEV_DSP) d->flags &= ~SND_F_BUSY_DSP ; else if (dev == SND_DEV_DSP16) d->flags &= ~SND_F_BUSY_DSP16 ; if ( d->flags & SND_F_BUSY_ANY ) /* still some device open */ splx(s); else { /* last one */ d->flags |= SND_F_CLOSING ; splx(s); /* is this ok here ? */ snd_flush(d); outb(io_Status(d), 0); /* Clear interrupt status */ d->flags = 0 ; } return 0 ;}static intmss_ioctl(dev_t dev, int cmd, caddr_t arg, int mode, struct proc * p){ snddev_info *d; int unit; dev = minor(dev); unit = dev >> 4 ; d = &pcm_info[unit] ; /* * handle mixer calls first. Reads are in the default handler, * so do not bother about them. */ if ( (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0) ) { cmd &= 0xff ; if (cmd == SOUND_MIXER_RECSRC) return mss_set_recsrc(d, *(int *)arg) ; else return mss_mixer_set(d, cmd, *(int *)arg) ; } return ENOSYS ; /* fallback to the default ioctl handler */}/* * the callback routine to handle all dma ops etc. * With the exception of INIT, all other callbacks are invoked * with interrupts disabled. */static intmss_callback(snddev_info *d, int reason){ u_char m; int retry, wr, cnt; DEB(printf("-- mss_callback reason 0x%03x\n", reason)); wr = reason & SND_CB_WR ; reason &= SND_CB_REASON_MASK ; switch (reason) { case SND_CB_INIT : /* called with int enabled and no pending I/O */ /* * perform all necessary initializations for i/o */ d->rec_fmt = d->play_fmt ; /* no split format on the MSS */ snd_set_blocksize(d); mss_reinit(d); reset_dbuf(& (d->dbuf_in), SND_CHAN_RD ); reset_dbuf(& (d->dbuf_out), SND_CHAN_WR ); return 1 ; break ; case SND_CB_START : cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl ; if (d->play_fmt == AFMT_S16_LE) cnt /= 2; if (d->flags & SND_F_STEREO) cnt /= 2; cnt-- ; DEB(printf("-- mss_callback, (re)start cnt %d\n", cnt)); m = ad_read(d,9) ; DEB( if (m & 4) printf("OUCH! reg 9 0x%02x\n", m); ); m |= wr ? I9_PEN : I9_CEN ; /* enable DMA */ /* * on the OPTi931 the enable bit seems hard to set... */ for (retry = 10; retry; retry--) { ad_write(d, 9, m ); if (ad_read(d,9) ==m) break; } if (retry == 0) printf("start dma, failed to set bit 0x%02x 0x%02x\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -