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

📄 es1938.cpp

📁 devloped under vxwork, support ess sound card. driver library, include datasheet.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
{
	es1938_t *chip = (es1938_t *) device->device_data;
	return snd_es1938_free(chip);
}


/* -----------------------------------------------------------------
 * Write command to Controller Registers
 * -----------------------------------------------------------------*/
static void snd_es1938_write_cmd(es1938_t *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("snd_es1938_write_cmd timeout (0x02%x/0x02%x)\n", cmd, v);
}

/* -----------------------------------------------------------------
 * Read the Read Data Buffer
 * -----------------------------------------------------------------*/
static int snd_es1938_get_byte(es1938_t *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("get_byte timeout: status 0x02%x\n", v);
	return -ENODEV;
}
/* -----------------------------------------------------------------
 * read value cmd register
 * -----------------------------------------------------------------*/
static unsigned char snd_es1938_read(es1938_t *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("Reg %02x now is %02x\n", reg, val);
#endif
	return val;
}

/* -----------------------------------------------------------------
 * Write value cmd register
 * -----------------------------------------------------------------*/
static void snd_es1938_write(es1938_t *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("Reg %02x set to %02x\n", reg, val);
#endif
}

/* -----------------------------------------------------------------
 * Write to a mixer register
 * -----------------------------------------------------------------*/
static void snd_es1938_mixer_write(es1938_t *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_interrupt("Mixer reg %02x set to %02x\n", reg, val);
#endif
}

/* -----------------------------------------------------------------
 * Write data to cmd register and return old value
 * -----------------------------------------------------------------*/
static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val)
{
	unsigned long flags;
	unsigned char old, newval, 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);
		newval = (old & ~mask) | (val & mask);
		snd_es1938_write_cmd(chip, newval);
#ifdef REG_DEBUG
		snd_printk("Reg %02x was %02x, set to %02x\n", reg, old, newval);
#endif
	}
	spin_unlock_irqrestore(&chip->reg_lock, flags);
	return oval;
}


static void snd_es1938_reset(es1938_t *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("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);
}


/*
 * initialize the chip - used by resume callback, too
 */
static void snd_es1938_chip_init(es1938_t *chip)
{
	/* reset chip */
	snd_es1938_reset(chip);

	/* configure native mode */

	/* enable bus master */
	pci_set_master(chip->pci);

	/* disable legacy audio */
	pci_write_config_word(chip->pci, SL_PCI_LEGACYCONTROL, 0x805f);

	/* set DDMA base */
	pci_write_config_word(chip->pci, SL_PCI_DDMACONTROL, chip->ddma_port | 1);

	/* set DMA/IRQ policy */
	pci_write_config_dword(chip->pci, SL_PCI_CONFIG, 0);

	/* enable Audio 1, Audio 2, MPU401 IRQ and HW volume IRQ*/
	outb(0xf0, SLIO_REG(chip, IRQCONTROL));

	/* reset DMA */
	outb(0, SLDM_REG(chip, DMACLEAR));
}


static snd_device_ops_t dev_ops;

static int __devinit snd_es1938_create(snd_card_t * card,
				    struct pci_dev * pci,
				    es1938_t ** rchip)
{
	es1938_t *chip;
	int err;

	dev_ops.dev_free = snd_es1938_dev_free;

	*rchip = NULL;

	/* enable PCI device */
	if ((err = pci_enable_device(pci)) < 0)
		return err;

        /* check, if we can restrict PCI DMA transfers to 24 bits */
	if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
	    pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
                snd_printk("architecture does not support 24bit PCI busmaster DMA\n");
		pci_disable_device(pci);
                return -ENXIO;
        }

	chip = (es1938_t *)kcalloc(1, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL) {
		pci_disable_device(pci);
		return -ENOMEM;
	}
	spin_lock_init(&chip->reg_lock);
	spin_lock_init(&chip->mixer_lock);
	chip->card = card;
	chip->pci = pci;

	pGlobChip = chip;
	
	if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
		kfree(chip);
		pci_disable_device(pci);
		return err;
	}

	chip->io_port = pci_resource_start(pci, 0);
	chip->sb_port = pci_resource_start(pci, 1);
	chip->vc_port = pci_resource_start(pci, 2);
	chip->mpu_port = pci_resource_start(pci, 3);
	chip->game_port = pci_resource_start(pci, 4);

	if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ, "ES1938", (void *)chip)) {
		snd_printk("unable to grab IRQ %d\n", pci->irq);
		snd_es1938_free(chip);
		return -EBUSY;
	}

	chip->irq = pci->irq;

	snd_printk("create: io: 0x%lx, sb: 0x%lx, vc: 0x%lx, mpu: 0x%lx, game: 0x%lx\n",
		   chip->io_port, chip->sb_port, chip->vc_port, chip->mpu_port, chip->game_port);

	chip->ddma_port = chip->vc_port + 0x00;		/* fix from Thomas Sailer */

	snd_es1938_chip_init(chip);

	snd_card_set_pm_callback(card, es1938_suspend, es1938_resume, chip);

	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &dev_ops)) < 0) {
		snd_es1938_free(chip);
		return err;
	}

	snd_card_set_dev(card, &pci->dev);

	
	*rchip = chip;
	return 0;
}

/* -----------------------------------------------------------------
 * Read from a mixer register
 * -----------------------------------------------------------------*/
static int snd_es1938_mixer_read(es1938_t *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("Mixer reg %02x now is %02x\n", reg, data);
#endif
	return data;
}

static int __devinit snd_es1938_mixer(es1938_t *chip)
{
	snd_card_t *card;
	unsigned int idx;
	int err;

	card = chip->card;

	strcpy(card->mixername, "ESS Solo-1");

#if VXWORKS_UNUSED
	for (idx = 0; idx < ARRAY_SIZE(snd_es1938_controls); idx++) {
		snd_kcontrol_t *kctl;
		kctl = snd_ctl_new1(&snd_es1938_controls[idx], chip);
		switch (idx) {
			case 0:
				chip->master_volume = kctl;
				kctl->private_free = snd_es1938_hwv_free;
				break;
			case 1:
				chip->master_switch = kctl;
				kctl->private_free = snd_es1938_hwv_free;
				break;
			case 2:
				chip->hw_volume = kctl;
				kctl->private_free = snd_es1938_hwv_free;
				break;
			case 3:
				chip->hw_switch = kctl;
				kctl->private_free = snd_es1938_hwv_free;
				break;
			}
		if ((err = snd_ctl_add(card, kctl)) < 0)
			return err;
	}
#endif
	return 0;
}


static int __devinit snd_es1938_probe(struct pci_dev *pci,
				      const struct pci_device_id *pci_id)
{
	static int dev=0;
	snd_card_t *card;
	es1938_t *chip;
#if ES1938_INCLUDE_OPL_FMSYNTH
	opl3_t *opl3;
#endif
	int idx, err;

	if (dev >= SNDRV_CARDS)
		return -ENODEV;
	if (!enable[dev]) {
		dev++;
		return -ENOENT;
	}

	card = snd_card_new(a_index[dev], id[dev], THIS_MODULE, 0);
	if (card == NULL)
		return -ENOMEM;
	for (idx = 0; idx < 5; idx++) {
		if (pci_resource_start(pci, idx) == 0 ||
		    !(pci_resource_flags(pci, idx) & IORESOURCE_IO)) {
		    	snd_card_free(card);
		    	return -ENODEV;
		}
	}
	if ((err = snd_es1938_create(card, pci, &chip)) < 0) {
		snd_card_free(card);
		return err;
	}

	strcpy(card->driver, "ES1938");
	strcpy(card->shortname, "ESS ES1938 (Solo-1)");
	sprintf(card->longname, "%s rev %i, irq %i",
		card->shortname,
		chip->revision,
		chip->irq);

	if ((err = snd_es1938_new_pcm(chip, 0)) < 0) {
		snd_card_free(card);
		return err;
	}
	if ((err = snd_es1938_mixer(chip)) < 0) {
		snd_card_free(card);
		return err;
	}
#if ES1938_INCLUDE_OPL_FMSYNTH
	if (snd_opl3_create(card,
			    SLSB_REG(chip, FMLOWADDR),
			    SLSB_REG(chip, FMHIGHADDR),
			    OPL3_HW_OPL3, 1, &opl3) < 0) {
		printk(KERN_ERR "es1938: OPL3 not detected at 0x%lx\n",
			   SLSB_REG(chip, FMLOWADDR));
	} else {
	        if ((err = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
	                snd_card_free(card);
	                return err;
		}
	        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
	                snd_card_free(card);
	                return err;
		}
	}
#endif

#if ES1938_INCLUDE_MP401_UART
	if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
				chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
		printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
	} else {
		// this line is vital for MIDI interrupt handling on ess-solo1
		// andreas@flying-snail.de
		snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);
	}
#endif

#if ES1938_INCLUDE_GAMEPORT
	snd_es1938_create_gameport(chip);
#endif

	/* note: added the variable chip->pcm for use w/ vxworks */
	if ((err = snd_card_register(card, chip->pcm )) < 0) {
		snd_card_free(card);
		return err;
	}

	pci_set_drvdata(pci, card);
	dev++;

	return 0;
}

/* sets the playback volume for left and right channels of specified substream
	TODO: make a better interface */
static int snd_es1938_set_volume(snd_pcm_substream_t * substream, uint8 value )
{
	es1938_t *chip = (es1938_t *)snd_pcm_substream_chip(substream);

	switch (substream->number) {
	case 0:
		snd_es1938_mixer_write(chip,ESSSB_IREG_AUDIO2,value);
		return 0;
	case 1:
		snd_es1938_mixer_write(chip,ESSSB_IREG_AUDIO1,value);
		return 0;
	}
	snd_BUG();
	return -EINVAL;
}

static void __devexit snd_es1938_remove(struct pci_dev *pci)
{
	snd_card_free((snd_card_t *)pci_get_drvdata(pci));
	pci_set_drvdata(pci, NULL);
}


char ES1938_Name[] = "ESS ES1938 (Solo-1)";
struct pci_driver ES1938_driver = {
	{ NULL },	/* list_head node */
	ES1938_Name,	/* char *name */
#if VXWORKS_UNUSED
	NULL,			/* struct module *owner */
#endif
	snd_es1938_ids,	/* const struct pci_device_id *id_table */
	snd_es1938_probe,	/* int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);	 New device inserted */	
	NULL,	/* Device removed (NULL if not a hot-plug capable driver) */
	vx_snd_start_driver,
	SND_PCI_PM_CALLBACKS
};

int dr_mixer( unsigned char reg )
{
	return snd_es1938_mixer_read( pGlobChip, reg );
}

void dw_mixer( unsigned char reg, unsigned char val )
{
	snd_es1938_mixer_write( pGlobChip, reg, val );
}

int dr_1938( unsigned char reg )
{
	return	snd_es1938_read( pGlobChip, reg );
}

void dw_1938( unsigned char reg, unsigned char val )
{
	snd_es1938_write( pGlobChip, reg, val );
}

⌨️ 快捷键说明

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