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

📄 azt3328.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  azt3328.c - driver for Aztech AZF3328 based soundcards (e.g. PCI168). *  Copyright (C) 2002 by Andreas Mohr <hw7oshyuv3001@sneakemail.com> * *  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 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) *    (really AC97 compliant?? I really doubt it when looking *    at the mixer register layout) *  - 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) *  - built-in General DirectX timer having a 20 bits counter *    with 1us resolution (FIXME: where is it?) *  - 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 * *  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 *   *  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 *  - when Ctrl-C'ing mpg321, the playback loops a bit *    (premature DMA playback reset?) *  - full-duplex sometimes breaks (IRQ management issues?). *    Once even a spontaneous REBOOT happened!!! *  * TODO *  - test MPU401 MIDI playback etc. *  - power management (CONFIG_PM). See e.g. intel8x0 or cs4281. *    This would be nice since the chip runs a bit hot, and it's *required* *    anyway for proper ACPI power management. In other words: rest *    assured that I *will* implement this very soon; as soon as Linux 2.5.x *    has power management that's bugfree enough to work properly on my desktop. *  - figure out what all unknown port bits are responsible for */#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 <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 <hw7oshyuv3001@sneakemail.com>");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 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 "entering %s\n", __FUNCTION__)#define snd_azf3328_dbgcallleave() printk(KERN_ERR "leaving %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_IO#define snd_azf3328_dbgio(chip, where) \	    printk(KERN_ERR "%s: IDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQMASK %04x, IDX_IO_IRQSTATUS %04x\n", where, inw(chip->codec_port+IDX_IO_PLAY_FLAGS), inw(chip->codec_port+IDX_IO_PLAY_IRQMASK), inw(chip->codec_port+IDX_IO_IRQSTATUS))#else#define snd_azf3328_dbgio(chip, where)#endif	    static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;	/* Enable this card */#ifdef SUPPORT_JOYSTICKstatic int joystick[SNDRV_CARDS];#endifstatic int boot_devs;module_param_array(index, int, boot_devs, 0444);MODULE_PARM_DESC(index, "Index value for AZF3328 soundcard.");module_param_array(id, charp, boot_devs, 0444);MODULE_PARM_DESC(id, "ID string for AZF3328 soundcard.");module_param_array(enable, bool, boot_devs, 0444);MODULE_PARM_DESC(enable, "Enable AZF3328 soundcard.");#ifdef SUPPORT_JOYSTICKmodule_param_array(joystick, bool, boot_devs, 0444);MODULE_PARM_DESC(joystick, "Enable joystick for AZF3328 soundcard.");#endiftypedef struct _snd_azf3328 azf3328_t;struct _snd_azf3328 {	int irq;	unsigned long codec_port;	unsigned long io2_port;	unsigned long mpu_port;	unsigned long synth_port;	unsigned long mixer_port;#ifdef SUPPORT_JOYSTICK	struct gameport gameport;	struct resource *res_joystick;#endif	struct pci_dev *pci;	snd_card_t *card;	snd_pcm_t *pcm;	snd_rawmidi_t *rmidi;	snd_pcm_substream_t *playback_substream;	snd_pcm_substream_t *capture_substream;	unsigned int is_playing;	unsigned int is_recording;	spinlock_t reg_lock;};static 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);void snd_azf3328_io2_write(azf3328_t *chip, int reg, unsigned char value){	outb(value, chip->io2_port + reg);}unsigned char snd_azf3328_io2_read(azf3328_t *chip, int reg){	return inb(chip->io2_port + reg);}void snd_azf3328_mixer_write(azf3328_t *chip, int reg, unsigned long value, int type){	switch(type) {	case WORD_VALUE:		outw(value, chip->mixer_port + reg);		break;	case DWORD_VALUE:		outl(value, chip->mixer_port + reg);		break;	case BYTE_VALUE:		outb(value, chip->mixer_port + reg);		break;	}}unsigned long snd_azf3328_mixer_read(azf3328_t *chip, int reg, int type){	unsigned long res = 0;	switch(type) {	case WORD_VALUE:		res = (unsigned long)inw(chip->mixer_port + reg);		break;	case DWORD_VALUE:		res = (unsigned long)inl(chip->mixer_port + reg);		break;	case BYTE_VALUE:		res = (unsigned long)inb(chip->mixer_port + reg);		break;	}	return res;}void snd_azf3328_mixer_set_mute(azf3328_t *chip, int reg, int do_mute){	unsigned char oldval;	/* the mute bit is on the *second* (i.e. right) register of a	 * left/right channel setting */	oldval = inb(chip->mixer_port + reg + 1);	if (do_mute)		oldval |= 0x80;	else		oldval &= ~0x80;	outb(oldval, chip->mixer_port + reg + 1);}void snd_azf3328_mixer_write_volume_gradually(azf3328_t *chip, int reg, unsigned char dst_vol_left, unsigned char dst_vol_right, int chan_sel, int delay){	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(chip->mixer_port + reg + 1);	else		left_done = 1;	if (chan_sel & SET_CHAN_RIGHT)		curr_vol_right = inb(chip->mixer_port + reg + 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, chip->mixer_port + reg + 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, chip->mixer_port + reg + 0);		}		if (delay)			mdelay(delay);	}	while ((!left_done) || (!right_done));	snd_azf3328_dbgcallleave();}/* * general mixer element */typedef 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;} azf3328_mixer_reg_t;#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(azf3328_mixer_reg_t *r, unsigned long val){	r->reg = val & 0xff;	r->lchan_shift = (val >> 8) & 0x0f;	r->rchan_shift = (val >> 12) & 0x0f;	r->mask = (val >> 16) & 0xff;	r->invert = (val >> 24) & 1;	r->stereo = (val >> 25) & 1;	r->enum_c = (val >> 26) & 0x0f;}/* * mixer switches/volumes */#define AZF3328_MIXER_SWITCH(xname, reg, shift, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \  .info = snd_azf3328_info_mixer, \  .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \  .private_value = COMPOSE_MIXER_REG(reg, shift, 0, 0x1, invert, 0, 0), \}#define AZF3328_MIXER_VOL_STEREO(xname, reg, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \  .info = snd_azf3328_info_mixer, \  .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \  .private_value = COMPOSE_MIXER_REG(reg, 8, 0, mask, invert, 1, 0), \}#define AZF3328_MIXER_VOL_MONO(xname, reg, mask, is_right_chan) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \  .info = snd_azf3328_info_mixer, \  .get = snd_azf3328_get_mixer, .put = snd_azf3328_put_mixer, \  .private_value = COMPOSE_MIXER_REG(reg, is_right_chan ? 0 : 8, 0, mask, 1, 0, 0), \}#define AZF3328_MIXER_VOL_SPECIAL(xname, reg, mask, shift, invert) \

⌨️ 快捷键说明

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