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

📄 es1938.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>, *                   Jaroslav Kysela <perex@perex.cz>, *                   Thomas Sailer <sailer@ife.ee.ethz.ch>, *                   Abramo Bagnara <abramo@alsa-project.org>, *                   Markus Gruber <gruber@eikon.tum.de> *  * Rewritten from sonicvibes.c source. * *  TODO: *    Rewrite better spinlocks * * *   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:  - Capture data is written unaligned starting from dma_base + 1 so I need to    disable mmap and to add a copy callback.  - After several cycle of the following:    while : ; do arecord -d1 -f cd -t raw | aplay -f cd ; done    a "playback write error (DMA or IRQ trouble?)" may happen.    This is due to playback interrupts not generated.    I suspect a timing issue.  - Sometimes the interrupt handler is invoked wrongly during playback.    This generates some harmless "Unexpected hw_pointer: wrong interrupt    acknowledge".    I've seen that using small period sizes.    Reproducible with:    mpg123 test.mp3 &    hdparm -t -T /dev/hda*/#include <sound/driver.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/gameport.h>#include <linux/moduleparam.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/opl3.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/tlv.h>#include <asm/io.h>MODULE_AUTHOR("Jaromir Koutek <miri@punknet.cz>");MODULE_DESCRIPTION("ESS Solo-1");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ESS,ES1938},"                "{ESS,ES1946},"                "{ESS,ES1969},"		"{TerraTec,128i PCI}}");#if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))#define SUPPORT_JOYSTICK 1#endifstatic 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 */module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for ESS Solo-1 soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for ESS Solo-1 soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable ESS Solo-1 soundcard.");#define SLIO_REG(chip, x) ((chip)->io_port + ESSIO_REG_##x)#define SLDM_REG(chip, x) ((chip)->ddma_port + ESSDM_REG_##x)#define SLSB_REG(chip, x) ((chip)->sb_port + ESSSB_REG_##x)#define SL_PCI_LEGACYCONTROL		0x40#define SL_PCI_CONFIG			0x50#define SL_PCI_DDMACONTROL		0x60#define ESSIO_REG_AUDIO2DMAADDR		0#define ESSIO_REG_AUDIO2DMACOUNT	4#define ESSIO_REG_AUDIO2MODE		6#define ESSIO_REG_IRQCONTROL		7#define ESSDM_REG_DMAADDR		0x00#define ESSDM_REG_DMACOUNT		0x04#define ESSDM_REG_DMACOMMAND		0x08#define ESSDM_REG_DMASTATUS		0x08#define ESSDM_REG_DMAMODE		0x0b#define ESSDM_REG_DMACLEAR		0x0d#define ESSDM_REG_DMAMASK		0x0f#define ESSSB_REG_FMLOWADDR		0x00#define ESSSB_REG_FMHIGHADDR		0x02#define ESSSB_REG_MIXERADDR		0x04#define ESSSB_REG_MIXERDATA		0x05#define ESSSB_IREG_AUDIO1		0x14#define ESSSB_IREG_MICMIX		0x1a#define ESSSB_IREG_RECSRC		0x1c#define ESSSB_IREG_MASTER		0x32#define ESSSB_IREG_FM			0x36#define ESSSB_IREG_AUXACD		0x38#define ESSSB_IREG_AUXB			0x3a#define ESSSB_IREG_PCSPEAKER		0x3c#define ESSSB_IREG_LINE			0x3e#define ESSSB_IREG_SPATCONTROL		0x50#define ESSSB_IREG_SPATLEVEL		0x52#define ESSSB_IREG_MASTER_LEFT		0x60#define ESSSB_IREG_MASTER_RIGHT		0x62#define ESSSB_IREG_MPU401CONTROL	0x64#define ESSSB_IREG_MICMIXRECORD		0x68#define ESSSB_IREG_AUDIO2RECORD		0x69#define ESSSB_IREG_AUXACDRECORD		0x6a#define ESSSB_IREG_FMRECORD		0x6b#define ESSSB_IREG_AUXBRECORD		0x6c#define ESSSB_IREG_MONO			0x6d#define ESSSB_IREG_LINERECORD		0x6e#define ESSSB_IREG_MONORECORD		0x6f#define ESSSB_IREG_AUDIO2SAMPLE		0x70#define ESSSB_IREG_AUDIO2MODE		0x71#define ESSSB_IREG_AUDIO2FILTER		0x72#define ESSSB_IREG_AUDIO2TCOUNTL	0x74#define ESSSB_IREG_AUDIO2TCOUNTH	0x76#define ESSSB_IREG_AUDIO2CONTROL1	0x78#define ESSSB_IREG_AUDIO2CONTROL2	0x7a#define ESSSB_IREG_AUDIO2		0x7c#define ESSSB_REG_RESET			0x06#define ESSSB_REG_READDATA		0x0a#define ESSSB_REG_WRITEDATA		0x0c#define ESSSB_REG_READSTATUS		0x0c#define ESSSB_REG_STATUS		0x0e#define ESS_CMD_EXTSAMPLERATE		0xa1#define ESS_CMD_FILTERDIV		0xa2#define ESS_CMD_DMACNTRELOADL		0xa4#define ESS_CMD_DMACNTRELOADH		0xa5#define ESS_CMD_ANALOGCONTROL		0xa8#define ESS_CMD_IRQCONTROL		0xb1#define ESS_CMD_DRQCONTROL		0xb2#define ESS_CMD_RECLEVEL		0xb4#define ESS_CMD_SETFORMAT		0xb6#define ESS_CMD_SETFORMAT2		0xb7#define ESS_CMD_DMACONTROL		0xb8#define ESS_CMD_DMATYPE			0xb9#define ESS_CMD_OFFSETLEFT		0xba	#define ESS_CMD_OFFSETRIGHT		0xbb#define ESS_CMD_READREG			0xc0#define ESS_CMD_ENABLEEXT		0xc6#define ESS_CMD_PAUSEDMA		0xd0#define ESS_CMD_ENABLEAUDIO1		0xd1#define ESS_CMD_STOPAUDIO1		0xd3#define ESS_CMD_AUDIO1STATUS		0xd8#define ESS_CMD_CONTDMA			0xd4#define ESS_CMD_TESTIRQ			0xf2#define ESS_RECSRC_MIC		0#define ESS_RECSRC_AUXACD	2#define ESS_RECSRC_AUXB		5#define ESS_RECSRC_LINE		6#define ESS_RECSRC_NONE		7#define DAC1 0x01#define ADC1 0x02#define DAC2 0x04/* */#define SAVED_REG_SIZE	32 /* max. number of registers to save */struct es1938 {	int irq;	unsigned long io_port;	unsigned long sb_port;	unsigned long vc_port;	unsigned long mpu_port;	unsigned long game_port;	unsigned long ddma_port;	unsigned char irqmask;	unsigned char revision;	struct snd_kcontrol *hw_volume;	struct snd_kcontrol *hw_switch;	struct snd_kcontrol *master_volume;	struct snd_kcontrol *master_switch;	struct pci_dev *pci;	struct snd_card *card;	struct snd_pcm *pcm;	struct snd_pcm_substream *capture_substream;	struct snd_pcm_substream *playback1_substream;	struct snd_pcm_substream *playback2_substream;	struct snd_rawmidi *rmidi;	unsigned int dma1_size;	unsigned int dma2_size;	unsigned int dma1_start;	unsigned int dma2_start;	unsigned int dma1_shift;	unsigned int dma2_shift;	unsigned int active;	spinlock_t reg_lock;	spinlock_t mixer_lock;        struct snd_info_entry *proc_entry;#ifdef SUPPORT_JOYSTICK	struct gameport *gameport;#endif#ifdef CONFIG_PM	unsigned char saved_regs[SAVED_REG_SIZE];#endif};static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);static struct pci_device_id snd_es1938_ids[] = {        { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */	{ 0, }};MODULE_DEVICE_TABLE(pci, snd_es1938_ids);#define RESET_LOOP_TIMEOUT	0x10000#define WRITE_LOOP_TIMEOUT	0x10000#define GET_LOOP_TIMEOUT	0x01000#undef REG_DEBUG/* ----------------------------------------------------------------- * Write to a mixer register * -----------------------------------------------------------------*/static void snd_es1938_mixer_write(struct es1938 *chip, unsigned char reg, unsigned char val){	unsigned long flags;	spin_lock_irqsave(&chip->mixer_lock, flags);	outb(reg, SLSB_REG(chip, MIXERADDR));	outb(val, SLSB_REG(chip, MIXERDATA));	spin_unlock_irqrestore(&chip->mixer_lock, flags);#ifdef REG_DEBUG	snd_printk(KERN_DEBUG "Mixer reg %02x set to %02x\n", reg, val);#endif}/* ----------------------------------------------------------------- * Read from a mixer register * -----------------------------------------------------------------*/static int snd_es1938_mixer_read(struct es1938 *chip, unsigned char reg){	int data;	unsigned long flags;	spin_lock_irqsave(&chip->mixer_lock, flags);	outb(reg, SLSB_REG(chip, MIXERADDR));	data = inb(SLSB_REG(chip, MIXERDATA));	spin_unlock_irqrestore(&chip->mixer_lock, flags);#ifdef REG_DEBUG	snd_printk(KERN_DEBUG "Mixer reg %02x now is %02x\n", reg, data);#endif	return data;}/* ----------------------------------------------------------------- * Write to some bits of a mixer register (return old value) * -----------------------------------------------------------------*/static int snd_es1938_mixer_bits(struct es1938 *chip, unsigned char reg,				 unsigned char mask, unsigned char val){	unsigned long flags;	unsigned char old, new, oval;	spin_lock_irqsave(&chip->mixer_lock, flags);	outb(reg, SLSB_REG(chip, MIXERADDR));	old = inb(SLSB_REG(chip, MIXERDATA));	oval = old & mask;	if (val != oval) {		new = (old & ~mask) | (val & mask);		outb(new, SLSB_REG(chip, MIXERDATA));#ifdef REG_DEBUG		snd_printk(KERN_DEBUG "Mixer reg %02x was %02x, set to %02x\n",			   reg, old, new);#endif	}	spin_unlock_irqrestore(&chip->mixer_lock, flags);	return oval;}/* ----------------------------------------------------------------- * Write command to Controller Registers * -----------------------------------------------------------------*/static void snd_es1938_write_cmd(struct es1938 *chip, unsigned char cmd){	int i;	unsigned char v;	for (i = 0; i < WRITE_LOOP_TIMEOUT; i++) {		if (!(v = inb(SLSB_REG(chip, READSTATUS)) & 0x80)) {			outb(cmd, SLSB_REG(chip, WRITEDATA));			return;		}	}	printk(KERN_ERR "snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);}/* ----------------------------------------------------------------- * Read the Read Data Buffer * -----------------------------------------------------------------*/static int snd_es1938_get_byte(struct es1938 *chip){	int i;	unsigned char v;	for (i = GET_LOOP_TIMEOUT; i; i--)		if ((v = inb(SLSB_REG(chip, STATUS))) & 0x80)			return inb(SLSB_REG(chip, READDATA));	snd_printk(KERN_ERR "get_byte timeout: status 0x02%x\n", v);	return -ENODEV;}/* ----------------------------------------------------------------- * Write value cmd register * -----------------------------------------------------------------*/static void snd_es1938_write(struct es1938 *chip, unsigned char reg, unsigned char val){	unsigned long flags;	spin_lock_irqsave(&chip->reg_lock, flags);	snd_es1938_write_cmd(chip, reg);	snd_es1938_write_cmd(chip, val);	spin_unlock_irqrestore(&chip->reg_lock, flags);#ifdef REG_DEBUG	snd_printk(KERN_DEBUG "Reg %02x set to %02x\n", reg, val);#endif}/* ----------------------------------------------------------------- * Read data from cmd register and return it * -----------------------------------------------------------------*/static unsigned char snd_es1938_read(struct es1938 *chip, unsigned char reg){	unsigned char val;	unsigned long flags;	spin_lock_irqsave(&chip->reg_lock, flags);	snd_es1938_write_cmd(chip, ESS_CMD_READREG);	snd_es1938_write_cmd(chip, reg);	val = snd_es1938_get_byte(chip);	spin_unlock_irqrestore(&chip->reg_lock, flags);#ifdef REG_DEBUG	snd_printk(KERN_DEBUG "Reg %02x now is %02x\n", reg, val);#endif	return val;}/* ----------------------------------------------------------------- * Write data to cmd register and return old value * -----------------------------------------------------------------*/static int snd_es1938_bits(struct es1938 *chip, unsigned char reg, unsigned char mask,			   unsigned char val){	unsigned long flags;	unsigned char old, new, oval;	spin_lock_irqsave(&chip->reg_lock, flags);	snd_es1938_write_cmd(chip, ESS_CMD_READREG);	snd_es1938_write_cmd(chip, reg);	old = snd_es1938_get_byte(chip);	oval = old & mask;	if (val != oval) {		snd_es1938_write_cmd(chip, reg);		new = (old & ~mask) | (val & mask);		snd_es1938_write_cmd(chip, new);#ifdef REG_DEBUG		snd_printk(KERN_DEBUG "Reg %02x was %02x, set to %02x\n",			   reg, old, new);#endif	}	spin_unlock_irqrestore(&chip->reg_lock, flags);	return oval;}/* -------------------------------------------------------------------- * Reset the chip * --------------------------------------------------------------------*/static void snd_es1938_reset(struct es1938 *chip){	int i;	outb(3, SLSB_REG(chip, RESET));	inb(SLSB_REG(chip, RESET));	outb(0, SLSB_REG(chip, RESET));	for (i = 0; i < RESET_LOOP_TIMEOUT; i++) {		if (inb(SLSB_REG(chip, STATUS)) & 0x80) {			if (inb(SLSB_REG(chip, READDATA)) == 0xaa)				goto __next;		}	}	snd_printk(KERN_ERR "ESS Solo-1 reset failed\n");     __next:	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEEXT);	/* Demand transfer DMA: 4 bytes per DMA request */	snd_es1938_write(chip, ESS_CMD_DMATYPE, 2);	/* Change behaviour of register A1	   4x oversampling	   2nd channel DAC asynchronous */                                                      	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2MODE, 0x32);	/* enable/select DMA channel and IRQ channel */	snd_es1938_bits(chip, ESS_CMD_IRQCONTROL, 0xf0, 0x50);	snd_es1938_bits(chip, ESS_CMD_DRQCONTROL, 0xf0, 0x50);	snd_es1938_write_cmd(chip, ESS_CMD_ENABLEAUDIO1);	/* Set spatializer parameters to recommended values */	snd_es1938_mixer_write(chip, 0x54, 0x8f);	snd_es1938_mixer_write(chip, 0x56, 0x95);	snd_es1938_mixer_write(chip, 0x58, 0x94);	snd_es1938_mixer_write(chip, 0x5a, 0x80);}/* -------------------------------------------------------------------- * Reset the FIFOs * --------------------------------------------------------------------*/static void snd_es1938_reset_fifo(struct es1938 *chip){	outb(2, SLSB_REG(chip, RESET));	outb(0, SLSB_REG(chip, RESET));}static struct snd_ratnum clocks[2] = {	{		.num = 793800,		.den_min = 1,		.den_max = 128,		.den_step = 1,	},	{		.num = 768000,		.den_min = 1,		.den_max = 128,		.den_step = 1,	}};static struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {	.nrats = 2,	.rats = clocks,};

⌨️ 快捷键说明

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