📄 opl3sa2.c
字号:
/* * 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) * */#include <linux/config.h>#include <linux/init.h>#include <linux/module.h>#include <linux/isapnp.h>#include <linux/pm.h>#include <linux/delay.h>#include "sound_config.h"#include "ad1848.h"#include "mpu401.h"#define OPL3SA2_MODULE_NAME "opl3sa2"/* 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_MODE1 0x05#define OPL3SA2_PM_MODE2 0x04#define OPL3SA2_PM_MODE3 0x03/* 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 1#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE#define OPL3SA2_CARDS_MAX 4#else#define OPL3SA2_CARDS_MAX 1#endif/* This should be pretty obvious */static int opl3sa2_cards_num; /* = 0 *//* What's my version(s)? */static int chipset[OPL3SA2_CARDS_MAX] = { CHIPSET_UNKNOWN };/* Oh well, let's just cache the name(s) */static char chipset_name[OPL3SA2_CARDS_MAX][12];/* Where's my mixer(s)? */static int opl3sa2_mixer[OPL3SA2_CARDS_MAX] = { -1 };/* Bag o' mixer data */typedef struct opl3sa2_mixerdata_tag { unsigned short cfg_port; unsigned short padding; unsigned char pm_reg; unsigned int in_suspend; struct pm_dev *pmdev; unsigned int card; 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_mixerdata;static opl3sa2_mixerdata opl3sa2_data[OPL3SA2_CARDS_MAX];static struct address_info cfg[OPL3SA2_CARDS_MAX];static struct address_info cfg_mss[OPL3SA2_CARDS_MAX];static struct address_info cfg_mpu[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;#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE/* PnP specific parameters */static int __initdata isapnp = 1;static int __initdata multiple = 1;/* PnP devices */struct pci_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)");#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULEMODULE_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_mixerdata* 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_mixerdata* 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_mixerdata* 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_mixerdata* 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 opl3sa3_set_wide(opl3sa2_mixerdata* devc, int left, int right){ unsigned char wide; wide = left ? ((unsigned char) (8 * left / 101)) : 0; wide |= (right ? ((unsigned char) (8 * right / 101)) : 0) << 4; opl3sa2_write(devc->cfg_port, OPL3SA3_WIDE, wide);}static void opl3sa2_mixer_reset(opl3sa2_mixerdata* devc, int card){ 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(chipset[card] == 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; } }}static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card){ if (devc) { opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r); opl3sa2_set_mic(devc, devc->mic); if (chipset[card] == CHIPSET_OPL3SA3) { opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r); opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r); } }}static 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, caddr_t arg){ int cmdf = cmd & 0xff; opl3sa2_mixerdata* devc = (opl3sa2_mixerdata*) mixer_devs[dev]->devc; switch(cmdf) { case SOUND_MIXER_VOLUME: case SOUND_MIXER_MIC: case SOUND_MIXER_DEVMASK: case SOUND_MIXER_STEREODEVS:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -