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

📄 azt3328.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). *  Copyright (C) 2002, 2005, 2006, 2007 by Andreas Mohr <andi AT lisas.de> * *  Framework borrowed from Bart Hartgers's als4000.c. *  Driver developed on PCI168 AP(W) version (PCI rev. 10, subsystem ID 1801), *  found in a Fujitsu-Siemens PC ("Cordant", aluminum case). *  Other versions are: *  PCI168 A(W), sub ID 1800 *  PCI168 A/AP, sub ID 8000 *  Please give me feedback in case you try my driver with one of these!! * * GPL LICENSE *  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 * * NOTES *  Since Aztech does not provide any chipset documentation, *  even on repeated request to various addresses, *  and the answer that was finally given was negative *  (and I was stupid enough to manage to get hold of a PCI168 soundcard *  in the first place >:-P}), *  I was forced to base this driver on reverse engineering *  (3 weeks' worth of evenings filled with driver work). *  (and no, I did NOT go the easy way: to pick up a SB PCI128 for 9 Euros) * *  The AZF3328 chip (note: AZF3328, *not* AZT3328, that's just the driver name *  for compatibility reasons) has the following features: * *  - builtin AC97 conformant codec (SNR over 80dB) *    Note that "conformant" != "compliant"!! this chip's mixer register layout *    *differs* from the standard AC97 layout: *    they chose to not implement the headphone register (which is not a *    problem since it's merely optional), yet when doing this, they committed *    the grave sin of letting other registers follow immediately instead of *    keeping a headphone dummy register, thereby shifting the mixer register *    addresses illegally. So far unfortunately it looks like the very flexible *    ALSA AC97 support is still not enough to easily compensate for such a *    grave layout violation despite all tweaks and quirks mechanisms it offers. *  - builtin genuine OPL3 *  - full duplex 16bit playback/record at independent sampling rate *  - MPU401 (+ legacy address support) FIXME: how to enable legacy addr?? *  - game port (legacy address support) *  - builtin 3D enhancement (said to be YAMAHA Ymersion) *  - builtin DirectInput support, helps reduce CPU overhead (interrupt-driven *    features supported) *  - built-in General DirectX timer having a 20 bits counter *    with 1us resolution (see below!) *  - I2S serial port for external DAC *  - supports 33MHz PCI spec 2.1, PCI power management 1.0, compliant with ACPI *  - supports hardware volume control *  - single chip low cost solution (128 pin QFP) *  - supports programmable Sub-vendor and Sub-system ID *    required for Microsoft's logo compliance (FIXME: where?) *  - PCI168 AP(W) card: power amplifier with 4 Watts/channel at 4 Ohms * *  Note that this driver now is actually *better* than the Windows driver, *  since it additionally supports the card's 1MHz DirectX timer - just try *  the following snd-seq module parameters etc.: *  - options snd-seq seq_default_timer_class=2 seq_default_timer_sclass=0 *    seq_default_timer_card=0 seq_client_load=1 seq_default_timer_device=0 *    seq_default_timer_subdevice=0 seq_default_timer_resolution=1000000 *  - "timidity -iAv -B2,8 -Os -EFreverb=0" *  - "pmidi -p 128:0 jazz.mid" * *  Certain PCI versions of this card are susceptible to DMA traffic underruns *  in some systems (resulting in sound crackling/clicking/popping), *  probably because they don't have a DMA FIFO buffer or so. *  Overview (PCI ID/PCI subID/PCI rev.): *  - no DMA crackling on SiS735: 0x50DC/0x1801/16 *  - unknown performance: 0x50DC/0x1801/10 *    (well, it's not bad on an Athlon 1800 with now very optimized IRQ handler) * *  Crackling happens with VIA chipsets or, in my case, an SiS735, which is *  supposed to be very fast and supposed to get rid of crackling much *  better than a VIA, yet ironically I still get crackling, like many other *  people with the same chipset. *  Possible remedies: *  - plug card into a different PCI slot, preferrably one that isn't shared *    too much (this helps a lot, but not completely!) *  - get rid of PCI VGA card, use AGP instead *  - upgrade or downgrade BIOS *  - fiddle with PCI latency settings (setpci -v -s BUSID latency_timer=XX) *    Not too helpful. *  - Disable ACPI/power management/"Auto Detect RAM/PCI Clk" in BIOS *  * BUGS *  - full-duplex might *still* be problematic, not fully tested recently *  - (non-bug) "Bass/Treble or 3D settings don't work" - they do get evaluated *    if you set PCM output switch to "pre 3D" instead of "post 3D". *    If this can't be set, then get a mixer application that Isn't Stupid (tm) *    (e.g. kmix, gamix) - unfortunately several are!! *  * TODO *  - test MPU401 MIDI playback etc. *  - add some power micro-management (disable various units of the card *    as long as they're unused). However this requires I/O ports which I *    haven't figured out yet and which thus might not even exist... *    The standard suspend/resume functionality could probably make use of *    some improvement, too... *  - figure out what all unknown port bits are responsible for *  - figure out some cleverly evil scheme to possibly make ALSA AC97 code *    fully accept our quite incompatible ""AC97"" mixer and thus save some *    code (but I'm not too optimistic that doing this is possible at all) */#include <sound/driver.h>#include <asm/io.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/gameport.h>#include <linux/moduleparam.h>#include <linux/dma-mapping.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/rawmidi.h>#include <sound/mpu401.h>#include <sound/opl3.h>#include <sound/initval.h>#include "azt3328.h"MODULE_AUTHOR("Andreas Mohr <andi AT lisas.de>");MODULE_DESCRIPTION("Aztech AZF3328 (PCI168)");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Aztech,AZF3328}}");#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))#define SUPPORT_JOYSTICK 1#endif#define DEBUG_MISC	0#define DEBUG_CALLS	0#define DEBUG_MIXER	0#define DEBUG_PLAY_REC	0#define DEBUG_IO	0#define DEBUG_TIMER	0#define MIXER_TESTING	0#if DEBUG_MISC#define snd_azf3328_dbgmisc(format, args...) printk(KERN_ERR format, ##args)#else#define snd_azf3328_dbgmisc(format, args...)#endif		#if DEBUG_CALLS#define snd_azf3328_dbgcalls(format, args...) printk(format, ##args)#define snd_azf3328_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)#define snd_azf3328_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)#else#define snd_azf3328_dbgcalls(format, args...)#define snd_azf3328_dbgcallenter()#define snd_azf3328_dbgcallleave()#endif		#if DEBUG_MIXER#define snd_azf3328_dbgmixer(format, args...) printk(format, ##args)#else#define snd_azf3328_dbgmixer(format, args...)#endif		#if DEBUG_PLAY_REC#define snd_azf3328_dbgplay(format, args...) printk(KERN_ERR format, ##args)#else#define snd_azf3328_dbgplay(format, args...)#endif		#if DEBUG_MISC#define snd_azf3328_dbgtimer(format, args...) printk(KERN_ERR format, ##args)#else#define snd_azf3328_dbgtimer(format, args...)#endif		static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");#ifdef SUPPORT_JOYSTICKstatic int joystick[SNDRV_CARDS];module_param_array(joystick, bool, NULL, 0444);MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");#endifstatic int seqtimer_scaling = 128;module_param(seqtimer_scaling, int, 0444);MODULE_PARM_DESC(seqtimer_scaling, "Set 1024000Hz sequencer timer scale factor (lockup danger!). Default 128.");struct snd_azf3328 {	/* often-used fields towards beginning, then grouped */	unsigned long codec_port;	unsigned long io2_port;	unsigned long mpu_port;	unsigned long synth_port;	unsigned long mixer_port;	spinlock_t reg_lock;	struct snd_timer *timer;		struct snd_pcm *pcm;	struct snd_pcm_substream *playback_substream;	struct snd_pcm_substream *capture_substream;	unsigned int is_playing;	unsigned int is_recording;	struct snd_card *card;	struct snd_rawmidi *rmidi;#ifdef SUPPORT_JOYSTICK	struct gameport *gameport;#endif	struct pci_dev *pci;	int irq;#ifdef CONFIG_PM	/* register value containers for power management	 * Note: not always full I/O range preserved (just like Win driver!) */	u16 saved_regs_codec [AZF_IO_SIZE_CODEC_PM / 2];	u16 saved_regs_io2   [AZF_IO_SIZE_IO2_PM / 2];	u16 saved_regs_mpu   [AZF_IO_SIZE_MPU_PM / 2];	u16 saved_regs_synth[AZF_IO_SIZE_SYNTH_PM / 2];	u16 saved_regs_mixer[AZF_IO_SIZE_MIXER_PM / 2];#endif};static const struct pci_device_id snd_azf3328_ids[] = {	{ 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* PCI168/3328 */	{ 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* 3328 */	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_azf3328_ids);static inline voidsnd_azf3328_codec_outb(const struct snd_azf3328 *chip, int reg, u8 value){	outb(value, chip->codec_port + reg);}static inline u8snd_azf3328_codec_inb(const struct snd_azf3328 *chip, int reg){	return inb(chip->codec_port + reg);}static inline voidsnd_azf3328_codec_outw(const struct snd_azf3328 *chip, int reg, u16 value){	outw(value, chip->codec_port + reg);}static inline u16snd_azf3328_codec_inw(const struct snd_azf3328 *chip, int reg){	return inw(chip->codec_port + reg);}static inline voidsnd_azf3328_codec_outl(const struct snd_azf3328 *chip, int reg, u32 value){	outl(value, chip->codec_port + reg);}static inline voidsnd_azf3328_io2_outb(const struct snd_azf3328 *chip, int reg, u8 value){	outb(value, chip->io2_port + reg);}static inline u8snd_azf3328_io2_inb(const struct snd_azf3328 *chip, int reg){	return inb(chip->io2_port + reg);}static inline voidsnd_azf3328_mixer_outw(const struct snd_azf3328 *chip, int reg, u16 value){	outw(value, chip->mixer_port + reg);}static inline u16snd_azf3328_mixer_inw(const struct snd_azf3328 *chip, int reg){	return inw(chip->mixer_port + reg);}static voidsnd_azf3328_mixer_set_mute(const struct snd_azf3328 *chip, int reg, int do_mute){	unsigned long portbase = chip->mixer_port + reg + 1;	unsigned char oldval;	/* the mute bit is on the *second* (i.e. right) register of a	 * left/right channel setting */	oldval = inb(portbase);	if (do_mute)		oldval |= 0x80;	else		oldval &= ~0x80;	outb(oldval, portbase);}static voidsnd_azf3328_mixer_write_volume_gradually(const struct snd_azf3328 *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay){	unsigned long portbase = chip->mixer_port + reg;	unsigned char curr_vol_left = 0, curr_vol_right = 0;	int left_done = 0, right_done = 0;		snd_azf3328_dbgcallenter();	if (chan_sel & SET_CHAN_LEFT)		curr_vol_left  = inb(portbase + 1);	else		left_done = 1;	if (chan_sel & SET_CHAN_RIGHT)		curr_vol_right = inb(portbase + 0);	else		right_done = 1;		/* take care of muting flag (0x80) contained in left channel */	if (curr_vol_left & 0x80)		dst_vol_left |= 0x80;	else		dst_vol_left &= ~0x80;	do {		if (!left_done) {			if (curr_vol_left > dst_vol_left)				curr_vol_left--;			else			if (curr_vol_left < dst_vol_left)				curr_vol_left++;			else			    left_done = 1;			outb(curr_vol_left, portbase + 1);		}		if (!right_done) {			if (curr_vol_right > dst_vol_right)				curr_vol_right--;			else			if (curr_vol_right < dst_vol_right)				curr_vol_right++;			else			    right_done = 1;			/* during volume change, the right channel is crackling			 * somewhat more than the left channel, unfortunately.			 * This seems to be a hardware issue. */			outb(curr_vol_right, portbase + 0);		}		if (delay)			mdelay(delay);	} while ((!left_done) || (!right_done));	snd_azf3328_dbgcallleave();}/* * general mixer element */struct azf3328_mixer_reg {	unsigned int reg;	unsigned int lchan_shift, rchan_shift;	unsigned int mask;	unsigned int invert: 1;	unsigned int stereo: 1;	unsigned int enum_c: 4;};#define COMPOSE_MIXER_REG(reg,lchan_shift,rchan_shift,mask,invert,stereo,enum_c) \ ((reg) | (lchan_shift << 8) | (rchan_shift << 12) | \  (mask << 16) | \  (invert << 24) | \  (stereo << 25) | \  (enum_c << 26))static void snd_azf3328_mixer_reg_decode(struct azf3328_mixer_reg *r, unsigned long val){

⌨️ 快捷键说明

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