⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ad1848.c

📁 freebsd v4.4内核源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 + -