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

📄 opl3_lib.c

📁 鼎力推荐!本程序是基于嵌入式LUNUX系统开发的源程序代码
💻 C
字号:
/* *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>, *                   Hannu Savolainen 1993-1996, *                   Rob Hooft *                    *  Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips) * *  Most if code is ported from OSS/Lite. * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License as published by *   the Free Software Foundation; either version 2 of the License, or *   (at your option) any later version. * *   This program is distributed in the hope that it will be useful, *   but WITHOUT ANY WARRANTY; without even the implied warranty of *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *   GNU General Public License for more details. * *   You should have received a copy of the GNU General Public License *   along with this program; if not, write to the Free Software *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA * */#include <sound/opl3.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/ioport.h>#include <sound/minors.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Hannu Savolainen 1993-1996, Rob Hooft");MODULE_DESCRIPTION("Routines for control of AdLib FM cards (OPL2/OPL3/OPL4 chips)");MODULE_LICENSE("GPL");extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];static void snd_opl2_command(opl3_t * opl3, unsigned short cmd, unsigned char val){	unsigned long flags;	unsigned long port;	/*	 * The original 2-OP synth requires a quite long delay	 * after writing to a register.	 */	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;	spin_lock_irqsave(&opl3->reg_lock, flags);	outb((unsigned char) cmd, port);	udelay(10);	outb((unsigned char) val, port + 1);	udelay(30);	spin_unlock_irqrestore(&opl3->reg_lock, flags);}static void snd_opl3_command(opl3_t * opl3, unsigned short cmd, unsigned char val){	unsigned long flags;	unsigned long port;	/*	 * The OPL-3 survives with just two INBs	 * after writing to a register.	 */	port = (cmd & OPL3_RIGHT) ? opl3->r_port : opl3->l_port;	spin_lock_irqsave(&opl3->reg_lock, flags);	outb((unsigned char) cmd, port);	inb(opl3->l_port);	inb(opl3->l_port);	outb((unsigned char) val, port + 1);	inb(opl3->l_port);	inb(opl3->l_port);	spin_unlock_irqrestore(&opl3->reg_lock, flags);}static int snd_opl3_detect(opl3_t * opl3){	/*	 * This function returns 1 if the FM chip is present at the given I/O port	 * The detection algorithm plays with the timer built in the FM chip and	 * looks for a change in the status register.	 *	 * Note! The timers of the FM chip are not connected to AdLib (and compatible)	 * boards.	 *	 * Note2! The chip is initialized if detected.	 */	unsigned char stat1, stat2, signature;	/* Reset timers 1 and 2 */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);	/* Reset the IRQ of the FM chip */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);	signature = stat1 = inb(opl3->l_port);	/* Status register */	if ((stat1 & 0xe0) != 0x00) {	/* Should be 0x00 */		snd_printd("OPL3: stat1 = 0x%x\n", stat1);		return -ENODEV;	}	/* Set timer1 to 0xff */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 0xff);	/* Unmask and start timer 1 */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER2_MASK | OPL3_TIMER1_START);	/* Now we have to delay at least 80us */	udelay(200);	/* Read status after timers have expired */	stat2 = inb(opl3->l_port);	/* Stop the timers */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_TIMER1_MASK | OPL3_TIMER2_MASK);	/* Reset the IRQ of the FM chip */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, OPL3_IRQ_RESET);	if ((stat2 & 0xe0) != 0xc0) {	/* There is no YM3812 */		snd_printd("OPL3: stat2 = 0x%x\n", stat2);		return -ENODEV;	}	/* If the toplevel code knows exactly the type of chip, don't try	   to detect it. */	if (opl3->hardware != OPL3_HW_AUTO)		return 0;	/* There is a FM chip on this address. Detect the type (OPL2 to OPL4) */	if (signature == 0x06) {	/* OPL2 */		opl3->hardware = OPL3_HW_OPL2;	} else {		/*		 * If we had an OPL4 chip, opl3->hardware would have been set		 * by the OPL4 driver; so we can assume OPL3 here.		 */		snd_assert(opl3->r_port != 0, return -ENODEV);		opl3->hardware = OPL3_HW_OPL3;	}	return 0;}/* *  AdLib timers *//* *  Timer 1 - 80us */static int snd_opl3_timer1_start(snd_timer_t * timer){	unsigned long flags;	unsigned char tmp;	unsigned int ticks;	opl3_t *opl3;	opl3 = snd_timer_chip(timer);	spin_lock_irqsave(&opl3->timer_lock, flags);	ticks = timer->sticks;	tmp = (opl3->timer_enable | OPL3_TIMER1_START) & ~OPL3_TIMER1_MASK;	opl3->timer_enable = tmp;	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER1, 256 - ticks);	/* timer 1 count */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */	spin_unlock_irqrestore(&opl3->timer_lock, flags);	return 0;}static int snd_opl3_timer1_stop(snd_timer_t * timer){	unsigned long flags;	unsigned char tmp;	opl3_t *opl3;	opl3 = snd_timer_chip(timer);	spin_lock_irqsave(&opl3->timer_lock, flags);	tmp = (opl3->timer_enable | OPL3_TIMER1_MASK) & ~OPL3_TIMER1_START;	opl3->timer_enable = tmp;	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */	spin_unlock_irqrestore(&opl3->timer_lock, flags);	return 0;}/* *  Timer 2 - 320us */static int snd_opl3_timer2_start(snd_timer_t * timer){	unsigned long flags;	unsigned char tmp;	unsigned int ticks;	opl3_t *opl3;	opl3 = snd_timer_chip(timer);	spin_lock_irqsave(&opl3->timer_lock, flags);	ticks = timer->sticks;	tmp = (opl3->timer_enable | OPL3_TIMER2_START) & ~OPL3_TIMER2_MASK;	opl3->timer_enable = tmp;	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER2, 256 - ticks);	/* timer 1 count */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* enable timer 1 IRQ */	spin_unlock_irqrestore(&opl3->timer_lock, flags);	return 0;}static int snd_opl3_timer2_stop(snd_timer_t * timer){	unsigned long flags;	unsigned char tmp;	opl3_t *opl3;	opl3 = snd_timer_chip(timer);	spin_lock_irqsave(&opl3->timer_lock, flags);	tmp = (opl3->timer_enable | OPL3_TIMER2_MASK) & ~OPL3_TIMER2_START;	opl3->timer_enable = tmp;	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TIMER_CONTROL, tmp);	/* disable timer #1 */	spin_unlock_irqrestore(&opl3->timer_lock, flags);	return 0;}/* */static struct _snd_timer_hardware snd_opl3_timer1 ={	.flags =	SNDRV_TIMER_HW_STOP,	.resolution =	80000,	.ticks =	256,	.start =	snd_opl3_timer1_start,	.stop =		snd_opl3_timer1_stop,};static struct _snd_timer_hardware snd_opl3_timer2 ={	.flags =	SNDRV_TIMER_HW_STOP,	.resolution =	320000,	.ticks =	256,	.start =	snd_opl3_timer2_start,	.stop =		snd_opl3_timer2_stop,};static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no){	snd_timer_t *timer = NULL;	snd_timer_id_t tid;	int err;	tid.dev_class = SNDRV_TIMER_CLASS_CARD;	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;	tid.card = opl3->card->number;	tid.device = timer_no;	tid.subdevice = 0;	if ((err = snd_timer_new(opl3->card, "AdLib timer #1", &tid, &timer)) >= 0) {		strcpy(timer->name, "AdLib timer #1");		timer->private_data = opl3;		timer->hw = snd_opl3_timer1;	}	opl3->timer1 = timer;	return err;}static int snd_opl3_timer2_init(opl3_t * opl3, int timer_no){	snd_timer_t *timer = NULL;	snd_timer_id_t tid;	int err;	tid.dev_class = SNDRV_TIMER_CLASS_CARD;	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;	tid.card = opl3->card->number;	tid.device = timer_no;	tid.subdevice = 0;	if ((err = snd_timer_new(opl3->card, "AdLib timer #2", &tid, &timer)) >= 0) {		strcpy(timer->name, "AdLib timer #2");		timer->private_data = opl3;		timer->hw = snd_opl3_timer2;	}	opl3->timer2 = timer;	return err;}/* */void snd_opl3_interrupt(snd_hwdep_t * hw){	unsigned char status;	opl3_t *opl3;	snd_timer_t *timer;	if (hw == NULL)		return;	opl3 = hw->private_data;	status = inb(opl3->l_port);#if 0	snd_printk("AdLib IRQ status = 0x%x\n", status);#endif	if (!(status & 0x80))		return;	if (status & 0x40) {		timer = opl3->timer1;		snd_timer_interrupt(timer, timer->sticks);	}	if (status & 0x20) {		timer = opl3->timer2;		snd_timer_interrupt(timer, timer->sticks);	}}/* */static int snd_opl3_free(opl3_t *opl3){	snd_assert(opl3 != NULL, return -ENXIO);	if (opl3->private_free)		opl3->private_free(opl3);	if (opl3->res_l_port) {		release_resource(opl3->res_l_port);		kfree_nocheck(opl3->res_l_port);	}	if (opl3->res_r_port) {		release_resource(opl3->res_r_port);		kfree_nocheck(opl3->res_r_port);	}	kfree(opl3);	return 0;}static int snd_opl3_dev_free(snd_device_t *device){	opl3_t *opl3 = device->device_data;	return snd_opl3_free(opl3);}int snd_opl3_new(snd_card_t *card,		 unsigned short hardware,		 opl3_t **ropl3){	static snd_device_ops_t ops = {		.dev_free = snd_opl3_dev_free,	};	opl3_t *opl3;	int err;	*ropl3 = NULL;	opl3 = kcalloc(1, sizeof(*opl3), GFP_KERNEL);	if (opl3 == NULL)		return -ENOMEM;	opl3->card = card;	opl3->hardware = hardware;	spin_lock_init(&opl3->reg_lock);	spin_lock_init(&opl3->timer_lock);	init_MUTEX(&opl3->access_mutex);	if ((err = snd_device_new(card, SNDRV_DEV_CODEC, opl3, &ops)) < 0) {		snd_opl3_free(opl3);		return err;	}	*ropl3 = opl3;	return 0;}int snd_opl3_init(opl3_t *opl3){	if (! opl3->command) {		printk(KERN_ERR "snd_opl3_init: command not defined!\n");		return -EINVAL;	}	opl3->command(opl3, OPL3_LEFT | OPL3_REG_TEST, OPL3_ENABLE_WAVE_SELECT);	/* Melodic mode */	opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, 0x00);	switch (opl3->hardware & OPL3_HW_MASK) {	case OPL3_HW_OPL2:		opl3->max_voices = MAX_OPL2_VOICES;		break;	case OPL3_HW_OPL3:	case OPL3_HW_OPL4:		opl3->max_voices = MAX_OPL3_VOICES;		/* Enter OPL3 mode */		opl3->command(opl3, OPL3_RIGHT | OPL3_REG_MODE, OPL3_OPL3_ENABLE);	}	return 0;}int snd_opl3_create(snd_card_t * card,		    unsigned long l_port,		    unsigned long r_port,		    unsigned short hardware,		    int integrated,		    opl3_t ** ropl3){	opl3_t *opl3;	int err;	*ropl3 = NULL;	if ((err = snd_opl3_new(card, hardware, &opl3)) < 0)		return err;	if (! integrated) {		if ((opl3->res_l_port = request_region(l_port, 2, "OPL2/3 (left)")) == NULL) {			snd_printk(KERN_ERR "opl3: can't grab left port 0x%lx\n", l_port);			snd_opl3_free(opl3);			return -EBUSY;		}		if (r_port != 0 &&		    (opl3->res_r_port = request_region(r_port, 2, "OPL2/3 (right)")) == NULL) {			snd_printk(KERN_ERR "opl3: can't grab right port 0x%lx\n", r_port);			snd_opl3_free(opl3);			return -EBUSY;		}	}	opl3->l_port = l_port;	opl3->r_port = r_port;	switch (opl3->hardware) {	/* some hardware doesn't support timers */	case OPL3_HW_OPL3_SV:	case OPL3_HW_OPL3_CS:	case OPL3_HW_OPL3_FM801:		opl3->command = &snd_opl3_command;		break;	default:		opl3->command = &snd_opl2_command;		if ((err = snd_opl3_detect(opl3)) < 0) {			snd_printd("OPL2/3 chip not detected at 0x%lx/0x%lx\n",				   opl3->l_port, opl3->r_port);			snd_opl3_free(opl3);			return err;		}		/* detect routine returns correct hardware type */		switch (opl3->hardware & OPL3_HW_MASK) {		case OPL3_HW_OPL3:		case OPL3_HW_OPL4:			opl3->command = &snd_opl3_command;		}	}	snd_opl3_init(opl3);	*ropl3 = opl3;	return 0;}int snd_opl3_timer_new(opl3_t * opl3, int timer1_dev, int timer2_dev){	int err;	if (timer1_dev >= 0)		if ((err = snd_opl3_timer1_init(opl3, timer1_dev)) < 0)			return err;	if (timer2_dev >= 0) {		if ((err = snd_opl3_timer2_init(opl3, timer2_dev)) < 0) {			snd_device_free(opl3->card, opl3->timer1);			opl3->timer1 = NULL;			return err;		}	}	return 0;}int snd_opl3_hwdep_new(opl3_t * opl3,		       int device, int seq_device,		       snd_hwdep_t ** rhwdep){	snd_hwdep_t *hw;	snd_card_t *card = opl3->card;	int err;	if (rhwdep)		*rhwdep = NULL;	/* create hardware dependent device (direct FM) */	if ((err = snd_hwdep_new(card, "OPL2/OPL3", device, &hw)) < 0) {		snd_device_free(card, opl3);		return err;	}	hw->private_data = opl3;#ifdef CONFIG_SND_OSSEMUL	if (device == 0) {		hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;		sprintf(hw->oss_dev, "dmfm%i", card->number);	}#endif	strcpy(hw->name, hw->id);	switch (opl3->hardware & OPL3_HW_MASK) {	case OPL3_HW_OPL2:		strcpy(hw->name, "OPL2 FM");		hw->iface = SNDRV_HWDEP_IFACE_OPL2;		break;	case OPL3_HW_OPL3:		strcpy(hw->name, "OPL3 FM");		hw->iface = SNDRV_HWDEP_IFACE_OPL3;		break;	case OPL3_HW_OPL4:		strcpy(hw->name, "OPL4 FM");		hw->iface = SNDRV_HWDEP_IFACE_OPL4;		break;	}	/* operators - only ioctl */	hw->ops.open = snd_opl3_open;	hw->ops.ioctl = snd_opl3_ioctl;	hw->ops.release = snd_opl3_release;	opl3->seq_dev_num = seq_device;#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))	if (snd_seq_device_new(card, seq_device, SNDRV_SEQ_DEV_ID_OPL3,			       sizeof(opl3_t*), &opl3->seq_dev) >= 0) {		strcpy(opl3->seq_dev->name, hw->name);		*(opl3_t**)SNDRV_SEQ_DEVICE_ARGPTR(opl3->seq_dev) = opl3;	}#endif	if (rhwdep)		*rhwdep = hw;	return 0;}EXPORT_SYMBOL(snd_opl3_interrupt);EXPORT_SYMBOL(snd_opl3_new);EXPORT_SYMBOL(snd_opl3_init);EXPORT_SYMBOL(snd_opl3_create);EXPORT_SYMBOL(snd_opl3_timer_new);EXPORT_SYMBOL(snd_opl3_hwdep_new);/* opl3_synth.c */EXPORT_SYMBOL(snd_opl3_regmap);EXPORT_SYMBOL(snd_opl3_reset);/* *  INIT part */static int __init alsa_opl3_init(void){	return 0;}static void __exit alsa_opl3_exit(void){}module_init(alsa_opl3_init)module_exit(alsa_opl3_exit)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -