/*
 *  Copyright (c) by Matze Braun <MatzeBraun@gmx.de>,
 *  
 *  Driver for ESS Maestro-1/2/2E Soundcards
 *	Based on ES1938.c and maestro.c from Alan Cox and Zach Brown!
 *
 *  See http://www.braunis.de/matze/essm2ee.html for more info!
 *
 *  BUGS:
 *   ??? (I don't know, is there a memory leak somewhere?)
 *
 *  TODO:
 *   Perhaps Synth
 *   Record (Mono partly works at the moment)
 *
 *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Notes from Zach Brown about the driver code
 *
 *  Hardware Description
 *
 *	A working Maestro setup contains the Maestro chip wired to a 
 *	codec or 2.  In the Maestro we have the APUs, the ASSP, and the
 *	Wavecache.  The APUs can be though of as virtual audio routing
 *	channels.  They can take data from a number of sources and perform
 *	basic encodings of the data.  The wavecache is a storehouse for
 *	PCM data.  Typically it deals with PCI and interracts with the
 *	APUs.  The ASSP is a wacky DSP like device that ESS is loth
 *	to release docs on.  Thankfully it isn't required on the Maestro
 *	until you start doing insane things like FM emulation and surround
 *	encoding.  The codecs are almost always AC-97 compliant codecs, 
 *	but it appears that early Maestros may have had PT101 (an ESS
 *	part?) wired to them.  The only real difference in the Maestro
 *	families is external goop like docking capability, memory for
 *	the ASSP, and initialization differences.
 *
 *  Driver Operation
 *
 *	We only drive the APU/Wavecache as typical DACs and drive the
 *	mixers in the codecs.  There are 64 APUs.  We assign 6 to each
 *	/dev/dsp? device.  2 channels for output, and 4 channels for
 *	input.
 *
 *	Each APU can do a number of things, but we only really use
 *	3 basic functions.  For playback we use them to convert PCM
 *	data fetched over PCI by the wavecahche into analog data that
 *	is handed to the codec.  One APU for mono, and a pair for stereo.
 *	When in stereo, the combination of smarts in the APU and Wavecache
 *	decide which wavecache gets the left or right channel.
 *
 *	For record we still use the old overly mono system.  For each in
 *	coming channel the data comes in from the codec, through a 'input'
 *	APU, through another rate converter APU, and then into memory via
 *	the wavecache and PCI.  If its stereo, we mash it back into LRLR in
 *	software.  The pass between the 2 APUs is supposedly what requires us
 *	to have a 512 byte buffer sitting around in wavecache/memory.
 *
 *	The wavecache makes our life even more fun.  First off, it can
 *	only address the first 28 bits of PCI address space, making it
 *	useless on quite a few architectures.  Secondly, its insane.
 *	It claims to fetch from 4 regions of PCI space, each 4 meg in length.
 *	But that doesn't really work.  You can only use 1 region.  So all our
 *	allocations have to be in 4meg of each other.  Booo.  Hiss.
 *	So we have a module parameter, dsps_order, that is the order of
 *	the number of dsps to provide.  All their buffer space is allocated
 *	on open time.  The sonicvibes OSS routines we inherited really want
 *	power of 2 buffers, so we have all those next to each other, then
 *	512 byte regions for the recording wavecaches.  This ends up
 *	wasting quite a bit of memory.  The only fixes I can see would be 
 *	getting a kernel allocator that could work in zones, or figuring out
 *	just how to coerce the WP into doing what we want.
 *
 *	The indirection of the various registers means we have to spinlock
 *	nearly all register accesses.  We have the main register indirection
 *	like the wave cache, maestro registers, etc.  Then we have beasts
 *	like the APU interface that is indirect registers gotten at through
 *	the main maestro indirection.  Ouch.  We spinlock around the actual
 *	ports on a per card basis.  This means spinlock activity at each IO
 *	operation, but the only IO operation clusters are in non critical 
 *	paths and it makes the code far easier to follow.  Interrupts are
 *	blocked while holding the locks because the int handler has to
 *	get at some of them :(.  The mixer interface doesn't, however.
 *	We also have an OSS state lock that is thrown around in a few
 *	places.
 */

#define __SND_OSS_COMPAT__
#define SND_MAIN_OBJECT_FILE
#include "../../include/driver.h"
#include "../../include/info.h"
#include "../../include/es1968.h"

/* ************
   * Debug ?  *
   ************/

#ifdef CONFIG_SND_DEBUG
 #define ESM_DDEBUG		/* Output on important locations */
 /*#define ESM_IDEBUG*//* Interrupt Debugging - Much Output */
#endif

/* *********
   * Hacks *
   *********/

/*#define ESM_SYNTHHACK*//* Synth experiment and test routines only for
   myself! */

/* Needed for Bob Calc */
static const unsigned sample_size[] = { 1, 2, 2, 4 };
static const unsigned sample_shift[] = { 0, 1, 1, 2 };

/* *********************
   * Low Level Funcs!  *
   *********************/

static void maestro_write(es1968_t * esm, u16 reg, u16 data)
{
	unsigned long ioaddr = esm->io_port;
	unsigned long flags;

	spin_lock_irqsave(&esm->reg_lock, flags);

	outw(reg, ioaddr + ESM_INDEX);
	outw(data, ioaddr + ESM_DATA);
	esm->maestro_map[reg] = data;

	spin_unlock_irqrestore(&esm->reg_lock, flags);
}

static void __maestro_write(es1968_t * esm, u16 reg, u16 data)
{
	unsigned long ioaddr = esm->io_port;

	outw(reg, ioaddr + ESM_INDEX);
	outw(data, ioaddr + ESM_DATA);
	esm->maestro_map[reg] = data;
}

static u16 maestro_read(es1968_t * esm, u16 reg)
{
	unsigned long ioaddr = esm->io_port;

	if (READABLE_MAP & (1 << reg)) {
		unsigned long flags;

		spin_lock_irqsave(&esm->reg_lock, flags);


		outw(reg, ioaddr + ESM_INDEX);
		esm->maestro_map[reg] = inw(ioaddr + ESM_DATA);

		spin_unlock_irqrestore(&esm->reg_lock, flags);
	}
	return esm->maestro_map[reg];
}

static u16 __maestro_read(es1968_t * esm, u16 reg)
{
	unsigned long ioaddr = esm->io_port;

	if (READABLE_MAP & (1 << reg)) {
		outw(reg, ioaddr + ESM_INDEX);
		esm->maestro_map[reg] = inw(ioaddr + ESM_DATA);
	}
	return esm->maestro_map[reg];
}

static void snd_es1968_codec_write(void *private_data,
				   unsigned short reg, unsigned short val)
{
	int i;
	es1968_t *esm = snd_magic_cast(es1968_t, private_data, );
	unsigned long flags;

	spin_lock_irqsave(&esm->reg_lock, flags);

	/* Wait for the codec bus to be free */
	for (i = 0; i < 10000; i++) {
		if (!(inb(esm->io_port + AC97_STATUS) & 1))
			break;
	}

	/* Write the bus */
	outw(val, esm->io_port + AC97_DATA);
	mdelay(1);
	outb(reg, esm->io_port + AC97_INDEX);
	mdelay(1);

	spin_unlock_irqrestore(&esm->reg_lock, flags);
}

static unsigned short snd_es1968_codec_read(void *private_data,
					    unsigned short reg)
{
	int sanity = 100000;
	u16 data;
	int i;
	es1968_t *esm = snd_magic_cast(es1968_t, private_data, 0);
	unsigned long flags;

	spin_lock_irqsave(&esm->reg_lock, flags);

	/* Wait for the codec bus to be free */

	for (i = 0; i < 10000; i++) {
		if (!(inb(esm->io_port + AC97_INDEX) & 1))
			break;
	}

	outb(reg | 0x80, esm->io_port + AC97_INDEX);
	mdelay(1);

	while (inb(esm->io_port + AC97_INDEX) & 1) {
		sanity--;
		if (!sanity) {
			snd_printk("maestro: ac97 codec timeout.\n");
			spin_unlock_irqrestore(&esm->reg_lock, flags);
			return 0;
		}
	}
	data = inw(esm->io_port + AC97_DATA);
	mdelay(1);
	spin_unlock_irqrestore(&esm->reg_lock, flags);

	return data;
}

static void apu_index_set(es1968_t * esm, u16 index)
{
	int i;
	__maestro_write(esm, IDR1_CRAM_POINTER, index);
	for (i = 0; i < 1000; i++)
		if (__maestro_read(esm, IDR1_CRAM_POINTER) == index)
			return;
#ifdef ESM_EDEBUG
	snd_printk("maestro: APU register select failed. (Timeout)\n");
#endif
}

static void apu_data_set(es1968_t * esm, u16 data)
{
	int i;
	for (i = 0; i < 1000; i++) {
		if (__maestro_read(esm, IDR0_DATA_PORT) == data)
			return;
		__maestro_write(esm, IDR0_DATA_PORT, data);
	}
#ifdef ESM_EDEBUG
	snd_printk
	    ("maestro: APU register set probably failed (Timeout)!\n");
#endif
}

static void apu_set_register(es1968_t * esm, u16 channel, u8 reg, u16 data)
{
	unsigned long flags;

	if (channel >= NR_APUS) {
#ifdef ESM_EDEBUG
		snd_printk("maestro: BAD CHANNEL %d (Please report this!).\n", channel);
#endif
		return;
	}

	reg |= (channel << 4);

	spin_lock_irqsave(&esm->reg_lock, flags);

	apu_index_set(esm, reg);
	apu_data_set(esm, data);

	spin_unlock_irqrestore(&esm->reg_lock, flags);
}

static u16 apu_get_register(es1968_t * esm, u16 channel, u8 reg)
{
	unsigned long flags;
	u16 v;

	if (channel >= NR_APUS) {
#ifdef ESM_EDEBUG
		snd_printk("maestro: BAD CHANNEL %d (Please report this!).\n", channel);
#endif
		return 0;
	}

	reg |= (channel << 4);

	spin_lock_irqsave(&esm->reg_lock, flags);

	apu_index_set(esm, reg);
	v = __maestro_read(esm, IDR0_DATA_PORT);

	spin_unlock_irqrestore(&esm->reg_lock, flags);
	return v;
}

#if 0

static void assp_set_register(es1968_t * esm, u32 reg, u32 value)
{
	unsigned long flags;

	spin_lock_irqsave(&esm->reg_lock, flags);

	outl(reg, esm->io_port + ASSP_INDEX);
	outl(value, esm->io_port + ASSP_DATA);

	spin_unlock_irqrestore(&esm->reg_lock, flags);
}

static u32 assp_get_register(es1968_t * esm, u32 reg)
{
	unsigned long flags;
	u32 value;

	spin_lock_irqsave(&esm->reg_lock, flags);

	outl(reg, esm->io_port + ASSP_INDEX);
	value = inl(esm->io_port + ASSP_DATA);

	spin_unlock_irqrestore(&esm->reg_lock, flags);

	return value;
}

#endif

static void wave_set_register(es1968_t * esm, u16 reg, u16 value)
{
	unsigned long ioaddr = esm->io_port;
	unsigned long flags;

	spin_lock_irqsave(&esm->reg_lock, flags);

	outw(reg, ioaddr + WC_INDEX);
	outw(value, ioaddr + WC_DATA);

	spin_unlock_irqrestore(&esm->reg_lock, flags);
}

static u16 wave_get_register(es1968_t * esm, u16 reg)
{
	unsigned long ioaddr = esm->io_port;
	unsigned long flags;
	u16 value;

	spin_lock_irqsave(&esm->reg_lock, flags);

	outw(reg, ioaddr + WC_INDEX);
	value = inw(ioaddr + WC_DATA);

	spin_unlock_irqrestore(&esm->reg_lock, flags);

	return value;
}

/* *******************
   * Bob the Timer!  *
   *******************/

static void esm_bob_stop(es1968_t * esm)
{
	u16 reg;

	reg = maestro_read(esm, 0x11);
	reg &= ~ESM_BOB_ENABLE;
	maestro_write(esm, 0x11, reg);
	reg = maestro_read(esm, 0x17);
	reg &= ~ESM_BOB_START;
	maestro_write(esm, 0x17, reg);
}

static void esm_bob_start(es1968_t * esm)
{
	int prescale;
	int divide;

	/* Check! */
	int freq = ESM_BOB_FREQ;	/* requested frequency - calculate what we want here. */

	/* compute ideal interrupt frequency for buffer size & play rate */
	/* first, find best prescaler value to match freq */
	for (prescale = 5; prescale < 12; prescale++)
		if (freq > (ESS_SYSCLK >> (prescale + 9)))
			break;

	/* next, back off prescaler whilst getting divider into optimum range */
	divide = 1;
	while ((prescale > 5) && (divide < 32)) {
		prescale--;
		divide <<= 1;
	}
	divide >>= 1;

	/* now fine-tune the divider for best match */
	for (; divide < 31; divide++)
		if (freq >=
		    ((ESS_SYSCLK >> (prescale + 9)) / (divide + 1))) break;

	/* divide = 0 is illegal, but don't let prescale = 4! */
	if (divide == 0) {
		divide++;
		if (prescale > 5)
			prescale--;
	}

	maestro_write(esm, 6, 0x9000 | (prescale << 5) | divide);	/* set reg */

	/* Now set IDR 11/17 */
	maestro_write(esm, 0x11, maestro_read(esm, 0x11) | 1);
	maestro_write(esm, 0x17, maestro_read(esm, 0x17) | 1);
}

#if 0

#define BOB_MIN 50
#define BOB_MAX 400
static void calc_bob_rate(es1968_t * esm)
{
	/* this thing tries to set the frequency of bob such that
	   there are 2 interrupts / buffer walked by the dac/adc.  That
	   is probably very wrong for people who actually care about 
	   mid buffer positioning.  it should be calculated as bytes/interrupt
	   and that needs to be decided :)  so for now just use the static 150
	   in start_bob. */

	unsigned int dac_rate = 2, adc_rate = 1, newrate;
	static int israte = -1;

	if (s->dma_dac.fragsize == 0)
		dac_rate = BOB_MIN;
	else {
		dac_rate =
		    (2 * s->ratedac *
		     sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK])
		    / (s->dma_dac.fragsize);
	}

	if (s->dma_adc.fragsize == 0)
		adc_rate = BOB_MIN;
	else {
		adc_rate =
		    (2 * s->rateadc *
		     sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK])
		    / (s->dma_adc.fragsize);
	}

	if (dac_rate > adc_rate)
		newrate = adc_rate;
	else
		newrate = dac_rate;

	if (newrate > BOB_MAX)
		newrate = BOB_MAX;
	else {
		if (newrate < BOB_MIN)
			newrate = BOB_MIN;
	}

	if (israte != newrate) {
		snd_printk("dac: %d  adc: %d rate: %d\n", dac_rate,
			   adc_rate, israte);
		israte = newrate;
	}
}

#endif

/*************
 *  PCM Part *
 *************/

static u32 compute_rate(es1968_t * esm, u32 freq)
{
	u32 clock;
	switch (esm->type) {
	case SND_CARD_TYPE_ESS_ES1978:
		clock = ESM_FREQ_ESM2E;
		break;
	case SND_CARD_TYPE_ESS_ES1968:
		clock = ESM_FREQ_ESM2;
		break;
	case SND_CARD_TYPE_ESS_ESOLDM1:
		clock = ESM_FREQ_ESM1;
		break;
	default:
		clock = ESM_FREQ_ESM2E;
#ifdef ESM_EDEBUG
		snd_printk("maestro: wrong typ in compute_rate (please report!)\n");
#endif
	}

	if (freq == 48000)
		return 0x10000;

	return ((freq / clock) << 16) + (((freq % clock) << 16) / clock);
}

/* Playback Pointer */
extern __inline__ unsigned get_dmaa(esschanp_t * es)
{
	int offset;

	offset = apu_get_register(es->card, es->apu[0], 5);

	offset -= es->base;

	return (offset & 0xFFFE) << 1;	/* hardware is in words */
}

/* Record pointer */
extern __inline__ unsigned get_dmac(esschanc_t * es)
{
	int offset;

	offset = apu_get_register(es->card, es->apu[0], 5);

	offset -= es->base;

	return (offset & 0xFFFE) << 1;	/* hardware is in words */
}

static void set_dac_rate(esschanp_t * es, unsigned rate)
{
	u32 freq;

	if (rate > 48000)
		rate = 48000;
	if (rate < 4000)
		rate = 4000;

	if (!(es->fmt & ESS_FMT_16BIT) && !(es->fmt & ESS_FMT_STEREO))
		rate >>= 1;

	freq = compute_rate(es->card, rate);

	/* Load the frequency, turn on 6dB */
	apu_set_register(es->card, es->apu[0], 2,
			 (apu_get_register(es->card, es->apu[0], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[0], 3, freq >> 8);
	apu_set_register(es->card, es->apu[1], 2,
			 (apu_get_register(es->card, es->apu[1], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[1], 3, freq >> 8);
}

static void set_adc_rate(esschanc_t * es, unsigned rate)
{
	u32 freq;

	/* Sample Rate conversion APUs don't like 0x10000 for their rate */
	if (rate > 47999)
		rate = 47999;
	if (rate < 4000)
		rate = 4000;

	freq = compute_rate(es->card, rate);

	/* Load the frequency, turn on 6dB */
	apu_set_register(es->card, es->apu[0], 2,
			 (apu_get_register(es->card, es->apu[0], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[0], 3, freq >> 8);
	apu_set_register(es->card, es->apu[1], 2,
			 (apu_get_register(es->card, es->apu[1], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[1], 3, freq >> 8);

	/* fix mixer rate at 48khz.  and its _must_ be 0x10000. */
	freq = 0x10000;

	apu_set_register(es->card, es->apu[2], 2,
			 (apu_get_register(es->card, es->apu[2], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[2], 3, freq >> 8);
	apu_set_register(es->card, es->apu[3], 2,
			 (apu_get_register(es->card, es->apu[3], 2) &
			  0x00FF) | (((freq & 0xFF) << 8) | 0x10));
	apu_set_register(es->card, es->apu[3], 3, freq >> 8);
}

extern void stop_dac(esschanp_t * es)
{
	if (!(es->enable & DAC_RUNNING))
		return;

	es->enable &= ~DAC_RUNNING;
#ifdef ESM_DDEBUG
	snd_printk("Playback Trigger down!\n");
#endif
	apu_set_register(es->card, es->apu[0], 0,
			 apu_get_register(es->card, es->apu[0],
					  0) & 0xFF0F);
	if (es->fmt & ESS_FMT_STEREO)
		apu_set_register(es->card, es->apu[1], 0,
				 apu_get_register(es->card, es->apu[1],
						  0) & 0xFF0F);
}

extern void stop_adc(esschanc_t * es)
{
	/* XXX lets hope we don't have to lock around this */
	if (!(es->enable & ADC_RUNNING))
		return;

	es->enable &= ~ADC_RUNNING;
#ifdef ESM_DDEBUG
	snd_printk("Capture Trigger down!\n");
#endif
	apu_set_register(es->card, es->apu[0], 0,
			 apu_get_register(es->card, es->apu[0],
					  0) & 0xFF0F);
	apu_set_register(es->card, es->apu[1], 0,
			 apu_get_register(es->card, es->apu[1],
					  0) & 0xFF0F);
	apu_set_register(es->card, es->apu[2], 0,
			 apu_get_register(es->card, es->apu[2],
					  0) & 0xFF0F);
	apu_set_register(es->card, es->apu[3], 0,
			 apu_get_register(es->card, es->apu[3],
					  0) & 0xFF0F);
}

static void start_dac(esschanp_t * es)
{
	if (!(es->enable & DAC_RUNNING)) {
#ifdef ESM_DDEBUG
		snd_printk("Playback Trigger up!\n");
#endif

		es->enable |= DAC_RUNNING;

		apu_set_register(es->card, es->apu[0], 0,
				 (apu_get_register(es->card, es->apu[0], 0)
				  & 0xFF0F) | es->apu_mode[0]);
		if (es->fmt & ESS_FMT_STEREO)
			apu_set_register(es->card, es->apu[1], 0,
					 (apu_get_register(es->card,
							   es->apu[1],
							   0) & 0xFF0F) |
					 es->apu_mode[1]);
	}
}

static void start_adc(esschanc_t * es)
{
#ifdef ESM_DDEBUG
	snd_printk("Capture Trigger up!\n");
#endif

	es->enable |= ADC_RUNNING;
	apu_set_register(es->card, es->apu[0], 0,
			 (apu_get_register(es->card, es->apu[0], 0) &
			  0xFF0F) | es->apu_mode[0]);
	apu_set_register(es->card, es->apu[2], 0,
			 (apu_get_register(es->card, es->apu[2], 0) &
			  0xFF0F) | es->apu_mode[2]);

	if (es->fmt & ESS_FMT_STEREO) {
		apu_set_register(es->card, es->apu[1], 0,
				 (apu_get_register(es->card, es->apu[0], 0)
				  & 0xFF0F) | es->apu_mode[0]);
		apu_set_register(es->card, es->apu[3], 0,
				 (apu_get_register(es->card, es->apu[2], 0)
				  & 0xFF0F) | es->apu_mode[2]);
	}
}

static void set_base_registers(es1968_t * esm, void *vaddr)
{
	unsigned long packed_phys = virt_to_bus(vaddr) >> 12;

	wave_set_register(esm, 0x01FC, packed_phys);
#if 0				/* Matze: These registers seem to have no effect. */
	wave_set_register(esm, 0x01FD, packed_phys);
	wave_set_register(esm, 0x01FE, packed_phys);
	wave_set_register(esm, 0x01FF, packed_phys);
#endif
}

static void ess_play_setup(esschanp_t * es, u32 rate, void *buffer)
{
	u32 pa;
	u32 tmpval;
	int high_apu = 0;
	int channel;
        int size=es->transs;
	unsigned long flags;
	es1968_t *esm = es->card;

	/* all maestro sizes are in 16bit words */
	size >>= 1;

	if (es->fmt & ESS_FMT_STEREO) {
		high_apu++;
		size >>= 1;
	}

	for (channel = 0; channel <= high_apu; channel++) {
		pa = virt_to_bus(buffer);

		tmpval = (pa - 0x10) & 0xFFF8;

		if (!(es->fmt & ESS_FMT_16BIT))
			tmpval |= 4;	/* 8bit */
		if (es->fmt & ESS_FMT_STEREO)
			tmpval |= 2;	/* stereo */

		/* set the wavecache control reg */

		wave_set_register(esm, es->apu[channel] << 3, tmpval);

		pa -= virt_to_bus(esm->firstbuf);	/* Offset from Base Adress */
		pa >>= 1;	/* words */

		/* base offset of dma calcs when reading the pointer
		   on this left one */
		if (!channel)
			es->base = pa & 0xFFFF;

		pa |= 0x00400000;	/* System RAM (Bit 22) */

		if ((es->fmt & ESS_FMT_STEREO) && (es->fmt & ESS_FMT_16BIT)) {
			/* Enable stereo */
			if (!channel)
				pa |= 0x00800000;	/* (Bit 23) */
			pa >>= 1;
		}

		/* Disable APU */
		apu_set_register(esm, es->apu[channel], 0, 0x0000);

		/* Disable SubGroups */
		apu_set_register(esm, es->apu[channel], 2, 0x0000);
		/* Frequ and APU Step?? */
		apu_set_register(esm, es->apu[channel], 3, 0x0000);
		/* XXX think about endianess when writing these registers */
		/* Load the buffer into the wave engine */
		apu_set_register(esm, es->apu[channel], 4,
				 ((pa >> 16) & 0xFF) << 8);
		apu_set_register(esm, es->apu[channel], 5, pa & 0xFFFF);
		apu_set_register(esm, es->apu[channel], 6,
				 (pa + size) & 0xFFFF);
		/* setting loop == sample len */
#if 1
		/* Check! */
		/* Matze: Without this you've got clicking! */
		if (channel) {
			apu_set_register(esm, es->apu[channel], 7,
					 size - 1);
		} else if (!(es->fmt & ESS_FMT_16BIT)
			   || !(es->fmt & ESS_FMT_STEREO)) {
			apu_set_register(esm, es->apu[channel], 7,
					 size - 1);
		} else
#endif
			apu_set_register(esm, es->apu[channel], 7, size);

		/* clear effects/env.. */
		apu_set_register(esm, es->apu[channel], 8, 0x0000);
		/* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */
		apu_set_register(esm, es->apu[channel], 9, 0xD000);

		/* clear routing stuff */
		apu_set_register(esm, es->apu[channel], 11, 0x0000);
		/* dma on, no envelopes, filter to all 1s) */
		apu_set_register(esm, es->apu[channel], 0, 0x400F);

		if (es->fmt & ESS_FMT_16BIT)
			es->apu_mode[channel] = 0x10;
		else
			es->apu_mode[channel] = 0x30;

		if (es->fmt & ESS_FMT_STEREO) {
			/* set panning: left or right */
			/* Check: different panning. On my Canyon 3D Chipset the
			   Channels are swapped. I don't know, about the output
			   to the SPDif Link. Perhaps you have to change this
			   and not the APU Regs 4-5. */
			apu_set_register(esm, es->apu[channel], 10,
					 0x8F00 | (channel ? 0x10 : 0));
			es->apu_mode[channel] += 0x10;
		} else
			apu_set_register(esm, es->apu[channel], 10,
					 0x8F08);
	}

	spin_lock_irqsave(&esm->reg_lock, flags);
	/* clear WP interupts */
	outw(1, esm->io_port + 0x04);
	/* enable WP ints */
	outw(inw(esm->io_port + 0x18) | 4, esm->io_port + 0x18);
	spin_unlock_irqrestore(&esm->reg_lock, flags);

	set_dac_rate(es, rate);
}

static void ess_rec_setup(esschanc_t * es, u32 rate, void *buffer)
{
	int apu_step = 2;
	int channel;
        int size=es->transs;
	int i;
	unsigned long flags;
        es1968_t *esm = es->card;

	snd_printd
	    ("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n",
	     es->fmt, rate, buffer, size);

	/* all maestro sizes are in 16bit words */
	size >>= 1;

	/* we're given the full size of the buffer, but
	   in stereo each channel will only use its half */
	if (es->fmt & ESS_FMT_STEREO) {
		size >>= 1;
		apu_step = 1;
	}

	/* APU assignments: 0 = mono/left SRC
	   1 = right SRC
	   2 = mono/left Input Mixer
	   3 = right Input Mixer */
	for (channel = 0; channel < 4; channel += apu_step) {
		int bsize, route;
		u32 pa;
		u32 tmpval;

		/* data seems to flow from the codec, through an apu into
		   the 'mixbuf' bit of page, then through the SRC apu
		   and out to the real 'buffer'.  ok.  sure.  */

		if (channel & 2) {
			/* ok, we're an input mixer going from adc
			   through the mixbuf to the other apus */

			if (!(channel & 0x01)) {
				pa = virt_to_bus(es->mixbuf);
			} else {
				pa = virt_to_bus(es->mixbuf + size * 2);
			}

			/* we source from a 'magic' apu */
			bsize = size >> 1;/* half of this channels alloc, in words */
			route = 0x14 /*+ es->apu[channel - 2] */ ;	/* parallel in crap, see maestro reg 0xC [8-11] */
			es->apu_mode[channel] = 0x90;	/* Input Mixer */
#ifdef ESM_DDEBUG
			snd_printk
			    ("Set Input Mixer: Apu:%d Mode:%x Route:%x\n",
			     es->apu[channel], es->apu_mode[channel],
			     route);
#endif
		} else {
			/* we're a rate converter taking
			   input from the input apus and outputing it to
			   system memory */
			if (!(channel & 0x01)) {
				pa = virt_to_bus(buffer);
			} else {
				/* right channel records its split half.
				   *2 accomodates for rampant shifting earlier */
				pa = virt_to_bus(buffer + size * 2);
			}

			es->apu_mode[channel] = 0xB0;	/* Sample Rate Converter */

			bsize = size;
			/* get input from inputing apu */
			route = es->apu[channel + 2];
#ifdef ESM_DDEBUG
			snd_printk
			    ("Set Rate Converter: Apu:%d Mode:%x Route:%x\n",
			     es->apu[channel], es->apu_mode[channel],
			     route);
#endif
		}

#ifdef ESM_DDEBUG
		snd_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n", pa, channel);
#endif

		/* set the wavecache control reg */
		tmpval = (pa - 0x10) & 0xFFF8;
		if (!(es->fmt & ESS_FMT_16BIT))
			tmpval |= 4;
		if (es->fmt & ESS_FMT_STEREO)
			tmpval |= 2;
		wave_set_register(esm, es->apu[channel] << 3, tmpval);

		/* Offset to PCMBAR */
		pa -= virt_to_bus(esm->firstbuf);
		pa >>= 1;	/* words */

		/* base offset of dma calcs when reading the pointer
		   on this left one */
		if (channel == 0)
			es->base = pa & 0xFFFF;

		pa |= 0x00400000;	/* bit 22 -> System RAM */

#ifdef ESM_DDEBUG
		snd_printk
		    ("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n",
		     es->apu[channel], pa, bsize, route);
#endif
                     
                /* Begin loading the APU */
                for (i=0;i<15;i++)
                   	apu_set_register(esm,es->apu[channel],i,0x0000);

		/* need to enable subgroups.. and we should probably
		   have different groups for different /dev/dsps..  */
		apu_set_register(esm, es->apu[channel], 2, 0x8);

		/* Load the buffer into the wave engine */
		apu_set_register(esm, es->apu[channel], 4,
				 ((pa >> 16) & 0xFF) << 8);
		/* XXX reg is little endian.. */
		apu_set_register(esm, es->apu[channel], 5, pa & 0xFFFF);
		apu_set_register(esm, es->apu[channel], 6,
				 (pa + bsize) & 0xFFFF);

#if 1
		if ( es->fmt & ESS_FMT_STEREO ) {
			apu_set_register(esm, es->apu[channel], 7,
					 bsize - 1);
		} else
#endif
			apu_set_register(esm, es->apu[channel], 7, bsize);

		/* clear effects/env.. */
		apu_set_register(esm, es->apu[channel], 8, 0x00F0);

		/* amplitude now?  sure.  why not.  */
		apu_set_register(esm, es->apu[channel], 9, 0x0000);

		/* set filter tune, radius, polar pan */
		apu_set_register(esm, es->apu[channel], 10, 0x8F08);

		/* route input */
		apu_set_register(esm, es->apu[channel], 11, route);

		apu_set_register(esm, es->apu[channel], 0, 0x400F);
	}

	spin_lock_irqsave(&esm->reg_lock, flags);
	/* clear WP interupts */
	outw(1, esm->io_port + 0x04);
	/* enable WP ints */
	outw(inw(esm->io_port + 0x18) | 4, esm->io_port + 0x18);
	spin_unlock_irqrestore(&esm->reg_lock, flags);

	/* let 'er rip */
	set_adc_rate(es, rate);
}

/*******************
 *  ALSA Interface *
 *******************/

static int snd_es1968_playback_ioctl(void *private_data,
				     snd_pcm_subchn_t * subchn,
				     unsigned int cmd, unsigned long *arg)
{
	/* We calculate the Realrate later. So we don't need any IOCTL */
	return snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
}

static int snd_es1968_capture_ioctl(void *private_data,
				    snd_pcm_subchn_t * subchn,
				    unsigned int cmd, unsigned long *arg)
{
	/* We calculate the Realrate later. So we don't need any IOCTL */
	return snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
}

static int snd_es1968_playback_prepare(void *private_data,
				       snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;
	esschanp_t *es = snd_magic_cast(esschanp_t, runtime->private_data, -ENXIO);
	unsigned int realrate;
	char *buffer;

	es->transs = snd_pcm_lib_transfer_size(subchn);
	es->transf = snd_pcm_lib_transfer_fragment(subchn);

	realrate = runtime->format.rate;
	buffer = runtime->dma_area->buf;

	es->fmt = 0;
	if (snd_pcm_format_width(runtime->format.format) == 16)
		es->fmt |= ESS_FMT_16BIT;
	if (runtime->format.voices > 1)
		es->fmt |= ESS_FMT_STEREO;

	stop_dac(es);
	ess_play_setup(es, realrate, buffer);

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_playback_prepare Voices:%d Mode:%d!\n",
		   runtime->format.voices,
		   snd_pcm_format_width(runtime->format.format));
	snd_printk("Fmt: %d Rate: %d Buffer: %p Size: %d Fragment: %d\n",
		   es->fmt, realrate, buffer, es->transs, es->transf);
#endif

	return 0;
}

static int snd_es1968_capture_prepare(void *private_data,
				      snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;
	esschanc_t *es = snd_magic_cast(esschanc_t, runtime->private_data, -ENXIO);
	unsigned int realrate;
	char *buffer;

	es->transs = snd_pcm_lib_transfer_size(subchn);
	es->transf = snd_pcm_lib_transfer_fragment(subchn);

	realrate = runtime->format.rate;
	buffer = runtime->dma_area->buf;

	es->fmt = 0;
	if (snd_pcm_format_width(runtime->format.format) == 16)
		es->fmt |= ESS_FMT_16BIT;
	if (runtime->format.voices > 1)
		es->fmt |= ESS_FMT_STEREO;

	stop_adc(es);
	ess_rec_setup(es, realrate, buffer);

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_capture_prepare Voices:%d Mode:%d!\n",
		   runtime->format.voices,
		   snd_pcm_format_width(runtime->format.format));
	snd_printk("Fmt: %d Rate: %d Buffer: %p Size: %d Fragment: %d\n",
		   es->fmt, realrate, buffer, es->transs, es->transf);
#endif

	return 0;
}

static int snd_es1968_playback_trigger(void *private_data,
				       snd_pcm_subchn_t * subchn, int up)
{
	esschanp_t *es = snd_magic_cast(esschanp_t, subchn->runtime->private_data, -ENXIO);

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_playback_trigger :%d!\n", up);
#endif

	if (up) {
		start_dac(es);
		es->count = 0;
		es->hwptr = 0;
		es->mode |= ESM_MODE_PLAY;
	} else {
		es->mode &= ~ESM_MODE_PLAY;
		stop_dac(es);
	}
	return 0;
}

static int snd_es1968_capture_trigger(void *private_data,
				      snd_pcm_subchn_t * subchn, int up)
{
	esschanc_t *es = snd_magic_cast(esschanc_t, subchn->runtime->private_data, -ENXIO);

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_capture_trigger :%d!\n", up);
#endif

	if (up) {
		start_adc(es);
		es->count = 0;
		es->hwptr = 0;
		es->mode |= ESM_MODE_REC;
	} else {
		es->mode &= ~ESM_MODE_REC;
		stop_adc(es);
	}
	return 0;
}

static unsigned int snd_es1968_playback_pointer(void *private_data,
						snd_pcm_subchn_t * subchn)
{
	esschanp_t *es = snd_magic_cast(esschanp_t, subchn->runtime->private_data, -ENXIO);
	unsigned int ptr;

	ptr = get_dmaa(es);

	if ( (es->fmt & ESS_FMT_STEREO) && (es->fmt & ESS_FMT_16BIT) )
           ptr <<= 1;

	return (subchn->runtime->buf_position + es->count) % es->transs;
}

static unsigned int snd_es1968_capture_pointer(void *private_data,
					       snd_pcm_subchn_t * subchn)
{
	esschanc_t *es = snd_magic_cast(esschanc_t, subchn->runtime->private_data, -ENXIO);
	unsigned int ptr;

	ptr = get_dmac(es);

	if ( (es->fmt & ESS_FMT_STEREO) && (es->fmt & ESS_FMT_16BIT) )
           ptr <<= 1;

	return (subchn->runtime->buf_position + es->count) % es->transs;
}

#if 0
#if 0
static int snd_es1968_capture_memcpy(snd_pcm_substream_t *substream, int voice, unsigned int pos,
   					 void *buf, size_t count)
{
   	unsigned char *combbuf;
       	unsigned char *so,*left,*right;
        int i;
   
	if (substream->runtime->format.voices > 1) {
           	if ( !(combbuf=snd_kmalloc (count,GFP_KERNEL)) )
                 return -ENOMEM;

	       so = combbuf;
	       left = substream->runtime->dma_area->buf + pos;
               right = substream->runtime->dma_area->buf + (substream->runtime->dma_area->size/2) + pos;

               for(i=count/4; i ; i--) {
                  (*(so+2)) = *(right++);
                  (*(so+3)) = *(right++);
                  (*so) = *(left++);
                  (*(so+1)) = *(left++);
                  so+=4;
               }
               
               i=copy_to_user (buf,combbuf,count);
               
               snd_kfree(combbuf);
               
               return i;
        } else {
               return copy_to_user (buf,substream->runtime->dma_area->buf+pos,count);
        }
}
#else
static int snd_es1968_capture_memcpy(snd_pcm_substream_t *substream, int voice, unsigned int pos,
   					 void *buf, size_t count)
{
   	if (voice==1)
          return copy_to_user (buf,substream->runtime->dma_area->buf
           + (substream->runtime->dma_area->size/2) + pos,count);
        else
          return copy_to_user (buf,substream->runtime->dma_area->buf+pos,count);
}
#endif
#endif

static snd_pcm_hardware_t snd_es1968_playback = {
	SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_STREAM |
	    SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_INTERLEAVE |
	    SND_PCM_CHNINFO_BLOCK_TRANSFER | SND_PCM_CHNINFO_MMAP_VALID,	/* flags */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_48000,	/* supported Rate */
	4000,			/* min. rate */
	48000,			/* max. rate */
	1,			/* min. voices */
	2,			/* max. voices */
	2048,			/* min. fragment size */
	65536,			/* max. fragment size */
	3,			/* fragment align */
	0,			/* FIFO size (unknown) */
	4,			/* transfer block size */
	snd_es1968_playback_ioctl,
	snd_es1968_playback_prepare,
	snd_es1968_playback_trigger,
	snd_es1968_playback_pointer
};

static snd_pcm_hardware_t snd_es1968_capture = {
	/* Matze: No mmap() for record at the moment */
	/*SND_PCM_CHNINFO_MMAP | */ SND_PCM_CHNINFO_STREAM |
	    SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_INTERLEAVE |
	    SND_PCM_CHNINFO_BLOCK_TRANSFER
	    /*SND_PCM_CHNINFO_MMAP_VALID */ ,
	/* flags */
	/*SND_PCM_FMT_U8 |*/ SND_PCM_FMT_S16_LE,	/* hardware formats */
	SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_48000,	/* supported Rate */
	4000,			/* min. rate */
	48000,			/* max. rate */
	1,			/* min. voices */
	1,			/* max. voices */
	2048,			/* min. fragment size */
	65536,			/* max. fragment size */
	3,			/* fragment align */
	0,			/* FIFO size (unknown) */
	4,			/* transfer block size */
	snd_es1968_capture_ioctl,
	snd_es1968_capture_prepare,
	snd_es1968_capture_trigger,
	snd_es1968_capture_pointer
};

static void snd_es1968_pcmp_free_subchn(void *private_data)
{
	esschanp_t *es = snd_magic_cast(esschanp_t, private_data, );
	es1968_t *esm;

	if (es) {
		esm = es->card;
		/* Free APUs */
		esm->apu[es->apu[0]] = free;
		esm->apu[es->apu[1]] = free;
		snd_magic_kfree(es);
	}
}

static void snd_es1968_pcmc_free_subchn(void *private_data)
{
	esschanc_t *es = snd_magic_cast(esschanc_t, private_data, );
	es1968_t *esm;

	if (es) {
		esm = es->card;
		/* Free APUs */
		esm->apu[es->apu[0]] = free;
		esm->apu[es->apu[1]] = free;
		esm->apu[es->apu[2]] = free;
		esm->apu[es->apu[3]] = free;
		snd_magic_kfree(es);
	}
}

/* *************
   * DMA Hack! *
   *************/

/* Because the Maestro can only take Adresses relative to the PCM Base Adress
   Register :( */

/* This is the original ALSA DMA Alloc code, but changed to use parts of a
   big buffer, which is allocated on driver loading... */

int snd_es1968_init_dmabuf(es1968_t * esm)
{
	int size = esm->gesbuf * 1024;
	int pg = 0;
	char *buf = NULL;

	while (!buf && size >= PAGE_SIZE) {
		buf = snd_malloc_pages(size, &pg, 0);
		if (!buf)
			size >>= 1;
	}

	if (buf == NULL)
		return -ENOMEM;
	else
		esm->firstbuf = buf;
	return 0;
}

int snd_es1968_free_dmabuf(es1968_t * esm)
{
	if (esm->firstbuf) {
		snd_free_pages(esm->firstbuf, esm->gesbuf * 1024);
		esm->firstbuf = NULL;
		return 0;
	}
#ifdef ESM_EDEBUG
	snd_printk("snd_es1968_free_dmabuf: No Buffer!\n");
#endif
	return -1;
}

/* Matze: This is my own Memory Management...
   		It's not optimal, cause it only get fragments at the end of the
                buffer... */
char *snd_es1968_get_dmabuf(es1968_t * esm, int size)
{
	esmdma_t *esd;
	esmdma_t *o,*p;
	char *lastbuf = NULL;

	/* Search last Buf */
	o = p = esm->bufs;
	while (p != NULL) {
		if (p->buf + p->size > lastbuf)
			lastbuf = p->buf + p->size;
                o = p;
		p = p->next;
	}
        
	if (lastbuf == NULL)
		lastbuf = esm->firstbuf;

	if ((lastbuf + size) - esm->firstbuf > esm->gesbuf * 1024) {
#ifdef ESM_EDEBUG
		snd_printk
		    ("snd_es1968_get_dmabuf: Buf full (Set total Buf higher!)\n");
#endif
		return NULL;
	} 
        
        esd = (esmdma_t *) snd_magic_kcalloc(esmdma_t, 0, GFP_KERNEL);
                
        if (esd == NULL)
	       return NULL;

        esd->buf = lastbuf;
	esd->size = size;
	esd->next = NULL;
	if (o != NULL)
		o->next = esd;
	else
		esm->bufs = esd;
	
	return lastbuf;
}

void snd_es1968_back_dmabuf(es1968_t * esm, char *buf)
{
	esmdma_t *esd = NULL;
	esmdma_t *o,*p;
	
	/* Search last Buf */
	p = esm->bufs;
	o = NULL;
	while (p != NULL) {
		if (p->buf == buf) {
			esd = p;
			break;
		}
		o = p;
		p = p->next;
	}

	if (p == NULL) {
#ifdef ESM_EDEBUG
		snd_printk("snd_es1968_back_dmabuf: Free Error!\n");
#endif
		return;
	}

	if (o == NULL) {
		esm->bufs = esd->next;
	} else {
		o->next = p->next;
	}
	snd_magic_kfree(esd);
	return;
}

void snd_es1968_dma_free1(es1968_t * esm, snd_card_t * card, snd_dma_t * dma,
		       snd_dma_area_t * area)
{
	if (area->mmaped) {	/* wait */
		area->mmap_free = 1;
		return;
	}
	area->mmap_free = 0;
	area->owner = NULL;
	if (area->buf) {
		/* free DMA buffer */
		if (dma->type == SND_DMA_TYPE_ISA &&
		    dma->areas == area && area->next == NULL)
			snd_dma_disable(dma->dma);
		/* Change... */
#if 0
		snd_free_pages(area->buf, area->size);
#endif
		snd_es1968_back_dmabuf(esm, area->buf);
		/* End Change */
		area->buf = NULL;
		area->size = 0;
		if (dma->areas == area) {
			dma->areas = area->next;
		} else {
			snd_dma_area_t *a = dma->areas;
			while (a->next && a->next != area)
				a = a->next;
			if (a->next == NULL) {
				snd_printk
				    ("snd_dma_free1: INTERNAL ERROR\n");
			} else {
				a->next = area->next;
			}
		}
		snd_kfree(area);
	}
}

void snd_es1968_dma_free(es1968_t * esm, snd_card_t * card,
			 snd_dma_area_t * area)
{
	snd_dma_t *dma;

	if (card == NULL || area == NULL)
		return;
	dma = area->dma;
	if (dma == NULL)
		return;
	if (dma->type == SND_DMA_TYPE_HARDWARE)
		return;

	down(&dma->mutex);
	snd_es1968_dma_free1(esm, card, dma, area);
	up(&dma->mutex);
}

int snd_es1968_dma_is_multi(snd_dma_t * dma, char *owner)
{
	if (!dma->multi)
		return 0;
	if (dma->multi_match[0] == NULL)
		return 1;
	if (dma->areas) {
		if (dma->areas->owner == NULL ||
		    strcmp(dma->areas->owner, owner)) return 0;
	}
	if (!strcmp(dma->multi_match[0], owner) ||
	    (dma->multi_match[1] && !strcmp(dma->multi_match[1], owner)))
		return 1;
	return 0;
}

int snd_es1968_dma_malloc(es1968_t * esm, snd_card_t * card, snd_dma_t * dma,
			  char *owner, snd_dma_area_t ** rarea)
{
	unsigned char *buf;
	long size;
	int pg, add_area = 0;
	snd_dma_area_t *area;

	*rarea = NULL;
	if (card == NULL || dma == NULL)
		return -EINVAL;
	if (dma->type == SND_DMA_TYPE_HARDWARE)
		return -EINVAL;
	down(&dma->mutex);
	if (!snd_es1968_dma_is_multi(dma, owner) && dma->areas) {
		area = dma->areas;
		if (area->owner) {
			if (area->mmaped) {
				area->mmap_free = 0;
				up(&dma->mutex);
				return 0;
			} else {
				up(&dma->mutex);
				return -EBUSY;
			}
		}
	} else {
		area = (snd_dma_area_t *) snd_kcalloc(sizeof(*area), GFP_KERNEL);
		if (area == NULL) {
			up(&dma->mutex);
			return -ENOMEM;
		}
		add_area = 1;
	}
	area->dma = dma;
	area->mmap_free = 0;
	buf = NULL;
	size = dma->rsize;
	pg = 0;
	/* Change... */
#if 0
	while (!buf && size >= PAGE_SIZE) {
		buf =
		    snd_malloc_pages(size, &pg,
				     dma->type == SND_DMA_TYPE_ISA
				     || dma->type ==
				     SND_DMA_TYPE_PCI_16MB ? 1 : 0);
		if (!buf)
			size >>= 1;
	}
#endif
	buf = snd_es1968_get_dmabuf(esm, size);
	/* End Change */
	if (buf) {
		/* Change */
		area->size = size;
		/* End Change */
		area->buf = buf;
		area->owner = owner;
		if (add_area) {
			area->next = dma->areas;
			dma->areas = area;
		}
		*rarea = area;
	} else {
		if (add_area)
			snd_kfree(area);
	}
	up(&dma->mutex);
	return buf == NULL ? -ENOMEM : 0;
}

int snd_es1968_pcm_dma_alloc(es1968_t * esm, snd_pcm_subchn_t * subchn,
			  snd_dma_t * dma, char *ident)
{
	int err;
	snd_dma_area_t *area;
	snd_pcm_runtime_t *runtime = subchn->runtime;

	if (
	    (err =
	     snd_es1968_dma_malloc(esm, subchn->pcm->card, dma, ident,
				&area)) < 0) return err;
	runtime->dma_area = area;
	if (area->size < 4096) {
		snd_printd("Invalid audio DMA size - %li\n", area->size);
		snd_es1968_dma_free(esm, subchn->pcm->card, area);
		return -ENOMEM;
	}
	runtime->flags |= SND_PCM_FLG_DMA_OK;
	return 0;
}

int snd_es1968_pcm_dma_free(es1968_t * esm, snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;

	if (!(runtime->flags & SND_PCM_FLG_DMA_OK))
		return -EINVAL;
	snd_es1968_dma_free(esm, subchn->pcm->card, runtime->dma_area);
	runtime->dma_area = NULL;
	runtime->flags &= ~SND_PCM_FLG_DMA_OK;
	return 0;
}

/* **********************
   * Back to ALSA Stuff *
   **********************/

static int snd_es1968_playback_open(void *private_data,
				    snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;
	es1968_t *esm = snd_magic_cast(es1968_t, private_data, -ENXIO);
	esschanp_t *es;
	u8 apu1;
	int err;

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_playback_open!\n");
#endif

	/* Search 2 APUs */
	err = -EFAULT;
	for (apu1 = 0; apu1 < NR_APUS; apu1 += 2)
		if ((esm->apu[apu1] == free)
		    && (esm->apu[apu1 + 1] == free)) {
			err = 0;
			break;
		}

	if (err)
		return err;

	/* Register APU */
	esm->apu[apu1] = esm->apu[apu1 + 1] = pcm_play;

	if ((err =
	     snd_es1968_pcm_dma_alloc(esm, subchn, esm->dma1ptr,
				   "ESS Maestro 2E - DAC")) < 0)
		return err;

	/* In a future Version of the Kernel this should be ommited by malloc under
	   256 MB Zone */
	if ((virt_to_bus(subchn->runtime->dma_area->buf) - 1) &
	    ~((1 << 28) - 1)) {
#ifdef ESM_EDEBUG
		snd_printk
		    ("snd_es1968_playback_open: Buffer Adresse to big!\n");
#endif
		snd_es1968_pcm_dma_free(esm, subchn);
		return -1;
	}

	/* Check! This shouldn't be required */
	if (subchn->runtime->dma_area->size > 65537) {
#ifdef ESM_EDEBUG
		snd_printk
		    ("snd_es1968_playback_open: DMA Area to big! Size:%ld\n",
		     subchn->runtime->dma_area->size);
#endif
		snd_es1968_pcm_dma_free(esm, subchn);
		return -1;
	}

	es = snd_magic_kcalloc(esschanp_t, 0, GFP_KERNEL);
	if (!es) {
		snd_printk("Mem Error!\n");
		snd_es1968_pcm_dma_free(esm, subchn);
		return -ENOMEM;
	}

	es->card = esm;
	es->apu[0] = apu1;
	es->apu[1] = apu1 + 1;
	es->apu_mode[0] = 0;
	es->apu_mode[1] = 0;
	es->enable = 0;

	runtime->private_data = es;
	runtime->private_free = snd_es1968_pcmp_free_subchn;
	runtime->hw = &snd_es1968_playback;

	/* Start Bob */
	esm->bobclient++;
	if (esm->bobclient == 1)
		esm_bob_start(esm);

	snd_pcm_set_mixer(subchn, esm->mixer->device, esm->ac97->me_playback);
	return 0;
}

static int snd_es1968_capture_open(void *private_data,
				   snd_pcm_subchn_t * subchn)
{
	snd_pcm_runtime_t *runtime = subchn->runtime;
	es1968_t *esm = snd_magic_cast(es1968_t, private_data, -ENXIO);
	esschanc_t *es;
	u8 apu1, apu2;
	int err;
	char *mixbuf;

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_capture_open!\n");
#endif

	/* Search 2 APUs */
	err = -EFAULT;
	for (apu1 = 0; apu1 < NR_APUS; apu1 += 2)
		if ((esm->apu[apu1] == free)
		    && (esm->apu[apu1 + 1] == free)) {
			err = 0;
			break;
		}

	if (err)
		return err;

	/* Search 2 more APUs */
	err = -EFAULT;
	for (apu2 = apu1 + 2; apu2 < NR_APUS; apu2 += 2)
		if ((esm->apu[apu2] == free)
		    && (esm->apu[apu2 + 1] == free)) {
			err = 0;
			break;
		}

	if (err)
		return err;

	/* Register APU */
	esm->apu[apu1] = esm->apu[apu1 + 1] = pcm_capture;
	esm->apu[apu2] = esm->apu[apu2 + 1] = pcm_rateconv;

	if ((err =
	     snd_es1968_pcm_dma_alloc(esm, subchn, esm->dma1ptr,
				   "ESS Maestro 2E - ADC")) < 0)
		return err;

	/* Get MixBuffer */
	if (
	    (mixbuf =
	     snd_es1968_get_dmabuf(esm,
				subchn->runtime->dma_area->size)) ==
	    NULL) return -ENOMEM;

	/* In a future Version of the Kernel this should be ommited by malloc under
	   256 MB Zone */
	if ((virt_to_bus(subchn->runtime->dma_area->buf) - 1) &
	    ~((1 << 28) - 1)) {
#ifdef ESM_EDEBUG
		snd_printk
		    ("snd_es1968_capture_open: Buffer Adresse to big!\n");
#endif
		snd_es1968_pcm_dma_free(esm, subchn);
		return -ENOMEM;
	}

	/* Check! This shouldn't be needet */
	if (subchn->runtime->dma_area->size > 65537) {
#ifdef ESM_EDEBUG
		snd_printk
		    ("snd_es1968_capture_open: DMA Area to big! Size:%ld\n",
		     subchn->runtime->dma_area->size);
#endif
		snd_es1968_pcm_dma_free(esm, subchn);
		return -ENOMEM;
	}

	es = snd_magic_kcalloc(esschanc_t, 0, GFP_KERNEL);
	if (!es) {
#ifdef ESM_EDEBUG
		snd_printk("snd_capture_open: Mem Error!\n");
#endif
		snd_es1968_pcm_dma_free(esm, subchn);
		return -ENOMEM;
	}

	es->card = esm;
	es->apu[0] = apu1;
	es->apu[1] = apu1 + 1;
	es->apu[2] = apu2;
	es->apu[3] = apu2 + 1;
	es->apu_mode[0] = 0;
	es->apu_mode[1] = 0;
	es->apu_mode[2] = 0;
	es->apu_mode[3] = 0;
	es->mixbuf = mixbuf;
	es->enable = 0;

	runtime->private_data = es;
	runtime->private_free = snd_es1968_pcmc_free_subchn;
	runtime->hw = &snd_es1968_capture;

	/* Start Bob */
	esm->bobclient++;
	if (esm->bobclient == 1)
		esm_bob_start(esm);

	snd_pcm_set_mixer(subchn, esm->mixer->device, esm->ac97->me_capture);
	return 0;
}

static int snd_es1968_playback_close(void *private_data,
				     snd_pcm_subchn_t * subchn)
{
	es1968_t *esm = (es1968_t *) private_data;

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_playback_close!\n");
#endif

	esm->bobclient--;
	if (esm->bobclient <= 0)
		esm_bob_stop(esm);

	snd_es1968_pcm_dma_free(esm, subchn);
	return 0;
}

static int snd_es1968_capture_close(void *private_data,
				    snd_pcm_subchn_t * subchn)
{
	es1968_t *esm = (es1968_t *) private_data;
	esschanc_t *es = snd_magic_cast(esschanc_t, subchn->runtime->private_data, -ENXIO);

#ifdef ESM_DDEBUG
	snd_printk("snd_es1968_capture_close!\n");
#endif

	esm->bobclient--;
	if (esm->bobclient <= 0)
		esm_bob_stop(esm);

	snd_es1968_back_dmabuf(esm, es->mixbuf);
	snd_es1968_pcm_dma_free(esm, subchn);
	return 0;
}

static void snd_es1968_pcm_free(void *private_data)
{
	es1968_t *esm = snd_magic_cast(es1968_t, private_data, );
	snd_es1968_free_dmabuf(esm);
	esm->pcm = NULL;
}

int snd_es1968_pcm(es1968_t * esm, int device, snd_pcm_t ** rpcm)
{
	snd_pcm_t *pcm;
	int err;

	*rpcm = NULL;

	/* DMA Hack */
	if ((err = snd_es1968_init_dmabuf(esm)) < 0)
		return err;

	/* Set PCMBAR */
	set_base_registers(esm, esm->firstbuf);

	if ((err =
	     snd_pcm_new(esm->card, "ess maestro", device, esm->pcmp,
			 esm->pcmc, &pcm)) < 0)
		return err;

	pcm->private_data = esm;
	pcm->private_free = snd_es1968_pcm_free;

	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].private_data = esm;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].open = snd_es1968_playback_open;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].close = snd_es1968_playback_close;

	pcm->chn[SND_PCM_CHANNEL_CAPTURE].private_data = esm;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].open = snd_es1968_capture_open;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].close = snd_es1968_capture_close;

	if (esm->pcmc > 0)
		pcm->info_flags =
		    SND_PCM_INFO_PLAYBACK | SND_PCM_INFO_CAPTURE |
		    SND_PCM_INFO_DUPLEX;
	else
		pcm->info_flags = SND_PCM_INFO_PLAYBACK;

	strcpy(pcm->name, "ESS Maestro");

	*rpcm = esm->pcm = pcm;

	return 0;
}

/* ******************
   *  Mixer Stuff   *
   ******************
 */

static void snd_es1968_ac97_reset(es1968_t * esm)
{
	unsigned long ioaddr = esm->io_port;

	unsigned short save_68;
	unsigned short w;
	unsigned int vend;

	outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
	outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
	outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);

	/* reset the first codec */
	outw(0x0000, ioaddr + 0x36);
	save_68 = inw(ioaddr + 0x68);
	pci_read_config_word(esm->pci, 0x58, &w);	/* something magical with gpio and bus arb. */
	pci_read_config_dword(esm->pci, PCI_SUBSYSTEM_VENDOR_ID, &vend);
	if (w & 1)
		save_68 |= 0x10;
	outw(0xfffe, ioaddr + 0x64);	/* tickly gpio 0.. */
	outw(0x0001, ioaddr + 0x68);
	outw(0x0000, ioaddr + 0x60);
	udelay(20);
	outw(0x0001, ioaddr + 0x60);
	mdelay(20);

	outw(save_68 | 0x1, ioaddr + 0x68);	/* now restore .. */
	outw((inw(ioaddr + 0x38) & 0xfffc) | 0x1, ioaddr + 0x38);
	outw((inw(ioaddr + 0x3a) & 0xfffc) | 0x1, ioaddr + 0x3a);
	outw((inw(ioaddr + 0x3c) & 0xfffc) | 0x1, ioaddr + 0x3c);

	/* now the second codec */
	outw(0x0000, ioaddr + 0x36);
	outw(0xfff7, ioaddr + 0x64);
	save_68 = inw(ioaddr + 0x68);
	outw(0x0009, ioaddr + 0x68);
	outw(0x0001, ioaddr + 0x60);
	udelay(20);
	outw(0x0009, ioaddr + 0x60);
	mdelay(500);		/* .. ouch.. */
	outw(inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38);
	outw(inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a);
	outw(inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c);

#if 0				/* the loop here needs to be much better if we want it.. */
	snd_printk("trying software reset\n");
	/* try and do a software reset */
	outb(0x80 | 0x7c, ioaddr + 0x30);
	for (w = 0;; w++) {
		if ((inw(ioaddr + 0x30) & 1) == 0) {
			if (inb(ioaddr + 0x32) != 0)
				break;

			outb(0x80 | 0x7d, ioaddr + 0x30);
			if (((inw(ioaddr + 0x30) & 1) == 0)
			    && (inb(ioaddr + 0x32) != 0))
				break;
			outb(0x80 | 0x7f, ioaddr + 0x30);
			if (((inw(ioaddr + 0x30) & 1) == 0)
			    && (inb(ioaddr + 0x32) != 0))
				break;
		}

		if (w > 10000) {
			outb(inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37);	/* do a software reset */
			mdelay(500);	/* oh my.. */
			outb(inb(ioaddr + 0x37) & ~0x08,
				ioaddr + 0x37);
			udelay(1);
			outw(0x80, ioaddr + 0x30);
			for (w = 0; w < 10000; w++) {
				if ((inw(ioaddr + 0x30) & 1) == 0)
					break;
			}
		}
	}
#endif
	if (vend == NEC_VERSA_SUBID1 || vend == NEC_VERSA_SUBID2) {
		/* turn on external amp? */
		outw(0xf9ff, ioaddr + 0x64);
		outw(inw(ioaddr + 0x68) | 0x600, ioaddr + 0x68);
		outw(0x0209, ioaddr + 0x60);
	}
}

static void snd_es1968_mixer_free_ac97(void *private_data)
{
	es1968_t * esm = snd_magic_cast(es1968_t, private_data, );
	esm->mixer = NULL;
}

int snd_es1968_mixer(es1968_t * esm, int device, snd_pcm_t * pcm,
		     snd_kmixer_t ** rmixer)
{
	ac97_t ac97;
	snd_kmixer_t *mixer;
	int err;

	*rmixer = NULL;

	memset(&ac97, 0, sizeof(ac97));
	ac97.write = snd_es1968_codec_write;
	ac97.read = snd_es1968_codec_read;
	ac97.private_data = esm;
	ac97.private_free = snd_es1968_mixer_free_ac97;
	if ((err =
	     snd_ac97_mixer(esm->card, device, &ac97, 1,
			    &pcm->device, &mixer)) < 0)
		return err;
	esm->ac97 = snd_magic_cast(ac97_t, (mixer->private_data), -ENXIO);
	*rmixer = esm->mixer = mixer;
	return 0;
}

/* *****************************
   * Config & Create Soundcard *
   *****************************/

static void snd_es1968_ringbus_setup(es1968_t * esm)
{
	u32 n;
	u16 w;
	unsigned long iobase = esm->io_port;

	/* Ring Bus Control A */

	n = inl(iobase + 0x34);
	n &= ~0xF000;
	n |= 12 << 12;		/* Direct Sound, Stereo */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n &= ~0x0F00;		/* Modem off */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n &= ~0x00F0;
	n |= 9 << 4;		/* DAC, Stereo */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n &= ~0x000F;		/* ASSP off */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n |= (1 << 29);		/* Enable ring bus */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n |= (1 << 28);		/* Enable serial bus */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n &= ~0x00F00000;	/* MIC off */
	outl(n, iobase + 0x34);

	n = inl(iobase + 0x34);
	n &= ~0x000F0000;	/* I2S off */
	outl(n, iobase + 0x34);

	/* Ring Bus Control B */

	n = inl(iobase + 0x38);
	n &= ~RINGB_EN_SPDIF;	/* SPDIF off */
	outl(n, iobase + 0x38);

	/* Enable IRQ's */

	if (esm->midi)
		w = ESM_HIRQ_DSIE | ESM_HIRQ_MPU401;
	else
		w = ESM_HIRQ_DSIE;

	outw(w, esm->io_port + ESM_PORT_HOST_IRQ);
}

static void snd_es1968_reset(es1968_t * esm)
{
	/* Reset */
	outw(ESM_RESET_MAESTRO | ESM_RESET_DIRECTSOUND,
		esm->io_port + ESM_PORT_HOST_IRQ);
	udelay(10);
	outw(0x0000, esm->io_port + ESM_PORT_HOST_IRQ);
	udelay(10);
}

#ifdef ESM_SYNTHHACK
/* FixMe! *//* Only a little Hack for experiments */
void esmst_set(es1968_t * s);
#endif

int snd_es1968_free(es1968_t * esm)
{
	snd_magic_kfree(esm);
	return 0;
}

int snd_es1968_create(snd_card_t * card,
		      struct pci_dev *pci,
		      snd_dma_t * dma1ptr,
		      snd_irq_t * irqptr,
		      int midi_enable,
		      int gesbuf, int pcmp, int pcmc, es1968_t ** resm)
{
	es1968_t *esm;
	int i, err;
	u16 w;
	static snd_device_ops_t ops = {
		(snd_dev_free_t *)snd_es1968_free,
		NULL,
		NULL
	};

	*resm = NULL;

	esm = (es1968_t *) snd_magic_kcalloc(es1968_t, 0, GFP_KERNEL);
	if (!esm)
		return -ENOMEM;

	/* Set Vars */

	esm->type = card->type;
	spin_lock_init(&esm->reg_lock);
	esm->card = card;
	esm->pci = pci;
	esm->irqptr = irqptr;
	esm->dma1ptr = dma1ptr;
	esm->midi = midi_enable;
	esm->gesbuf = gesbuf;
	esm->pcmp = pcmp;
	esm->pcmc = pcmc;

	esm->io_port = pci_resource_start(pci, 0);

#ifdef ESM_DDEBUG
	snd_printk("snd_maestro_create: io: 0x%lx\n", esm->io_port);
#endif
	/* Clear Maestro_map */
	for (i = 0; i < 32; i++)
		esm->maestro_map[i] = 0;

	/* Clear Apu Map */
	for (i = 0; i < NR_APUS; i++)
		esm->apu[i] = free;

	esm->bobclient = 0;
	esm->firstbuf = NULL;
	esm->bufs = NULL;

	/* just to be sure */
	pci_set_master(pci);

	/* PCI Config Regs */

#ifdef ESM_DDEBUG
	pci_read_config_word(esm->pci, 0x06, &w);
	snd_printk("ESM: Dev Cap:%d  %x\n", w, w);
	pci_read_config_word(esm->pci, 0x08, &w);
	snd_printk("ESM: Dev Rev:%d  %x\n", w, w);
	pci_read_config_word(esm->pci, 0x2c, &w);
	snd_printk("ESM: Sub Vendor:%d  %x\n", w, w);
	pci_read_config_word(esm->pci, 0x2e, &w);
	snd_printk("ESM: Sub ID:%d  %x\n", w, w);
#endif

	/* Disable ACPI */

	pci_write_config_word(esm->pci, 0x54, 0x00000000);
	pci_write_config_word(esm->pci, 0x56, 0x00000000);

	/* Config Reg A */

	pci_read_config_word(esm->pci, ESM_CONFIG_A, &w);

	/*      Use TDMA for now. TDMA works on all boards, so while its
	 *      not the most efficient its the simplest. */
	w &= ~DMA_CLEAR;	/* Clear DMA bits */
	w |= DMA_TDMA;		/* TDMA on */
	w &= ~(PIC_SNOOP1 | PIC_SNOOP2);	/* Clear Pic Snoop Mode Bits */
	w &= ~SAFEGUARD;	/* Safeguard off */
	w |= POST_WRITE;	/* Posted write */
	w |= ISA_TIMING;	/* ISA timing on */
	/* XXX huh?  claims to be reserved.. */
	w &= ~SWAP_LR;		/* swap left/right 
				   seems to only have effect on SB
				   Emulation */
	w &= ~SUBTR_DECODE;	/* Subtractive decode off */

	pci_write_config_word(esm->pci, ESM_CONFIG_A, w);

	/* Config Reg B */

	pci_read_config_word(esm->pci, ESM_CONFIG_B, &w);

	w &= ~(1 << 15);	/* Turn off internal clock multiplier */
	/* XXX how do we know which to use? */
	w &= ~(1 << 14);	/* External clock */

	w &= ~SPDIF_CONFB;	/* disable S/PDIF */
	w &= ~HWV_CONFB;	/* HWV off */
	w &= ~DEBOUNCE;		/* Debounce off */
	w &= ~GPIO_CONFB;	/* GPIO 4:5 */
	w |= CHI_CONFB;		/* Disconnect from the CHI.  Enabling this made a dell 7500 work. */
	w &= ~IDMA_CONFB;	/* IDMA off (undocumented) */
	w &= ~MIDI_FIX;		/* MIDI fix off (undoc) */
	w &= ~(1 << 1);		/* reserved, always write 0 */
	w &= ~IRQ_TO_ISA;	/* IRQ to ISA off (undoc) */

	pci_write_config_word(esm->pci, ESM_CONFIG_B, w);

	/* DDMA off */

	pci_read_config_word(esm->pci, 0x60, &w);
	w &= ~(1 << 0);
	pci_write_config_word(esm->pci, 0x60, w);

	/* Set ESM_LEGACY_AUDIO_CONTROL */

	pci_read_config_word(esm->pci, ESM_LEGACY_AUDIO_CONTROL, &w);

	w &= ~ESS_ENABLE_AUDIO;	/* Disable Legacy Audio */
	w &= ~ESS_ENABLE_SERIAL_IRQ;	/* Disable SIRQ */
	w &= ~(0x1f);		/* disable mpu irq/io, game port, fm, SB */

	pci_write_config_word(esm->pci, ESM_LEGACY_AUDIO_CONTROL, w);

	/* Sound Reset */

	snd_es1968_reset(esm);

	snd_es1968_ac97_reset(esm);

	snd_es1968_ringbus_setup(esm);

	/* it appears some maestros (dell 7500) only work if these are set,
	   regardless of wether we use the assp or not. */

	outb(0, esm->io_port + ASSP_CONTROL_B);
	outb(3, esm->io_port + ASSP_CONTROL_A);	/* M: Reserved bits... */
	outb(0, esm->io_port + ASSP_CONTROL_C);	/* M: Disable ASSP, ASSP IRQ's and FM Port */

	for (i = 0; i < 16; i++) {
		/* Write 0 into the buffer area 0x1E0->1EF */
		outw(0x01E0 + i, esm->io_port + WC_INDEX);
		outw(0x0000, esm->io_port + WC_DATA);

		/* The 1.10 test program seem to write 0 into the buffer area
		 * 0x1D0-0x1DF too.*/
		outw(0x01D0 + i, esm->io_port + WC_INDEX);
		outw(0x0000, esm->io_port + WC_DATA);
	}
	wave_set_register(esm, IDR7_WAVE_ROMRAM,
			  (wave_get_register
			   (esm, IDR7_WAVE_ROMRAM) & 0xFF00));
	wave_set_register(esm, IDR7_WAVE_ROMRAM,
			  wave_get_register(esm,
					    IDR7_WAVE_ROMRAM) | 0x100);
	wave_set_register(esm, IDR7_WAVE_ROMRAM,
			  wave_get_register(esm,
					    IDR7_WAVE_ROMRAM) & ~0x200);
	wave_set_register(esm, IDR7_WAVE_ROMRAM,
			  wave_get_register(esm,
					    IDR7_WAVE_ROMRAM) | ~0x400);


	maestro_write(esm, IDR2_CRAM_DATA, 0x0000);
	maestro_write(esm, 0x08, 0xB004);
	/* Now back to the DirectSound stuff */
	maestro_write(esm, 0x09, 0x001B);
	maestro_write(esm, 0x0A, 0x8000);
	maestro_write(esm, 0x0B, 0x3F37);
	maestro_write(esm, 0x0C, 0x0098);

	/* parallel out ?? */
	maestro_write(esm, 0x0C,
		      (maestro_read(esm, 0x0C) & ~0xF000) | 0x8000);
	/* parallel in, has something to do with recording :) */
	maestro_write(esm, 0x0C,
		      (maestro_read(esm, 0x0C) & ~0x0F00) | 0x0500);

	maestro_write(esm, 0x0D, 0x7632);

	/* Wave cache control on - test off, sg off, 
	   enable, enable extra chans 1Mb */

	w = inw(esm->io_port + WC_CONTROL);

	w &= ~0xFA00;		/* Seems to be reserved? I don't know */
	w |= 0xA000;		/* reserved... I don't know */
	w &= ~0x0200;		/* Channels 56,57,58,59 as Extra Play,Rec Channel enable
				   Seems to crash the Computer if enabled... */
	w |= 0x0100;		/* Wave Cache Operation Enabled */
	w |= 0x0080;		/* Channels 60/61 as Placback/Record enabled */
	w &= ~0x0060;		/* Clear Wavtable Size */
	w |= 0x0020;		/* Wavetable Size : 1MB */
	/* Bit 4 is reserved */
	w &= ~0x000C;		/* DMA Stuff? I don't understand what the datasheet means */
	/* Bit 1 is reserved */
	w &= ~0x0001;		/* Test Mode off */

	outw(w, esm->io_port + WC_CONTROL);

	/* Now clear the APU control ram */
	for (i = 0; i < NR_APUS; i++) {
		for (w = 0; w < NR_APU_REGS; w++)
			apu_set_register(esm, i, w, 0);

	}

#ifdef ESM_SYNTHHACK
	/* FixMe! */
	/* This only a little hacking to experiment with Synth programming */
	esmst_set(esm);
#endif

	if ((err = snd_device_new(card, SND_DEV_LOWLEVEL, esm, 0, &ops, NULL)) < 0) {
		snd_es1968_free(esm);
		return err;
	}

	*resm = esm;

	return 0;
}

/* *************
   * Midi Part *
   *************/
int snd_es1968_midi(es1968_t * esm, int device, snd_rawmidi_t ** rawmidi)
{
	int mpu_port;
	int err;
	snd_rawmidi_t *rm;

	rm = NULL;

	mpu_port = esm->io_port + ESM_MPU401_PORT;

	err = snd_mpu401_uart_new(esm->card, device,
				  MPU401_HW_MPU401,
				  mpu_port, esm->irqptr->irq, &rm);
	if (err < 0)
		return err;

	*rawmidi = esm->rmidi = rm;
	return 0;
}

/* ****************
   * IRQ Handling *
   ****************/
static void ess_update_ptrp(snd_pcm_subchn_t * subchn)
{
	unsigned int hwptr;
	unsigned int diff;
	esschanp_t *es;
        
        if (subchn->runtime && subchn->runtime->private_data) {
		es = snd_magic_cast(esschanp_t, subchn->runtime->private_data, );

		if (es->mode & ESM_MODE_PLAY) {
			spin_lock(&es->reg_lock);
			hwptr = get_dmaa(es) % es->transs;

			if ((es->fmt & ESS_FMT_MASK) ==
			    (ESS_FMT_STEREO | ESS_FMT_16BIT))
				    hwptr <<= 1;

			diff = (es->transs + hwptr - es->hwptr) % es->transs;

			es->hwptr = hwptr;
			es->count += diff;

			if (es->count > es->transf) {
				spin_unlock(&es->reg_lock);
				snd_pcm_transfer_done(subchn);	/* Update */
				spin_lock(&es->reg_lock);
				es->count -= es->transf;
			}
#ifdef ESM_EDEBUG
			if (es->count > es->transf)

				snd_printk
				    ("ess_update_ptrp: IRQ to slow!\n");
#endif

			spin_unlock(&es->reg_lock);
		}
	}
}

static void ess_update_ptrc(snd_pcm_subchn_t * subchn)
{
	unsigned int hwptr;
	unsigned int diff;
	esschanc_t *es;
        
	if (subchn->runtime && subchn->runtime->private_data) {
		es = snd_magic_cast(esschanc_t, subchn->runtime->private_data, );

		if (es->mode & ESM_MODE_REC) {
			spin_lock(&es->reg_lock);
			hwptr = get_dmac(es) % es->transs;

			if (es->fmt & ESS_FMT_STEREO)
				hwptr <<= 1;

			diff = (es->transs + hwptr - es->hwptr) % es->transs;

			es->hwptr = hwptr;
			es->count += diff;

			if (es->count > es->transf) {
				spin_unlock(&es->reg_lock);
				snd_pcm_transfer_done(subchn);	/* Update */
				spin_lock(&es->reg_lock);
				es->count -= es->transf;
			}
#ifdef ESM_EDEBUG
			if (es->count > es->transf)

				snd_printk
				    ("ess_update_ptrc: IRQ to slow!\n");
#endif
			spin_unlock(&es->reg_lock);
		}
	}
}

void snd_es1968_interrupt(es1968_t * esm)
{
	u32 event;
	snd_pcm_subchn_t *subchn;


	if (!(event = inb(esm->io_port + 0x1A))) {
		return;
	}

	outw(inw(esm->io_port + 4) & 1, esm->io_port + 4);

#ifdef ESM_IDEBUG
	snd_printk("maestro int: %x\n", event);
#endif

	if (event & ESM_HWVOL_IRQ) {
		/* XXX if we have a hw volume control int enable
		   all the ints?  doesn't make sense.. */
		event = inw(esm->io_port + 0x18);
		outb(0xFF, esm->io_port + 0x1A);
#ifdef ESM_DDEBUG
		snd_printk("HWVol Int (Why this?) !\n");
#endif
	} else {
		/* else ack 'em all, i imagine */
		outb(0xFF, esm->io_port + 0x1A);
	}

	if (event & ESM_MPU401_IRQ) {
		snd_mpu401_uart_interrupt(esm->rmidi);
	}

	if (event & ESM_SOUND_IRQ) {
           	subchn = esm->pcm->chn[SND_PCM_CHANNEL_PLAYBACK].subchn;
		while (subchn) {
			ess_update_ptrp(subchn);
			subchn = subchn->next;
		}
		subchn = esm->pcm->chn[SND_PCM_CHANNEL_CAPTURE].subchn;
		while (subchn) {
			ess_update_ptrc(subchn);
			subchn = subchn->next;
		}
	}
}

/* ***************
   * Modul Stuff *
   ***************/

EXPORT_SYMBOL(snd_es1968_create);
EXPORT_SYMBOL(snd_es1968_free);
EXPORT_SYMBOL(snd_es1968_pcm);
EXPORT_SYMBOL(snd_es1968_mixer);
EXPORT_SYMBOL(snd_es1968_midi);
EXPORT_SYMBOL(snd_es1968_interrupt);
#ifdef ESM_SYNTHHACK
EXPORT_SYMBOL(snd_es1968_get_dmabuf);
EXPORT_SYMBOL(snd_es1968_back_dmabuf);
#endif

static int __init alsa_es1968_init(void)
{
	return 0;
}

static void __exit alsa_es1968_exit(void)
{
}

module_init(alsa_es1968_init)
module_exit(alsa_es1968_exit)
