es18xx.c

来自「linux 内核源代码」· C语言 代码 · 共 2,181 行 · 第 1/5 页

C
2,181
字号
/* * The chipset specific mixer controls */static struct snd_kcontrol_new snd_es18xx_opt_speaker =	ES18XX_SINGLE("PC Speaker Playback Volume", 0, 0x3c, 0, 7, 0);static struct snd_kcontrol_new snd_es18xx_opt_1869[] = {ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),ES18XX_SINGLE("Video Playback Switch", 0, 0x7f, 0, 1, 0),ES18XX_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),ES18XX_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_opt_1878 =	ES18XX_DOUBLE("Video Playback Volume", 0, 0x68, 0x68, 4, 0, 15, 0);static struct snd_kcontrol_new snd_es18xx_opt_1879[] = {ES18XX_SINGLE("Video Playback Switch", 0, 0x71, 6, 1, 0),ES18XX_DOUBLE("Video Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),ES18XX_DOUBLE("Video Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_pcm1_controls[] = {ES18XX_DOUBLE("PCM Playback Volume", 0, 0x14, 0x14, 4, 0, 15, 0),};static struct snd_kcontrol_new snd_es18xx_pcm2_controls[] = {ES18XX_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),ES18XX_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0)};static struct snd_kcontrol_new snd_es18xx_spatializer_controls[] = {ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "3D Control - Switch",	.info = snd_es18xx_info_spatializer_enable,	.get = snd_es18xx_get_spatializer_enable,	.put = snd_es18xx_put_spatializer_enable,}};static struct snd_kcontrol_new snd_es18xx_micpre1_control = ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0xa9, 2, 1, 0);static struct snd_kcontrol_new snd_es18xx_micpre2_control =ES18XX_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0);static struct snd_kcontrol_new snd_es18xx_hw_volume_controls[] = {{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Hardware Master Playback Volume",	.access = SNDRV_CTL_ELEM_ACCESS_READ,	.info = snd_es18xx_info_hw_volume,	.get = snd_es18xx_get_hw_volume,},{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,	.name = "Hardware Master Playback Switch",	.access = SNDRV_CTL_ELEM_ACCESS_READ,	.info = snd_es18xx_info_hw_switch,	.get = snd_es18xx_get_hw_switch,},ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),};static int __devinit snd_es18xx_config_read(struct snd_es18xx *chip, unsigned char reg){	int data;	unsigned long flags;        spin_lock_irqsave(&chip->ctrl_lock, flags);	outb(reg, chip->ctrl_port);	data = inb(chip->ctrl_port + 1);        spin_unlock_irqrestore(&chip->ctrl_lock, flags);	return data;}static void __devinit snd_es18xx_config_write(struct snd_es18xx *chip, 					      unsigned char reg, unsigned char data){	/* No need for spinlocks, this function is used only in	   otherwise protected init code */	outb(reg, chip->ctrl_port);	outb(data, chip->ctrl_port + 1);#ifdef REG_DEBUG	snd_printk(KERN_DEBUG "Config reg %02x set to %02x\n", reg, data);#endif}static int __devinit snd_es18xx_initialize(struct snd_es18xx *chip){	int mask = 0;        /* enable extended mode */        snd_es18xx_dsp_command(chip, 0xC6);	/* Reset mixer registers */	snd_es18xx_mixer_write(chip, 0x00, 0x00);        /* Audio 1 DMA demand mode (4 bytes/request) */        snd_es18xx_write(chip, 0xB9, 2);	if (chip->caps & ES18XX_CONTROL) {		/* Hardware volume IRQ */		snd_es18xx_config_write(chip, 0x27, chip->irq);		if (chip->fm_port > 0 && chip->fm_port != SNDRV_AUTO_PORT) {			/* FM I/O */			snd_es18xx_config_write(chip, 0x62, chip->fm_port >> 8);			snd_es18xx_config_write(chip, 0x63, chip->fm_port & 0xff);		}		if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {			/* MPU-401 I/O */			snd_es18xx_config_write(chip, 0x64, chip->mpu_port >> 8);			snd_es18xx_config_write(chip, 0x65, chip->mpu_port & 0xff);			/* MPU-401 IRQ */			snd_es18xx_config_write(chip, 0x28, chip->irq);		}		/* Audio1 IRQ */		snd_es18xx_config_write(chip, 0x70, chip->irq);		/* Audio2 IRQ */		snd_es18xx_config_write(chip, 0x72, chip->irq);		/* Audio1 DMA */		snd_es18xx_config_write(chip, 0x74, chip->dma1);		/* Audio2 DMA */		snd_es18xx_config_write(chip, 0x75, chip->dma2);		/* Enable Audio 1 IRQ */		snd_es18xx_write(chip, 0xB1, 0x50);		/* Enable Audio 2 IRQ */		snd_es18xx_mixer_write(chip, 0x7A, 0x40);		/* Enable Audio 1 DMA */		snd_es18xx_write(chip, 0xB2, 0x50);		/* Enable MPU and hardware volume interrupt */		snd_es18xx_mixer_write(chip, 0x64, 0x42);	}	else {		int irqmask, dma1mask, dma2mask;		switch (chip->irq) {		case 2:		case 9:			irqmask = 0;			break;		case 5:			irqmask = 1;			break;		case 7:			irqmask = 2;			break;		case 10:			irqmask = 3;			break;		default:			snd_printk(KERN_ERR "invalid irq %d\n", chip->irq);			return -ENODEV;		}		switch (chip->dma1) {		case 0:			dma1mask = 1;			break;		case 1:			dma1mask = 2;			break;		case 3:			dma1mask = 3;			break;		default:			snd_printk(KERN_ERR "invalid dma1 %d\n", chip->dma1);			return -ENODEV;		}		switch (chip->dma2) {		case 0:			dma2mask = 0;			break;		case 1:			dma2mask = 1;			break;		case 3:			dma2mask = 2;			break;		case 5:			dma2mask = 3;			break;		default:			snd_printk(KERN_ERR "invalid dma2 %d\n", chip->dma2);			return -ENODEV;		}		/* Enable and set Audio 1 IRQ */		snd_es18xx_write(chip, 0xB1, 0x50 | (irqmask << 2));		/* Enable and set Audio 1 DMA */		snd_es18xx_write(chip, 0xB2, 0x50 | (dma1mask << 2));		/* Set Audio 2 DMA */		snd_es18xx_mixer_bits(chip, 0x7d, 0x07, 0x04 | dma2mask);		/* Enable Audio 2 IRQ and DMA		   Set capture mixer input */		snd_es18xx_mixer_write(chip, 0x7A, 0x68);		/* Enable and set hardware volume interrupt */		snd_es18xx_mixer_write(chip, 0x64, 0x06);		if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {			/* MPU401 share irq with audio			   Joystick enabled			   FM enabled */			snd_es18xx_mixer_write(chip, 0x40, 0x43 | (chip->mpu_port & 0xf0) >> 1);		}		snd_es18xx_mixer_write(chip, 0x7f, ((irqmask + 1) << 1) | 0x01);	}	if (chip->caps & ES18XX_NEW_RATE) {		/* Change behaviour of register A1		   4x oversampling		   2nd channel DAC asynchronous */		snd_es18xx_mixer_write(chip, 0x71, 0x32);	}	if (!(chip->caps & ES18XX_PCM2)) {		/* Enable DMA FIFO */		snd_es18xx_write(chip, 0xB7, 0x80);	}	if (chip->caps & ES18XX_SPATIALIZER) {		/* Set spatializer parameters to recommended values */		snd_es18xx_mixer_write(chip, 0x54, 0x8f);		snd_es18xx_mixer_write(chip, 0x56, 0x95);		snd_es18xx_mixer_write(chip, 0x58, 0x94);		snd_es18xx_mixer_write(chip, 0x5a, 0x80);	}	/* Flip the "enable I2S" bits for those chipsets that need it */	switch (chip->version) {	case 0x1879:		//Leaving I2S enabled on the 1879 screws up the PCM playback (rate effected somehow)		//so a Switch control has been added to toggle this 0x71 bit on/off:		//snd_es18xx_mixer_bits(chip, 0x71, 0x40, 0x40);		/* Note: we fall through on purpose here. */	case 0x1878:		snd_es18xx_config_write(chip, 0x29, snd_es18xx_config_read(chip, 0x29) | 0x40);		break;	}	/* Mute input source */	if (chip->caps & ES18XX_MUTEREC)		mask = 0x10;	if (chip->caps & ES18XX_RECMIX)		snd_es18xx_mixer_write(chip, 0x1c, 0x05 | mask);	else {		snd_es18xx_mixer_write(chip, 0x1c, 0x00 | mask);		snd_es18xx_write(chip, 0xb4, 0x00);	}#ifndef AVOID_POPS	/* Enable PCM output */	snd_es18xx_dsp_command(chip, 0xD1);#endif        return 0;}static int __devinit snd_es18xx_identify(struct snd_es18xx *chip){	int hi,lo;	/* reset */	if (snd_es18xx_reset(chip) < 0) {		snd_printk(KERN_ERR "reset at 0x%lx failed!!!\n", chip->port);		return -ENODEV;	}	snd_es18xx_dsp_command(chip, 0xe7);	hi = snd_es18xx_dsp_get_byte(chip);	if (hi < 0) {		return hi;	}	lo = snd_es18xx_dsp_get_byte(chip);	if ((lo & 0xf0) != 0x80) {		return -ENODEV;	}	if (hi == 0x48) {		chip->version = 0x488;		return 0;	}	if (hi != 0x68) {		return -ENODEV;	}	if ((lo & 0x0f) < 8) {		chip->version = 0x688;		return 0;	}			        outb(0x40, chip->port + 0x04);	udelay(10);	hi = inb(chip->port + 0x05);	udelay(10);	lo = inb(chip->port + 0x05);	if (hi != lo) {		chip->version = hi << 8 | lo;		chip->ctrl_port = inb(chip->port + 0x05) << 8;		udelay(10);		chip->ctrl_port += inb(chip->port + 0x05);		if ((chip->res_ctrl_port = request_region(chip->ctrl_port, 8, "ES18xx - CTRL")) == NULL) {			snd_printk(KERN_ERR PFX "unable go grab port 0x%lx\n", chip->ctrl_port);			return -EBUSY;		}		return 0;	}	/* If has Hardware volume */	if (snd_es18xx_mixer_writable(chip, 0x64, 0x04)) {		/* If has Audio2 */		if (snd_es18xx_mixer_writable(chip, 0x70, 0x7f)) {			/* If has volume count */			if (snd_es18xx_mixer_writable(chip, 0x64, 0x20)) {				chip->version = 0x1887;			} else {				chip->version = 0x1888;			}		} else {			chip->version = 0x1788;		}	}	else		chip->version = 0x1688;	return 0;}static int __devinit snd_es18xx_probe(struct snd_es18xx *chip){	if (snd_es18xx_identify(chip) < 0) {		snd_printk(KERN_ERR PFX "[0x%lx] ESS chip not found\n", chip->port);                return -ENODEV;	}	switch (chip->version) {	case 0x1868:		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_CONTROL;		break;	case 0x1869:		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_MONO | ES18XX_MUTEREC | ES18XX_CONTROL | ES18XX_HWV;		break;	case 0x1878:		chip->caps = ES18XX_DUPLEX_MONO | ES18XX_DUPLEX_SAME | ES18XX_I2S | ES18XX_CONTROL;		break;	case 0x1879:		chip->caps = ES18XX_PCM2 | ES18XX_SPATIALIZER | ES18XX_RECMIX | ES18XX_NEW_RATE | ES18XX_AUXB | ES18XX_I2S | ES18XX_CONTROL | ES18XX_HWV;		break;	case 0x1887:		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;		break;	case 0x1888:		chip->caps = ES18XX_PCM2 | ES18XX_RECMIX | ES18XX_AUXB | ES18XX_DUPLEX_SAME;		break;	default:		snd_printk(KERN_ERR "[0x%lx] unsupported chip ES%x\n",                           chip->port, chip->version);                return -ENODEV;        }        snd_printd("[0x%lx] ESS%x chip found\n", chip->port, chip->version);	if (chip->dma1 == chip->dma2)		chip->caps &= ~(ES18XX_PCM2 | ES18XX_DUPLEX_SAME);        return snd_es18xx_initialize(chip);}static struct snd_pcm_ops snd_es18xx_playback_ops = {	.open =		snd_es18xx_playback_open,	.close =	snd_es18xx_playback_close,	.ioctl =	snd_pcm_lib_ioctl,	.hw_params =	snd_es18xx_playback_hw_params,	.hw_free =	snd_es18xx_pcm_hw_free,	.prepare =	snd_es18xx_playback_prepare,	.trigger =	snd_es18xx_playback_trigger,	.pointer =	snd_es18xx_playback_pointer,};static struct snd_pcm_ops snd_es18xx_capture_ops = {	.open =		snd_es18xx_capture_open,	.close =	snd_es18xx_capture_close,	.ioctl =	snd_pcm_lib_ioctl,	.hw_params =	snd_es18xx_capture_hw_params,	.hw_free =	snd_es18xx_pcm_hw_free,	.prepare =	snd_es18xx_capture_prepare,	.trigger =	snd_es18xx_capture_trigger,	.pointer =	snd_es18xx_capture_pointer,};static int __devinit snd_es18xx_pcm(struct snd_es18xx *chip, int device, struct snd_pcm ** rpcm){        struct snd_pcm *pcm;	char str[16];	int err;	if (rpcm)		*rpcm = NULL;	sprintf(str, "ES%x", chip->version);	if (chip->caps & ES18XX_PCM2)		err = snd_pcm_new(chip->card, str, device, 2, 1, &pcm);	else		err = snd_pcm_new(chip->card, str, device, 1, 1, &pcm);        if (err < 0)                return err;	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_es18xx_playback_ops);	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_es18xx_capture_ops);	/* global setup */        pcm->private_data = chip;        pcm->info_flags = 0;	if (chip->caps & ES18XX_DUPLEX_SAME)		pcm->info_flags |= SNDRV_PCM_INFO_JOINT_DUPLEX;	if (! (chip->caps & ES18XX_PCM2))		pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;	sprintf(pcm->name, "ESS AudioDrive ES%x", chip->version);        chip->pcm = pcm;	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,					      snd_dma_isa_data(),					      64*1024,					      chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024);        if (rpcm)        	*rpcm = pcm;	return 0;}/* Power Management support functions */#ifdef CONFIG_PMstatic int snd_es18xx_suspend(struct snd_card *card, pm_message_t state){	struct snd_audiodrive *acard = card->private_data;	struct snd_es18xx *chip = acard->chip;	snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);	snd_pcm_suspend_all(chip->pcm);	/* power down */	chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM);	chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS);	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg);	snd_es18xx_write(chip, ES18XX_PM, chip->pm_reg ^= ES18XX_PM_SUS);

⌨️ 快捷键说明

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