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

📄 es1938.cpp

📁 devloped under vxwork, support ess sound card. driver library, include datasheet.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
/****************************************************************** MODULE:		ES1938 Sound Card Driver file** DESCRIPTION:	Driver for capture/playback functions on the ESS ES1938
*				chipset** ORIGINAL AUTHOR: 	GNU ALSA Linux Source Code (see header below) 
*					Modified for use with VxWorks by Dan Walkes* UPDATED BY:		* * CREATED:		Oct, 2005* MODIFIED:		
* * NOTES:** This file was modified as little as possible to make future porting of 
* ALSA code to VxWorks as easy as possible.  I also attempted to keep as much code
* as possible resident in the file so it could be referenced if any issues arise with
* operation in the future.  In many cases #if VX_UNUSED compile options were placed
* around code sections which I did not plan on including but may possibly be included
* in future revisions.** CODE USAGE:** This file requires files VxSound.cpp and VxPCI.cpp. See documentation
* in these modules for more information.  Playback has been tested with
* the vx_tone_ap.cpp source file which generates a continuous tone at 
* a specified frequency.  Capture has not yet been tested.
*** REVISION HISTORY AND NOTES:** Date			Update* ---------------------------------------------------------------------* Oct , 2005	Created.** REFERENCES:** 1) "DSSolo1.pdf" datasheet - ES1938 datasheet.** 2) ALSA Linux driver page at http://www.alsa-project.org/alsa-doc/alsa-lib/index.html*    * 3) "vxWALSA Sound Driver" document included with this release.
*****************************************************************/
/* Header below is the original header included with the es1938.cpp file 
	found in the ALSA library.  Functions were modified for use with VxWorks */
/* *  Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>, *                   Jaroslav Kysela <perex@suse.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 "VxWorks.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "VxTypes.h"
#include "VxSound.h"
#include "VxPCI.h"
#include "es1938.h"


typedef struct _snd_es1938 es1938_t;
es1938_t *pGlobChip;


#define SAVED_REG_SIZE	32 /* max. number of registers to save */

struct _snd_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;

	snd_kcontrol_t *hw_volume;
	snd_kcontrol_t *hw_switch;
	snd_kcontrol_t *master_volume;
	snd_kcontrol_t *master_switch;

	struct pci_dev *pci;
	snd_card_t *card;
	snd_pcm_t *pcm;
	snd_pcm_substream_t *capture_substream;
	snd_pcm_substream_t *playback1_substream;
	snd_pcm_substream_t *playback2_substream;

#if VXWORKS_UNUSED
	snd_kmixer_t *mixer;
	snd_rawmidi_t *rmidi;
	spinlock_t reg_lock;
	spinlock_t mixer_lock;
        snd_info_entry_t *proc_entry;

#ifdef SUPPORT_JOYSTICK
	struct gameport *gameport;
#endif
#ifdef CONFIG_PM
	unsigned char saved_regs[SAVED_REG_SIZE];
#endif

#endif
	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;

};


#ifndef PCI_VENDOR_ID_ESS
#define PCI_VENDOR_ID_ESS		0x125d
#endif
#ifndef PCI_DEVICE_ID_ESS_ES1938
#define PCI_DEVICE_ID_ESS_ES1938	0x1969
#endif


static const struct pci_device_id snd_es1938_ids[] = {
        { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */
	{ 0, }
};

#define SNDRV_DEFAULT_IDX	{ -1, -1, -1, -1, -1, -1, -1, -1 }
#define SNDRV_DEFAULT_STR	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
#define SNDRV_DEFAULT_ENABLE	{ 1, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
#define SNDRV_DEFAULT_ENABLE_PNP { 1, 1, 1, 1, 1, 1, 1, 1 }


/* for support of multiple cards */
static int a_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 */


static void snd_es1938_reset_fifo(es1938_t *chip);
static void snd_es1938_rate_set(es1938_t *chip, 
				snd_pcm_substream_t *substream,
				int mode);
static void snd_es1938_mixer_write(es1938_t *chip, unsigned char reg, unsigned char val);
static void snd_es1938_playback1_setdma(es1938_t *chip);
static void snd_es1938_write(es1938_t *chip, unsigned char reg, unsigned char val);
static int snd_es1938_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val);
static int snd_es1938_mixer_bits(es1938_t *chip, unsigned char reg, unsigned char mask, unsigned char val);
static int snd_es1938_set_volume(snd_pcm_substream_t * substream, uint8 value );
static void snd_es1938_reset(es1938_t *chip);


/* --------------------------------------------------------------------
 * Interrupt handler
 * -------------------------------------------------------------------- */
static irqreturn_t snd_es1938_interrupt( int dev_id )
{
	es1938_t *chip = (es1938_t *)dev_id;
	unsigned char status, audiostatus;

	status = inb(SLIO_REG(chip, IRQCONTROL));
	printk("Es1938debug - interrupt status: =0x%x\n", status);
	
	/* AUDIO 1 */
	if (status & 0x10) {
#if 0
                printk("Es1938debug - AUDIO channel 1 interrupt\n");
		printk("Es1938debug - AUDIO channel 1 DMAC DMA count: %u\n", inw(SLDM_REG(chip, DMACOUNT)));
		printk("Es1938debug - AUDIO channel 1 DMAC DMA base: %u\n", inl(SLDM_REG(chip, DMAADDR)));
		printk("Es1938debug - AUDIO channel 1 DMAC DMA status: 0x%x\n", inl(SLDM_REG(chip, DMASTATUS)));
#endif
		/* clear irq */
		audiostatus = inb(SLSB_REG(chip, STATUS));
		if (chip->active & ADC1)
			snd_pcm_period_elapsed(chip->capture_substream);
		else if (chip->active & DAC1)
			snd_pcm_period_elapsed(chip->playback2_substream);
	}
	
	/* AUDIO 2 */
	if (status & 0x20) {
#if 0
                printk("Es1938debug - AUDIO channel 2 interrupt\n");
		printk("Es1938debug - AUDIO channel 2 DMAC DMA count: %u\n", inw(SLIO_REG(chip, AUDIO2DMACOUNT)));
		printk("Es1938debug - AUDIO channel 2 DMAC DMA base: %u\n", inl(SLIO_REG(chip, AUDIO2DMAADDR)));

#endif
		/* clear irq */
		snd_es1938_mixer_bits(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x80, 0);
		if (chip->active & DAC2)
			snd_pcm_period_elapsed(chip->playback1_substream);
	}


#if ADD_LATER_INTERRUPT	/* add the volume and MPU401 handler later */
	(int irq, void *dev_id, struct pt_regs *regs)	/* need to set these.. are passed values in linux implementation */

/* Hardware volume */
	if (status & 0x40) {
		int split = snd_es1938_mixer_read(chip, 0x64) & 0x80;
		handled = 1;
		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_switch->id);
		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->hw_volume->id);
		if (!split) {
			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_switch->id);
			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &chip->master_volume->id);
		}
		/* ack interrupt */
		snd_es1938_mixer_write(chip, 0x66, 0x00);
	}

	/* MPU401 */
	if (status & 0x80) {
		// the following line is evil! It switches off MIDI interrupt handling after the first interrupt received.
		// replacing the last 0 by 0x40 works for ESS-Solo1, but just doing nothing works as well!
		// andreas@flying-snail.de
		// snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */
		if (chip->rmidi) {
			handled = 1;
			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
		}
	}
#else
	if (status & 0x40) {
		snd_es1938_mixer_write(chip, 0x66, 0x00);
	}
#endif

	return IRQ_RETVAL(handled);
}

/* ------------------------------------------------------------------------------
 * Second Audio channel DAC Operation
 * ------------------------------------------------------------------------------*/
static int snd_es1938_playback1_prepare(snd_pcm_substream_t * substream)
{
	es1938_t *chip = (es1938_t *)snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int u, is8, mono;
	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
	unsigned int count = snd_pcm_lib_period_bytes(substream);

	chip->dma2_size = size;
	chip->dma2_start = runtime->dma_addr;

	mono = (runtime->channels > 1) ? 0 : 1;
	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
	u = snd_pcm_format_unsigned(runtime->format);

	chip->dma2_shift = 2 - mono - is8;

        snd_es1938_reset_fifo(chip);

	/* set clock and counters */
        snd_es1938_rate_set(chip, substream, DAC2);

	count >>= 1;
	count = 0x10000 - count;
	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTL, count & 0xff);
	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2TCOUNTH, count >> 8);

	/* initialize and configure Audio 2 DAC */
	snd_es1938_mixer_write(chip, ESSSB_IREG_AUDIO2CONTROL2, 0x40 | (u ? 0 : 4) | (mono ? 0 : 2) | (is8 ? 0 : 1));

	/* program DMA */
	snd_es1938_playback1_setdma(chip);
	
	return 0;
}

/* --------------------------------------------------------------------
 * Reset the FIFOs
 * --------------------------------------------------------------------*/
static void snd_es1938_reset_fifo(es1938_t *chip)
{
	outb(2, SLSB_REG(chip, RESET));
	outb(0, SLSB_REG(chip, RESET));
}


static ratnum_t 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 snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
	/*.nrats =*/ 2,
	/*.rats = */ clocks,
};


static void snd_es1938_rate_set(es1938_t *chip, 
				snd_pcm_substream_t *substream,
				int mode)
{
	unsigned int bits, div0;
	snd_pcm_runtime_t *runtime = substream->runtime;
	if (runtime->rate_num == clocks[0].num)
		bits = 128 - runtime->rate_den;
	else
		bits = 256 - runtime->rate_den;

	/* set filter register */
	div0 = 256 - 7160000*20/(8*82*runtime->rate);
		
	if (mode == DAC2) {
		snd_es1938_mixer_write(chip, 0x70, bits);
		snd_es1938_mixer_write(chip, 0x72, div0);
	} else {
		snd_es1938_write(chip, 0xA1, bits);
		snd_es1938_write(chip, 0xA2, div0);
	}
}

static void snd_es1938_playback1_setdma(es1938_t *chip)
{
	outb(0x00, SLIO_REG(chip, AUDIO2MODE));
	outl(chip->dma2_start, SLIO_REG(chip, AUDIO2DMAADDR));
	outw(0, SLIO_REG(chip, AUDIO2DMACOUNT));
	outw(chip->dma2_size, SLIO_REG(chip, AUDIO2DMACOUNT));
}

static void snd_es1938_playback2_setdma(es1938_t *chip)
{
	/* Enable DMA controller */
	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
	/* 1. Master reset */
	outb(0, SLDM_REG(chip, DMACLEAR));
	/* 2. Mask DMA */
	outb(1, SLDM_REG(chip, DMAMASK));
	outb(0x18, SLDM_REG(chip, DMAMODE));
	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
	/* 3. Unmask DMA */
	outb(0, SLDM_REG(chip, DMAMASK));
}

static void snd_es1938_capture_setdma(es1938_t *chip)
{
	/* Enable DMA controller */
	outb(0xc4, SLDM_REG(chip, DMACOMMAND));
	/* 1. Master reset */
	outb(0, SLDM_REG(chip, DMACLEAR));
	/* 2. Mask DMA */
	outb(1, SLDM_REG(chip, DMAMASK));
	outb(0x14, SLDM_REG(chip, DMAMODE));
	outl(chip->dma1_start, SLDM_REG(chip, DMAADDR));
	outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT));
	/* 3. Unmask DMA */
	outb(0, SLDM_REG(chip, DMAMASK));
}


static int snd_es1938_playback2_prepare(snd_pcm_substream_t * substream)
{
	es1938_t *chip = (es1938_t *)snd_pcm_substream_chip(substream);
	snd_pcm_runtime_t *runtime = substream->runtime;
	int u, is8, mono;
	unsigned int size = snd_pcm_lib_buffer_bytes(substream);
	unsigned int count = snd_pcm_lib_period_bytes(substream);

	chip->dma1_size = size;
	chip->dma1_start = runtime->dma_addr;

	mono = (runtime->channels > 1) ? 0 : 1;
	is8 = snd_pcm_format_width(runtime->format) == 16 ? 0 : 1;
	u = snd_pcm_format_unsigned(runtime->format);

	chip->dma1_shift = 2 - mono - is8;

	count = 0x10000 - count;
 
	/* reset */
	snd_es1938_reset_fifo(chip);
	
	snd_es1938_bits(chip, ESS_CMD_ANALOGCONTROL, 0x03, (mono ? 2 : 1));

	/* set clock and counters */
        snd_es1938_rate_set(chip, substream, DAC1);
	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADL, count & 0xff);
	snd_es1938_write(chip, ESS_CMD_DMACNTRELOADH, count >> 8);

	/* initialized and configure DAC */
        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x80 : 0x00);
        snd_es1938_write(chip, ESS_CMD_SETFORMAT, u ? 0x51 : 0x71);
        snd_es1938_write(chip, ESS_CMD_SETFORMAT2, 
			 0x90 | (mono ? 0x40 : 0x08) |
			 (is8 ? 0x00 : 0x04) | (u ? 0x00 : 0x20));

	/* program DMA */
	snd_es1938_playback2_setdma(chip);
	
	return 0;
}


static int snd_es1938_playback_prepare(snd_pcm_substream_t *substream)
{
	switch (substream->number) {
	case 0:
		return snd_es1938_playback1_prepare(substream);
	case 1:
		return snd_es1938_playback2_prepare(substream);

⌨️ 快捷键说明

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