opl3sa2.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,133 行 · 第 1/2 页

C
1,133
字号
/* * sound/opl3sa2.c * * A low level driver for Yamaha OPL3-SA2 and SA3 cards. * NOTE: All traces of the name OPL3-SAx have now (December 2000) been *       removed from the driver code, as an email exchange with Yamaha *       provided the information that the YMF-719 is indeed just a *       re-badged 715. * * Copyright 1998-2001 Scott Murray <scott@spiteful.org> * * Originally based on the CS4232 driver (in cs4232.c) by Hannu Savolainen * and others.  Now incorporates code/ideas from pss.c, also by Hannu * Savolainen.  Both of those files are distributed with the following * license: * * "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." * * As such, in accordance with the above license, this file, opl3sa2.c, is * distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2 (June 1991). * See the "COPYING" file distributed with this software for more information. * * Change History * -------------- * Scott Murray            Original driver (Jun 14, 1998) * Paul J.Y. Lahaie        Changed probing / attach code order * Scott Murray            Added mixer support (Dec 03, 1998) * Scott Murray            Changed detection code to be more forgiving, *                         added force option as last resort, *                         fixed ioctl return values. (Dec 30, 1998) * Scott Murray            Simpler detection code should work all the time now *                         (with thanks to Ben Hutchings for the heuristic), *                         removed now unnecessary force option. (Jan 5, 1999) * Christoph Hellwig	   Adapted to module_init/module_exit (Mar 4, 2000) * Scott Murray            Reworked SA2 versus SA3 mixer code, updated chipset *                         version detection code (again!). (Dec 5, 2000) * Scott Murray            Adjusted master volume mixer scaling. (Dec 6, 2000) * Scott Murray            Based on a patch by Joel Yliluoma (aka Bisqwit), *                         integrated wide mixer and adjusted mic, bass, treble *                         scaling. (Dec 6, 2000) * Scott Murray            Based on a patch by Peter Englmaier, integrated *                         ymode and loopback options. (Dec 6, 2000) * Scott Murray            Inspired by a patch by Peter Englmaier, and based on *                         what ALSA does, added initialization code for the *                         default DMA and IRQ settings. (Dec 6, 2000) * Scott Murray            Added some more checks to the card detection code, *                         based on what ALSA does. (Dec 12, 2000) * Scott Murray            Inspired by similar patches from John Fremlin, *                         Jim Radford, Mike Rolig, and Ingmar Steen, added 2.4 *                         ISA PnP API support, mainly based on bits from *                         sb_card.c and awe_wave.c. (Dec 12, 2000) * Scott Murray            Some small cleanups to the init code output. *                         (Jan 7, 2001) * Zwane Mwaikambo	   Added PM support. (Dec 4 2001) * * Adam Belay              Converted driver to new PnP Layer (Oct 12, 2002) * Zwane Mwaikambo	   Code, data structure cleanups. (Feb 15 2002) * Zwane Mwaikambo	   Free resources during auxiliary device probe * 			   failures (Apr 29 2002) *    */#include <linux/config.h>#include <linux/pnp.h>#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/pm.h>#include "sound_config.h"#include "ad1848.h"#include "mpu401.h"#define OPL3SA2_MODULE_NAME	"opl3sa2"#define PFX			OPL3SA2_MODULE_NAME ": "/* Useful control port indexes: */#define OPL3SA2_PM	     0x01#define OPL3SA2_SYS_CTRL     0x02#define OPL3SA2_IRQ_CONFIG   0x03#define OPL3SA2_DMA_CONFIG   0x06#define OPL3SA2_MASTER_LEFT  0x07#define OPL3SA2_MASTER_RIGHT 0x08#define OPL3SA2_MIC          0x09#define OPL3SA2_MISC         0x0A#define OPL3SA3_WIDE         0x14#define OPL3SA3_BASS         0x15#define OPL3SA3_TREBLE       0x16/* Useful constants: */#define DEFAULT_VOLUME 50#define DEFAULT_MIC    50#define DEFAULT_TIMBRE 0/* Power saving modes */#define OPL3SA2_PM_MODE0	0x00#define OPL3SA2_PM_MODE1	0x04	/* PSV */#define OPL3SA2_PM_MODE2	0x05	/* PSV | PDX */#define OPL3SA2_PM_MODE3	0x27	/* ADOWN | PSV | PDN | PDX *//* For checking against what the card returns: */#define VERSION_UNKNOWN 0#define VERSION_YMF711  1#define VERSION_YMF715  2#define VERSION_YMF715B 3#define VERSION_YMF715E 4/* also assuming that anything > 4 but <= 7 is a 715E *//* Chipset type constants for use below */#define CHIPSET_UNKNOWN -1#define CHIPSET_OPL3SA2 0#define CHIPSET_OPL3SA3 1static const char *CHIPSET_TABLE[] = {"OPL3-SA2", "OPL3-SA3"};#ifdef CONFIG_PNP#define OPL3SA2_CARDS_MAX 4#else#define OPL3SA2_CARDS_MAX 1#endif/* This should be pretty obvious */static int opl3sa2_cards_num;typedef struct {	/* device resources */	unsigned short cfg_port;	struct address_info cfg;	struct address_info cfg_mss;	struct address_info cfg_mpu;#ifdef CONFIG_PNP	/* PnP Stuff */	struct pnp_dev* pdev;	int activated;			/* Whether said devices have been activated */#endif#ifdef CONFIG_PM	unsigned int	in_suspend;	struct pm_dev	*pmdev;#endif	unsigned int	card;	int		chipset;	/* What's my version(s)? */	char		*chipset_name;	/* mixer data */	int		mixer;	unsigned int	volume_l;	unsigned int	volume_r;	unsigned int	mic;	unsigned int	bass_l;	unsigned int	bass_r;	unsigned int	treble_l;	unsigned int	treble_r;	unsigned int	wide_l;	unsigned int	wide_r;} opl3sa2_state_t;static opl3sa2_state_t opl3sa2_state[OPL3SA2_CARDS_MAX];	/* Our parameters */static int __initdata io	= -1;static int __initdata mss_io	= -1;static int __initdata mpu_io	= -1;static int __initdata irq	= -1;static int __initdata dma	= -1;static int __initdata dma2	= -1;static int __initdata ymode	= -1;static int __initdata loopback	= -1;#ifdef CONFIG_PNP/* PnP specific parameters */static int __initdata isapnp = 1;static int __initdata multiple = 1;/* PnP devices */struct pnp_dev* opl3sa2_dev[OPL3SA2_CARDS_MAX];/* Whether said devices have been activated */static int opl3sa2_activated[OPL3SA2_CARDS_MAX];#elsestatic int __initdata isapnp; /* = 0 */static int __initdata multiple; /* = 0 */#endifMODULE_DESCRIPTION("Module for OPL3-SA2 and SA3 sound cards (uses AD1848 MSS driver).");MODULE_AUTHOR("Scott Murray <scott@spiteful.org>");MODULE_LICENSE("GPL");MODULE_PARM(io, "i");MODULE_PARM_DESC(io, "Set I/O base of OPL3-SA2 or SA3 card (usually 0x370.  Address must be even and must be from 0x100 to 0xFFE)");MODULE_PARM(mss_io, "i");MODULE_PARM_DESC(mss_io, "Set MSS (audio) I/O base (0x530, 0xE80, or other. Address must end in 0 or 4 and must be from 0x530 to 0xF48)");MODULE_PARM(mpu_io, "i");MODULE_PARM_DESC(mpu_io, "Set MIDI I/O base (0x330 or other. Address must be even and must be from 0x300 to 0x334)");MODULE_PARM(irq, "i");MODULE_PARM_DESC(mss_irq, "Set MSS (audio) IRQ (5, 7, 9, 10, 11, 12)");MODULE_PARM(dma, "i");MODULE_PARM_DESC(dma, "Set MSS (audio) first DMA channel (0, 1, 3)");MODULE_PARM(dma2, "i");MODULE_PARM_DESC(dma2, "Set MSS (audio) second DMA channel (0, 1, 3)");MODULE_PARM(ymode, "i");MODULE_PARM_DESC(ymode, "Set Yamaha 3D enhancement mode (0 = Desktop/Normal, 1 = Notebook PC (1), 2 = Notebook PC (2), 3 = Hi-Fi)");MODULE_PARM(loopback, "i");MODULE_PARM_DESC(loopback, "Set A/D input source. Useful for echo cancellation (0 = Mic Rch (default), 1 = Mono output loopback)");#ifdef CONFIG_PNPMODULE_PARM(isapnp, "i");MODULE_PARM_DESC(isapnp, "When set to 0, ISA PnP support will be disabled");MODULE_PARM(multiple, "i");MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards");#endif/* * Standard read and write functions*/static inline void opl3sa2_write(unsigned short port,				 unsigned char  index,				 unsigned char  data){	outb_p(index, port);	outb(data, port + 1);}static inline void opl3sa2_read(unsigned short port,				unsigned char  index,				unsigned char* data){	outb_p(index, port);	*data = inb(port + 1);}/* * All of the mixer functions... */static void opl3sa2_set_volume(opl3sa2_state_t* devc, int left, int right){	static unsigned char scale[101] = {		0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0e, 0x0e,		0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c,		0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b,		0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09,		0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08,		0x08, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06,		0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,		0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03,		0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,		0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,		0x00	};	unsigned char vol;	vol = scale[left];	/* If level is zero, turn on mute */	if(!left)		vol |= 0x80;	opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_LEFT, vol);	vol = scale[right];	/* If level is zero, turn on mute */	if(!right)		vol |= 0x80;	opl3sa2_write(devc->cfg_port, OPL3SA2_MASTER_RIGHT, vol);}static void opl3sa2_set_mic(opl3sa2_state_t* devc, int level){	unsigned char vol = 0x1F;	if((level >= 0) && (level <= 100))		vol = 0x1F - (unsigned char) (32 * level / 101);	/* If level is zero, turn on mute */	if(!level)		vol |= 0x80;	opl3sa2_write(devc->cfg_port, OPL3SA2_MIC, vol);}static void opl3sa3_set_bass(opl3sa2_state_t* devc, int left, int right){	unsigned char bass;	bass = left ? ((unsigned char) (8 * left / 101)) : 0; 	bass |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4;	opl3sa2_write(devc->cfg_port, OPL3SA3_BASS, bass);}static void opl3sa3_set_treble(opl3sa2_state_t* devc, int left, int right){		unsigned char treble;	treble = left ? ((unsigned char) (8 * left / 101)) : 0; 	treble |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4;	opl3sa2_write(devc->cfg_port, OPL3SA3_TREBLE, treble);}static void opl3sa2_mixer_reset(opl3sa2_state_t* devc){	if (devc) {		opl3sa2_set_volume(devc, DEFAULT_VOLUME, DEFAULT_VOLUME);		devc->volume_l = devc->volume_r = DEFAULT_VOLUME;		opl3sa2_set_mic(devc, DEFAULT_MIC);		devc->mic = DEFAULT_MIC;		if (devc->chipset == CHIPSET_OPL3SA3) {			opl3sa3_set_bass(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE);			devc->bass_l = devc->bass_r = DEFAULT_TIMBRE;			opl3sa3_set_treble(devc, DEFAULT_TIMBRE, DEFAULT_TIMBRE);			devc->treble_l = devc->treble_r = DEFAULT_TIMBRE;		}	}}/* Currently only used for power management */#ifdef CONFIG_PMstatic void opl3sa2_mixer_restore(opl3sa2_state_t* devc){	if (devc) {		opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);		opl3sa2_set_mic(devc, devc->mic);		if (devc->chipset == CHIPSET_OPL3SA3) {			opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r);			opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r);		}	}}#endifstatic inline void arg_to_vol_mono(unsigned int vol, int* value){	int left;		left = vol & 0x00ff;	if (left > 100)		left = 100;	*value = left;}static inline void arg_to_vol_stereo(unsigned int vol, int* aleft, int* aright){	arg_to_vol_mono(vol, aleft);	arg_to_vol_mono(vol >> 8, aright);}static inline int ret_vol_mono(int vol){	return ((vol << 8) | vol);}static inline int ret_vol_stereo(int left, int right){	return ((right << 8) | left);}static int opl3sa2_mixer_ioctl(int dev, unsigned int cmd, void __user *arg){	int retval, value, cmdf = cmd & 0xff;	int __user *p = (int __user *)arg;	opl3sa2_state_t* devc = &opl3sa2_state[dev];		switch (cmdf) {		case SOUND_MIXER_VOLUME:		case SOUND_MIXER_MIC:		case SOUND_MIXER_DEVMASK:		case SOUND_MIXER_STEREODEVS: 		case SOUND_MIXER_RECMASK:		case SOUND_MIXER_RECSRC:		case SOUND_MIXER_CAPS: 			break;		default:			return -EINVAL;	}		if (((cmd >> 8) & 0xff) != 'M')		return -EINVAL;			retval = 0;	if (_SIOC_DIR (cmd) & _SIOC_WRITE) {		switch (cmdf) {			case SOUND_MIXER_VOLUME:				retval = get_user(value, (unsigned __user *) arg);				if (retval)					break;				arg_to_vol_stereo(value, &devc->volume_l, &devc->volume_r);				opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);				value = ret_vol_stereo(devc->volume_l, devc->volume_r);				retval = put_user(value, p);				break;		  			case SOUND_MIXER_MIC:				retval = get_user(value, (unsigned __user *) arg);				if (retval)					break;				arg_to_vol_mono(value, &devc->mic);				opl3sa2_set_mic(devc, devc->mic);				value = ret_vol_mono(devc->mic);				retval = put_user(value, p);				break;			default:				retval = -EINVAL;		}	}	else {		/*		 * Return parameters		 */		switch (cmdf) {			case SOUND_MIXER_DEVMASK:				retval = put_user(SOUND_MASK_VOLUME | SOUND_MASK_MIC, p);				break;		  			case SOUND_MIXER_STEREODEVS:				retval = put_user(SOUND_MASK_VOLUME, p);				break;		  			case SOUND_MIXER_RECMASK:				/* No recording devices */				retval = put_user(0, p);				break;			case SOUND_MIXER_CAPS:				retval = put_user(SOUND_CAP_EXCL_INPUT, p);				break;			case SOUND_MIXER_RECSRC:				/* No recording source */				retval = put_user(0, p);				break;			case SOUND_MIXER_VOLUME:				value = ret_vol_stereo(devc->volume_l, devc->volume_r);				retval = put_user(value, p);				break;			  			case SOUND_MIXER_MIC:				value = ret_vol_mono(devc->mic);				put_user(value, p);				break;			default:				retval = -EINVAL;		}	}	return retval;}/* opl3sa2_mixer_ioctl end */static int opl3sa3_mixer_ioctl(int dev, unsigned int cmd, void __user * arg){	int value, retval, cmdf = cmd & 0xff;	opl3sa2_state_t* devc = &opl3sa2_state[dev];	switch (cmdf) {	case SOUND_MIXER_BASS:		value = ret_vol_stereo(devc->bass_l, devc->bass_r);		retval = put_user(value, (int __user *) arg);		break;			case SOUND_MIXER_TREBLE:		value = ret_vol_stereo(devc->treble_l, devc->treble_r);		retval = put_user(value, (int __user *) arg);		break;	case SOUND_MIXER_DIGITAL1:		value = ret_vol_stereo(devc->wide_l, devc->wide_r);		retval = put_user(value, (int __user *) arg);		break;	default:		retval = -EINVAL;	}	return retval;}/* opl3sa3_mixer_ioctl end */static struct mixer_operations opl3sa2_mixer_operations ={	.owner	= THIS_MODULE,	.id	= "OPL3-SA2",	.name	= "Yamaha OPL3-SA2",	.ioctl	= opl3sa2_mixer_ioctl};static struct mixer_operations opl3sa3_mixer_operations ={	.owner	= THIS_MODULE,	.id	= "OPL3-SA3",	.name	= "Yamaha OPL3-SA3",	.ioctl	= opl3sa3_mixer_ioctl};/* End of mixer-related stuff *//* * Component probe, attach, unload functions */static inline void __exit unload_opl3sa2_mpu(struct address_info *hw_config){	unload_mpu401(hw_config);}static void __init attach_opl3sa2_mss(struct address_info* hw_config, struct resource *ports){	int initial_mixers;	initial_mixers = num_mixers;	attach_ms_sound(hw_config, ports, THIS_MODULE);	/* Slot 0 */	if (hw_config->slots[0] != -1) {		/* Did the MSS driver install? */		if(num_mixers == (initial_mixers + 1)) {			/* The MSS mixer is installed, reroute mixers appropiately */			AD1848_REROUTE(SOUND_MIXER_LINE1, SOUND_MIXER_CD);			AD1848_REROUTE(SOUND_MIXER_LINE2, SOUND_MIXER_SYNTH);			AD1848_REROUTE(SOUND_MIXER_LINE3, SOUND_MIXER_LINE);		}		else {			printk(KERN_ERR PFX "MSS mixer not installed?\n");		}	}}

⌨️ 快捷键说明

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