/*
 * Emuxki BeOS Driver for Creative Labs SBLive!/Audigy series
 *
 * Copyright (c) 2002, Jerome Duval (jerome.duval@free.fr)
 *
*/
/* This code is derived from the NetBSD driver for Creative Labs SBLive! series
 *
 * Copyright (c) 2001 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Yannick Montulet.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the NetBSD
 *	Foundation, Inc. and its contributors.
 * 4. Neither the name of The NetBSD Foundation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <KernelExport.h>
#include <PCI.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <midi_driver.h>
#include "emuxki.h"
#include "debug.h"
#include "config.h"
#include "util.h"
#include "io.h"
#include <fcntl.h>
#include <unistd.h>
#include <malloc.h>
#include "multi.h"
#include "ac97.h"

status_t init_hardware(void);
status_t init_driver(void);
void uninit_driver(void);
const char ** publish_devices(void);
device_hooks * find_device(const char *);

static char pci_name[] = B_PCI_MODULE_NAME;
static pci_module_info	*pci;
static char mpu401_name[] = B_MPU_401_MODULE_NAME;
generic_mpu401_module * mpu401;


int32 num_cards;
emuxki_dev cards[NUM_CARDS];
int32 num_names;
char * names[NUM_CARDS*20+1];

status_t emuxki_init(emuxki_dev * card);

extern device_hooks multi_hooks;
extern device_hooks midi_hooks;

/* Hardware Dump */

static void 
dump_hardware_regs(device_config *config)
{
	LOG(("EMU_IPR = %#08x\n",emuxki_reg_read_32(config, EMU_IPR)));
	LOG(("EMU_INTE = %#08x\n",emuxki_reg_read_32(config, EMU_INTE)));
	LOG(("EMU_HCFG = %#08x\n",emuxki_reg_read_32(config, EMU_HCFG)));
	snooze(100);	
	/*emuxki_reg_write_8(config, EMU_AC97ADDRESS, EMU_AC97ADDRESS_READY);
	LOG(("EMU_AC97ADDRESS_READY = %#08x\n", emuxki_reg_read_16(config, EMU_AC97DATA)));*/
	
	/*emuxki_reg_write_8(config, EMU_AC97ADDRESS, EMU_AC97ADDRESS_ADDRESS);
	LOG(("EMU_AC97ADDRESS_ADDRESS = %#08x\n", emuxki_reg_read_16(config, EMU_AC97DATA)));*/
	
	/*LOG(("EMU_CHAN_CPF_STEREO = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CPF_STEREO)));
	LOG(("EMU_CHAN_FXRT = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FXRT)));
	LOG(("EMU_CHAN_PTRX = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PTRX)));
	LOG(("EMU_CHAN_DSL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_DSL)));
	LOG(("EMU_CHAN_PSST = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PSST)));
	LOG(("EMU_CHAN_CCCA = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CCCA)));
	LOG(("EMU_CHAN_Z1 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_Z1)));
	LOG(("EMU_CHAN_Z2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_Z2)));
	LOG(("EMU_CHAN_MAPA = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_MAPA)));
	LOG(("EMU_CHAN_MAPB = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_MAPB)));
	LOG(("EMU_CHAN_CVCF_CURRENTFILTER = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_CVCF_CURRENTFILTER)));
	LOG(("EMU_CHAN_VTFT_FILTERTARGET = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_VTFT_FILTERTARGET)));
	LOG(("EMU_CHAN_ATKHLDM = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ATKHLDM)));
	LOG(("EMU_CHAN_DCYSUSM = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_DCYSUSM)));
	LOG(("EMU_CHAN_LFOVAL1 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_LFOVAL1)));
	LOG(("EMU_CHAN_LFOVAL2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_LFOVAL2)));
	LOG(("EMU_CHAN_FMMOD = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FMMOD)));
	LOG(("EMU_CHAN_TREMFRQ = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_TREMFRQ)));
	LOG(("EMU_CHAN_FM2FRQ2 = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_FM2FRQ2)));
	LOG(("EMU_CHAN_ENVVAL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ENVVAL)));
	LOG(("EMU_CHAN_ATKHLDV = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ATKHLDV)));
	LOG(("EMU_CHAN_ENVVOL = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_ENVVOL)));
	LOG(("EMU_CHAN_PEFE = %#08x\n",emuxki_chan_read(config, 0, EMU_CHAN_PEFE)));*/
	
}

static void 
trace_hardware_regs(device_config *config)
{
	TRACE(("EMU_IPR = %#08x\n",emuxki_reg_read_32(config, EMU_IPR)));
	TRACE(("EMU_INTE = %#08x\n",emuxki_reg_read_32(config, EMU_INTE)));
	TRACE(("EMU_HCFG = %#08x\n",emuxki_reg_read_32(config, EMU_HCFG)));
}

/* Misc stuff relative to Emuxki */

int             emu10k1_recbuf_sizes[] = {
	0, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792,
	2048, 2560, 3072, 3584, 4096, 5120, 6144, 7168, 8192, 10240, 12288,
	14366, 16384, 20480, 24576, 28672, 32768, 40960, 49152, 57344, 65536
};

static uint32
emuxki_rate_to_pitch(uint32 rate)
{
	static uint32 logMagTable[128] = {
		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3,
		0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a,
		0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb,
		0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01,
		0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006,
		0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00,
		0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4,
		0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20,
		0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec,
		0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241,
		0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f,
		0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b,
		0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f,
		0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a,
		0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a,
		0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57,
		0xfd1a7, 0xfe8df
	};
	static uint8 logSlopeTable[128] = {
		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
	};
	int8          i;

	if (rate == 0)
		return 0;	/* Bail out if no leading "1" */
	rate *= 11185;		/* Scale 48000 to 0x20002380 */
	for (i = 31; i > 0; i--) {
		if (rate & 0x80000000) {	/* Detect leading "1" */
			return (((uint32) (i - 15) << 20) +
				logMagTable[0x7f & (rate >> 24)] +
				(0x7f & (rate >> 17)) *
				logSlopeTable[0x7f & (rate >> 24)]);
		}
		rate <<= 1;
	}
	return 0;		/* Should never reach this point */
}

/* Emuxki Memory management */

static emuxki_mem *
emuxki_mem_new(emuxki_dev *card, int ptbidx, size_t size)
{
	emuxki_mem *mem;

	if ((mem = malloc(sizeof(*mem))) == NULL)
		return (NULL);

	mem->ptbidx = ptbidx;
	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "emuxki buffer");
	mem->size = size;
	if (mem->area < B_OK) {
		free(mem);
		return NULL;
	}
	return mem;
}

static void
emuxki_mem_delete(emuxki_mem *mem)
{
	if(mem->area > B_OK)
		delete_area(mem->area);
	free(mem);
}

void *
emuxki_pmem_alloc(emuxki_dev *card, size_t size)
{
	int             i;//, s;
	size_t          numblocks;
	emuxki_mem *mem;
	uint32      j, *ptb, silentpage;

	ptb = card->ptb_log_base;
	silentpage = ((uint32)card->silentpage_phy_base) << 1;
	numblocks = size / EMU_PTESIZE;
	if (size % EMU_PTESIZE)
		numblocks++;
		
	PRINT(("emuxki_pmem_alloc : numblocks : %d\n", numblocks));

	for (i = 0; i < EMU_MAXPTE; i++) {
		PRINT(("emuxki_pmem_alloc : %d\n", i));
		if ((ptb[i] & EMU_CHAN_MAP_PTE_MASK) == silentpage) {
			/* We look for a free PTE */
			//s = splaudio();
			for (j = 0; j < numblocks; j++)
				if ((ptb[i + j] & EMU_CHAN_MAP_PTE_MASK)
				    != silentpage)
					break;
			if (j == numblocks) {
				PRINT(("emuxki_pmem_alloc : j == numblocks %d\n", j));
				if ((mem = emuxki_mem_new(card, i, size)) == NULL) {
					//splx(s);
					return (NULL);
				}
				PRINT(("emuxki_pmem_alloc : j == numblocks emuxki_mem_new ok\n"));
				for (j = 0; j < numblocks; j++)
					ptb[i + j] = (uint32) (
						(( ((uint32)mem->phy_base) +
						 j * EMU_PTESIZE) << 1)
						| (i + j));
				LIST_INSERT_HEAD(&(card->mem), mem, next);
				PRINT(("emuxki_pmem_alloc : j == numblocks returning\n"));
				
				//splx(s);
				return mem->log_base;
			} else {
				PRINT(("emuxki_pmem_alloc : j != numblocks %d\n", j));
				i += j;
			}
			//splx(s);
		}
	}
	return NULL;
}

void *
emuxki_rmem_alloc(emuxki_dev *card, size_t size)
{
	emuxki_mem *mem;
	//int             s;

	mem = emuxki_mem_new(card, EMU_RMEM, size);
	if (mem == NULL)
		return (NULL);

	//s = splaudio();
	LIST_INSERT_HEAD(&(card->mem), mem, next);
	//splx(s);

	return mem->log_base;
}

void
emuxki_mem_free(emuxki_dev *card, void *ptr)
{
	emuxki_mem 		*mem;
	size_t          numblocks;
	uint32      	i, *ptb, silentpage;

	ptb = card->ptb_log_base;
	silentpage = ((uint32)card->silentpage_phy_base) << 1;
	LOG(("emuxki_mem_free 1\n"));
	LIST_FOREACH(mem, &card->mem, next) {
		LOG(("emuxki_mem_free 2\n"));
		if (mem->log_base != ptr)
			continue;
		LOG(("emuxki_mem_free 3\n"));
		//s = splaudio();
		if (mem->ptbidx != EMU_RMEM) {
			LOG(("mem_size : %i\n", mem->size));
			numblocks = mem->size / EMU_PTESIZE;
			if (mem->size % EMU_PTESIZE)
				numblocks++;
			for (i = 0; i < numblocks; i++)
				ptb[mem->ptbidx + i] =
					silentpage | (mem->ptbidx + i);
		}
		LIST_REMOVE(mem, next);
		//splx(s);

		LOG(("freeing mem\n"));
		emuxki_mem_delete(mem);
		break;
	}
}

/* Emuxki channel functions */

static void
emuxki_chanparms_set_defaults(emuxki_channel *chan)
{
	chan->fxsend.a.level = chan->fxsend.b.level =
	chan->fxsend.c.level = chan->fxsend.d.level =
	/* for audigy */
	chan->fxsend.e.level = chan->fxsend.f.level =	
	chan->fxsend.g.level = chan->fxsend.h.level = 
		IS_AUDIGY(&chan->voice->stream->card->config) ? 0xc0 : 0xff;	/* not max */
		
	chan->fxsend.a.dest = 0x0;
	chan->fxsend.b.dest = 0x1;
	chan->fxsend.c.dest = 0x2;
	chan->fxsend.d.dest = 0x3;
	/* for audigy */
	chan->fxsend.e.dest = 0x4;
	chan->fxsend.f.dest = 0x5;
	chan->fxsend.g.dest = 0x6;
	chan->fxsend.h.dest = 0x7;

	chan->pitch.intial = 0x0000;	/* shouldn't it be 0xE000 ? */
	chan->pitch.current = 0x0000;	/* should it be 0x0400 */
	chan->pitch.target = 0x0000;	/* the unity pitch shift ? */
	chan->pitch.envelope_amount = 0x00;	/* none */

	chan->initial_attenuation = 0x00;	/* no attenuation */
	chan->volume.current = 0x0000;	/* no volume */
	chan->volume.target = 0xffff;
	chan->volume.envelope.current_state = 0x8000;	/* 0 msec delay */
	chan->volume.envelope.hold_time = 0x7f;	/* 0 msec */
	chan->volume.envelope.attack_time = 0x7f;	/* 5.5msec */
	chan->volume.envelope.sustain_level = 0x7f;	/* full  */
	chan->volume.envelope.decay_time = 0x7f;	/* 22msec  */

	chan->filter.initial_cutoff_frequency = 0xff;	/* no filter */
	chan->filter.current_cutoff_frequency = 0xffff;	/* no filtering */
	chan->filter.target_cutoff_frequency = 0xffff;	/* no filtering */
	chan->filter.lowpass_resonance_height = 0x0;
	chan->filter.interpolation_ROM = 0x1;	/* full band */
	chan->filter.envelope_amount = 0x7f;	/* none */
	chan->filter.LFO_modulation_depth = 0x00;	/* none */

	chan->loop.start = 0x000000;
	chan->loop.end = 0x000010;	/* Why ? */

	chan->modulation.envelope.current_state = 0x8000;
	chan->modulation.envelope.hold_time = 0x00;	/* 127 better ? */
	chan->modulation.envelope.attack_time = 0x00;	/* infinite */
	chan->modulation.envelope.sustain_level = 0x00;	/* off */
	chan->modulation.envelope.decay_time = 0x7f;	/* 22 msec */
	chan->modulation.LFO_state = 0x8000;

	chan->vibrato_LFO.state = 0x8000;
	chan->vibrato_LFO.modulation_depth = 0x00;	/* none */
	chan->vibrato_LFO.vibrato_depth = 0x00;
	chan->vibrato_LFO.frequency = 0x00;	/* Why set to 24 when
						 * initialized ? */

	chan->tremolo_depth = 0x00;
}

static emuxki_channel *
emuxki_channel_new(emuxki_voice *voice, uint8 num)
{
	emuxki_channel *chan;

	chan = (emuxki_channel *) malloc(sizeof(emuxki_channel));
	if (chan == NULL)
		return NULL;
	chan->voice = voice;
	chan->num = num;
	emuxki_chanparms_set_defaults(chan);
	chan->voice->stream->card->channel[num] = chan;
	return chan;
}

static void
emuxki_channel_delete(emuxki_channel *chan)
{
	chan->voice->stream->card->channel[chan->num] = NULL;
	free(chan);
}

static void
emuxki_channel_set_fxsend(emuxki_channel *chan,
			   emuxki_chanparms_fxsend *fxsend)
{
	/* Could do a memcpy ...*/
	chan->fxsend.a.level = fxsend->a.level;
	chan->fxsend.b.level = fxsend->b.level;
	chan->fxsend.c.level = fxsend->c.level;
	chan->fxsend.d.level = fxsend->d.level;
	chan->fxsend.a.dest = fxsend->a.dest;
	chan->fxsend.b.dest = fxsend->b.dest;
	chan->fxsend.c.dest = fxsend->c.dest;
	chan->fxsend.d.dest = fxsend->d.dest;
	
	/* for audigy */
	chan->fxsend.e.level = fxsend->e.level;
	chan->fxsend.f.level = fxsend->f.level;
	chan->fxsend.g.level = fxsend->g.level;
	chan->fxsend.h.level = fxsend->h.level;
	chan->fxsend.e.dest = fxsend->e.dest;
	chan->fxsend.f.dest = fxsend->f.dest;
	chan->fxsend.g.dest = fxsend->g.dest;
	chan->fxsend.h.dest = fxsend->h.dest;
}

static void
emuxki_channel_set_srate(emuxki_channel *chan, uint32 srate)
{
	chan->pitch.target = (srate << 8) / 375;
	chan->pitch.target = (chan->pitch.target >> 1) +
		(chan->pitch.target & 1);
	chan->pitch.target &= 0xffff;
	chan->pitch.current = chan->pitch.target;
	chan->pitch.intial =
		(emuxki_rate_to_pitch(srate) >> 8) & EMU_CHAN_IP_MASK;
}

/* voice params must be set before calling this */
static void
emuxki_channel_set_bufparms(emuxki_channel *chan,
			     uint32 start, uint32 end)
{
	chan->loop.start = start & EMU_CHAN_PSST_LOOPSTARTADDR_MASK;
	chan->loop.end = end & EMU_CHAN_DSL_LOOPENDADDR_MASK;
}

static void
emuxki_channel_commit_fx(emuxki_channel *chan)
{
	emuxki_dev   	*card = chan->voice->stream->card;
	uint8			chano = chan->num;
	
	if(IS_AUDIGY(&card->config)) {
		emuxki_chan_write(&card->config, chano, EMU_A_CHAN_FXRT1,
			      (chan->fxsend.d.dest << 24) |
			      (chan->fxsend.c.dest << 16) |
			      (chan->fxsend.b.dest << 8) |
			      (chan->fxsend.a.dest));
		emuxki_chan_write(&card->config, chano, EMU_A_CHAN_FXRT2,
			      (chan->fxsend.h.dest << 24) |
			      (chan->fxsend.g.dest << 16) |
			      (chan->fxsend.f.dest << 8) |
			      (chan->fxsend.e.dest));
		emuxki_chan_write(&card->config, chano, EMU_A_CHAN_SENDAMOUNTS,
			      (chan->fxsend.e.level << 24) |
			      (chan->fxsend.f.level << 16) |
			      (chan->fxsend.g.level << 8) |
			      (chan->fxsend.h.level));
	} else {
		emuxki_chan_write(&card->config, chano, EMU_CHAN_FXRT,
			      (chan->fxsend.d.dest << 28) |
			      (chan->fxsend.c.dest << 24) |
			      (chan->fxsend.b.dest << 20) |
			      (chan->fxsend.a.dest << 16));
	}
	
	emuxki_chan_write(&card->config, chano, 0x10000000 | EMU_CHAN_PTRX,
		      (chan->fxsend.a.level << 8) | chan->fxsend.b.level);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_DSL,
		      (chan->fxsend.d.level << 24) | chan->loop.end);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_PSST,
		      (chan->fxsend.c.level << 24) | chan->loop.start);
}

static void
emuxki_channel_commit_parms(emuxki_channel *chan)
{
	emuxki_voice *voice = chan->voice;
	emuxki_dev   	*card = chan->voice->stream->card;
	uint32			start, mapval;
	uint8			chano = chan->num;
	//int             s;

	start = chan->loop.start +
		(voice->stereo ? 28 : 30) * (voice->b16 + 1);
	mapval = ((uint32)card->silentpage_phy_base) << 1 | EMU_CHAN_MAP_PTI_MASK;

	//s = splaudio();
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_STEREO, voice->stereo);
	
	emuxki_channel_commit_fx(chan);
		      
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CCCA,
		      (chan->filter.lowpass_resonance_height << 28) |
		      (chan->filter.interpolation_ROM << 25) |
		   (voice->b16 ? 0 : EMU_CHAN_CCCA_8BITSELECT) | start);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_Z1, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_Z2, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_MAPA, mapval);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_MAPB, mapval);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRFILTER,
		      chan->filter.current_cutoff_frequency);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_FILTERTARGET,
		      chan->filter.target_cutoff_frequency);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_ATKHLDM,
		      (chan->modulation.envelope.hold_time << 8) |
		      chan->modulation.envelope.attack_time);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_DCYSUSM,
		      (chan->modulation.envelope.sustain_level << 8) |
		      chan->modulation.envelope.decay_time);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_LFOVAL1,
		      chan->modulation.LFO_state);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_LFOVAL2,
		      chan->vibrato_LFO.state);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_FMMOD,
		      (chan->vibrato_LFO.modulation_depth << 8) |
		      chan->filter.LFO_modulation_depth);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_TREMFRQ,
		      (chan->tremolo_depth << 8));
	emuxki_chan_write(&card->config, chano, EMU_CHAN_FM2FRQ2,
		      (chan->vibrato_LFO.vibrato_depth << 8) |
		      chan->vibrato_LFO.frequency);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_ENVVAL,
		      chan->modulation.envelope.current_state);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_ATKHLDV,
		      (chan->volume.envelope.hold_time << 8) |
		      chan->volume.envelope.attack_time);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_ENVVOL,
		      chan->volume.envelope.current_state);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_PEFE,
		      (chan->pitch.envelope_amount << 8) |
		      chan->filter.envelope_amount);
	//splx(s);
}

static void
emuxki_channel_start(emuxki_channel *chan)
{
	emuxki_voice *voice = chan->voice;
	emuxki_dev   *card = chan->voice->stream->card;
	uint8        cache_sample, cache_invalid_size, chano = chan->num;
	uint32       sample;
	//int             s;

	cache_sample = voice->stereo ? 4 : 2;
	sample = voice->b16 ? 0x00000000 : 0x80808080;
	cache_invalid_size = (voice->stereo ? 28 : 30) * (voice->b16 + 1);

	//s = splaudio();
	while (cache_sample--)
		emuxki_chan_write(&card->config, chano,
			      EMU_CHAN_CD0 + cache_sample, sample);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_READADDRESS, 64);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CCR_CACHEINVALIDSIZE,
		      cache_invalid_size);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_IFATN,
		      (chan->filter.target_cutoff_frequency << 8) |
		      chan->initial_attenuation);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_VOLUMETARGET,
		      chan->volume.target);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRVOL,
		      chan->volume.current);
	emuxki_chan_write(&card->config, 0, EMU_MKSUBREG(1, chano,
					       EMU_SOLEL + (chano >> 5)),
		      0);	/* Clear stop on loop */
	emuxki_chan_write(&card->config, 0, EMU_MKSUBREG(1, chano,
					       EMU_CLIEL + (chano >> 5)),
		      0);	/* Clear loop interrupt */
	emuxki_chan_write(&card->config, chano, EMU_CHAN_DCYSUSV,
		      (chan->volume.envelope.sustain_level << 8) |
		      chan->volume.envelope.decay_time);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_PTRX_PITCHTARGET,
		      chan->pitch.target);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_PITCH,
		      chan->pitch.current);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_IP, chan->pitch.intial);

	//splx(s);
}

static void
emuxki_channel_stop(emuxki_channel *chan)
{
	emuxki_dev   	*card = chan->voice->stream->card;
	//int             s;
	uint8           chano = chan->num;

	//s = splaudio();

	emuxki_chan_write(&card->config, chano, EMU_CHAN_PTRX_PITCHTARGET, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CPF_PITCH, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_IFATN_ATTENUATION, 0xff);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_VTFT_VOLUMETARGET, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_CVCF_CURRVOL, 0);
	emuxki_chan_write(&card->config, chano, EMU_CHAN_IP, 0);

	//splx(s);
}

/*	Emuxki voice functions */
static void 
emuxki_dump_voice(emuxki_voice *voice)
{
	LOG(("voice->use = %#u\n", voice->use));
	LOG(("voice->state = %#u\n", voice->state));
	LOG(("voice->stereo = %#u\n", voice->stereo));
	LOG(("voice->b16 = %#u\n", voice->b16));
	LOG(("voice->sample_rate = %#lu\n", voice->sample_rate));
	LOG(("voice->buffer = %#08x\n", voice->buffer));
	if(voice->buffer) {
		LOG(("voice->buffer->ptbidx = %#u\n", voice->buffer->ptbidx));
		LOG(("voice->buffer->log_base = %#08x\n", voice->buffer->log_base));
		LOG(("voice->buffer->phy_base = %#08x\n", voice->buffer->phy_base));
		LOG(("voice->buffer->size = %#08x\n", voice->buffer->size));
		LOG(("voice->buffer->area = %#08x\n", voice->buffer->area));
	}
	LOG(("voice->blksize = %#u\n", voice->blksize));
	LOG(("voice->trigblk = %#u\n", voice->trigblk));
	LOG(("voice->blkmod = %#u\n", voice->blkmod));
	LOG(("voice->timerate = %#u\n", voice->timerate));
}

/* Allocate channels for voice in case of play voice */
static status_t
emuxki_voice_channel_create(emuxki_voice *voice)
{
	emuxki_channel **channel = voice->stream->card->channel;
	uint8	        i, stereo = voice->stereo;
	//int             s;

	for (i = 0; i < EMU_NUMCHAN; i += stereo + 1) {
		if ((stereo && (channel[i + 1] != NULL)) ||
		    (channel[i] != NULL))	/* Looking for free channels */
			continue;
		//s = splaudio();
		if (stereo) {
			voice->dataloc.chan[1] =
				emuxki_channel_new(voice, i + 1);
			if (voice->dataloc.chan[1] == NULL) {
				//splx(s);
				return ENOMEM;
			}
		}
		voice->dataloc.chan[0] = emuxki_channel_new(voice, i);
		if (voice->dataloc.chan[0] == NULL) {
			if (stereo) {
				emuxki_channel_delete(voice->dataloc.chan[1]);
				voice->dataloc.chan[1] = NULL;
			}
			//splx(s);
			return ENOMEM;
		}
		//splx(s);
		return B_OK;
	}
	return EAGAIN;
}

/* When calling this function we assume no one can access the voice */
static void
emuxki_voice_channel_destroy(emuxki_voice *voice)
{
	emuxki_channel_delete(voice->dataloc.chan[0]);
	voice->dataloc.chan[0] = NULL;
	if (voice->stereo)
		emuxki_channel_delete(voice->dataloc.chan[1]);
	voice->dataloc.chan[1] = NULL;
}

static status_t
emuxki_voice_dataloc_create(emuxki_voice *voice)
{
	status_t	error;

	if (voice->use & EMU_USE_PLAY) {
		if ((error = emuxki_voice_channel_create(voice)))
			return (error);
	} else {
		
	}
	return B_OK;
}

static void
emuxki_voice_dataloc_destroy(emuxki_voice *voice)
{
	if (voice->use & EMU_USE_PLAY) {
		if (voice->dataloc.chan[0] != NULL)
			emuxki_voice_channel_destroy(voice);
	} else {
		uint32 buffaddr_reg, buffsize_reg;
		switch (voice->dataloc.source) {
			case EMU_RECSRC_MIC:
				buffaddr_reg = EMU_MICBA;
				buffsize_reg = EMU_MICBS;
				break;
			case EMU_RECSRC_ADC:
				buffaddr_reg = EMU_ADCBA;
				buffsize_reg = EMU_ADCBS;
				break;
			case EMU_RECSRC_FX:
				buffaddr_reg = EMU_FXBA;
				buffsize_reg = EMU_FXBS;
				break;
			default:
				return;
		}
		emuxki_chan_write(&voice->stream->card->config, 0, buffaddr_reg, 0);
		emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_NONE);
	}
}

static void
emuxki_voice_fxupdate(emuxki_voice *voice)
{
	emuxki_chanparms_fxsend fxsend;
	
	uint8 maxlevel = 
		IS_AUDIGY(&voice->stream->card->config) ? 0xc0 : 0xff;	/* not max */

	if (voice->use & EMU_USE_PLAY) {
		fxsend.a.dest = 0x3f;
		fxsend.b.dest = 0x3f;
		fxsend.c.dest = 0x3f;
		fxsend.d.dest = 0x3f;
		/* for audigy */
		fxsend.e.dest = 0x3f;
		fxsend.f.dest = 0x3f;
		fxsend.g.dest = 0x3f;
		fxsend.h.dest = 0x3f;
		
		fxsend.a.level = fxsend.b.level = fxsend.c.level = fxsend.d.level = 
		fxsend.e.level = fxsend.g.level = fxsend.f.level = fxsend.h.level = 0x00;
						
		if (voice->stereo) {
			switch(voice->stream->card->play_mode) {
				case 2:
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
					} else if((voice->stream->nstereo == 2) || 
						((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
						if(voice->voicenum > 1 - 1)
							fxsend.a.dest-=2;
					} else if(voice->stream->nstereo == 3 && voice->voicenum > 1) {
							fxsend.a.dest = 0x0;
							fxsend.a.level = maxlevel / 2;
							fxsend.b.dest = 0x1;
							fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 2 badly managed\n"));
					}
					break;
				case 4:
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum * 2 + 2;
						fxsend.b.level = maxlevel;
					} else if((voice->stream->nstereo == 2) || 
						((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
					} else if(voice->stream->nstereo == 3 && voice->voicenum > 1) {
						fxsend.a.dest = 0x0;
						fxsend.a.level = maxlevel / 2;
						fxsend.b.dest = 0x1;
						fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 4 badly managed\n"));
					}
					break;
				case 6: // only on audigy
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum * 2 + 2;
						fxsend.b.level = maxlevel;
						fxsend.c.dest = 0x4;
						fxsend.c.level = maxlevel / 2;
						fxsend.d.dest = 0x5;
						fxsend.d.level = maxlevel / 2;
					} else if(voice->stream->nstereo == 2) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
						if(voice->voicenum < 1) {
							fxsend.b.dest = 0x4;
							fxsend.b.level = maxlevel / 2;
							fxsend.c.dest = 0x5;
							fxsend.c.level = maxlevel / 2;
						}
					} else if(voice->stream->nstereo == 3) {
						fxsend.a.dest = voice->voicenum * 2;
						fxsend.a.level = maxlevel;
					} else {
						LOG(("emuxki_voice_set_stereo case 6 badly managed\n"));
					}
					break;
			}
		
			emuxki_channel_set_fxsend(voice->dataloc.chan[0],
						   &fxsend);
			
			switch(voice->stream->card->play_mode) {
				case 2:
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
					} else if((voice->stream->nstereo == 2) || 
						((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
						if(voice->voicenum > 1 - 1)
							fxsend.a.dest-=2;
					} else if(voice->stream->nstereo == 3 && voice->voicenum > 1) {
							fxsend.a.dest = 0x0;
							fxsend.a.level = maxlevel / 2;
							fxsend.b.dest = 0x1;
							fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 2 badly managed\n"));
					}
					break;
				case 4:
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum * 2 + 3;
						fxsend.b.level = maxlevel;
					} else if((voice->stream->nstereo == 2) || 
						((voice->stream->nstereo == 3)&&(voice->voicenum < 2))) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
					} else if(voice->stream->nstereo == 3 && voice->voicenum > 1) {
						fxsend.a.dest = 0x0;
						fxsend.a.level = maxlevel / 2;
						fxsend.b.dest = 0x1;
						fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 4 badly managed\n"));
					}
					break;
				case 6: // only on audigy
					if(voice->stream->nstereo == 1) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum * 2 + 3;
						fxsend.b.level = maxlevel;
						fxsend.c.dest = 0x4;
						fxsend.c.level = maxlevel / 2;
						fxsend.d.dest = 0x5;
						fxsend.d.level = maxlevel / 2;
					} else if(voice->stream->nstereo == 2) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
						if(voice->voicenum < 1) {
							fxsend.b.dest = 0x4;
							fxsend.b.level = maxlevel / 2;
							fxsend.c.dest = 0x5;
							fxsend.c.level = maxlevel / 2;
						}
					} else if(voice->stream->nstereo == 3) {
						fxsend.a.dest = voice->voicenum * 2 + 1;
						fxsend.a.level = maxlevel;
					} else {
						LOG(("emuxki_voice_set_stereo case 6 badly managed\n"));
					}
					break;
			}
						   
			emuxki_channel_set_fxsend(voice->dataloc.chan[1],
						   &fxsend);
		} else {
			switch(voice->stream->card->play_mode) {
				case 2:
					if(voice->stream->nmono == 1) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum + 1;
						fxsend.b.level = maxlevel;
					} else if(voice->stream->nmono == 2) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
					} else if((voice->stream->nmono == 4) || 
						((voice->stream->nmono == 6)&&(voice->voicenum < 4))) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						if(voice->voicenum > 2 - 1)
							fxsend.a.dest-=2;
					} else if(voice->stream->nmono == 6 && voice->voicenum > 3) {
							fxsend.a.dest = 0x0;
							fxsend.a.level = maxlevel / 2;
							fxsend.b.dest = 0x1;
							fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 2 badly managed\n"));
					}
					break;
				case 4:
					if(voice->stream->nmono == 1) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum + 1;
						fxsend.b.level = maxlevel;
						fxsend.c.dest = voice->voicenum + 2;
						fxsend.c.level = maxlevel;
						fxsend.d.dest = voice->voicenum + 3;
						fxsend.d.level = maxlevel;
					} else if(voice->stream->nmono == 2) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum + 2;
						fxsend.b.level = maxlevel;
					} else if((voice->stream->nmono == 4) || 
						((voice->stream->nmono == 6)&&(voice->voicenum < 4))) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
					} else if(voice->stream->nmono == 6 && voice->voicenum > 3) {
							fxsend.a.dest = 0x0;
							fxsend.a.level = maxlevel / 2;
							fxsend.b.dest = 0x1;
							fxsend.b.level = maxlevel / 2;
					} else {
						LOG(("emuxki_voice_set_stereo case 4 badly managed\n"));
					}
					break;
				case 6: // only on audigy
					if(voice->stream->nmono == 1) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum + 1;
						fxsend.b.level = maxlevel;
						fxsend.c.dest = voice->voicenum + 2;
						fxsend.c.level = maxlevel;
						fxsend.d.dest = voice->voicenum + 3;
						fxsend.d.level = maxlevel;
						fxsend.e.dest = voice->voicenum + 4;
						fxsend.e.level = maxlevel;
						fxsend.f.dest = voice->voicenum + 5;
						fxsend.f.level = maxlevel;
					} else if(voice->stream->nmono == 2) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						fxsend.b.dest = voice->voicenum + 2;
						fxsend.b.level = maxlevel;
						fxsend.c.dest = 0x4;
						fxsend.c.level = maxlevel / 2;
						fxsend.d.dest = 0x5;
						fxsend.d.level = maxlevel / 2;
					} else if(voice->stream->nmono == 4) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
						if(voice->voicenum < 2) {
							fxsend.b.dest = 0x4;
							fxsend.b.level = maxlevel / 2;
							fxsend.c.dest = 0x5;
							fxsend.c.level = maxlevel / 2;
						}
					} else if(voice->stream->nmono == 6) {
						fxsend.a.dest = voice->voicenum;
						fxsend.a.level = maxlevel;
					} else {
						LOG(("emuxki_voice_set_stereo case 6 badly managed\n"));
					}
					break;
			}
			
			emuxki_channel_set_fxsend(voice->dataloc.chan[0],
						   &fxsend);
		}
	}
}

static status_t
emuxki_voice_set_stereo(emuxki_voice *voice, uint8 stereo)
{
	status_t	error;
	
	emuxki_voice_dataloc_destroy(voice);
	voice->stereo = stereo;
	if ((error = emuxki_voice_dataloc_create(voice)))
	  return error;
	emuxki_voice_fxupdate(voice);
	return B_OK;
}

static status_t
emuxki_voice_set_srate(emuxki_voice *voice, uint32 srate)
{
	voice->sample_rate = srate;
	if (voice->use & EMU_USE_PLAY) {
		if ((srate < 4000) || (srate > 48000))
			return (EINVAL);
		emuxki_channel_set_srate(voice->dataloc.chan[0], srate);
		if (voice->stereo)
			emuxki_channel_set_srate(voice->dataloc.chan[1],
						  srate);
	}
	return B_OK;
}

status_t
emuxki_voice_set_audioparms(emuxki_voice *voice, uint8 stereo,
			     uint8 b16, uint32 srate)
{
	status_t             error;

	if (voice->stereo == stereo && voice->b16 == b16 &&
	    voice->sample_rate == srate)
		return B_OK;
	
	if (voice->stereo != stereo) {
		if ((error = emuxki_voice_set_stereo(voice, stereo)))
			return error;
	}
	voice->b16 = b16;
	if (voice->sample_rate != srate)
		emuxki_voice_set_srate(voice, srate);
	return B_OK;
}

status_t
emuxki_voice_set_recparms(emuxki_voice *voice, emuxki_recsrc_t recsrc,
			     	emuxki_recparams *recparams)
{
	if(voice->use & EMU_USE_RECORD) {
		switch(recsrc) {
			case EMU_RECSRC_MIC:
				break;
			case EMU_RECSRC_ADC:
				break;
			case EMU_RECSRC_FX:
				if(!recparams)
					return B_ERROR;
				voice->recparams.efx_voices[0] = recparams->efx_voices[0];
				voice->recparams.efx_voices[1] = recparams->efx_voices[1];
				break;
			default:
				return B_ERROR;
				break;
		}
		voice->dataloc.source = recsrc;
	}
	return B_OK;
}

/* voice audio parms (see just before) must be set prior to this */
status_t
emuxki_voice_set_bufparms(emuxki_voice *voice, void *ptr,
			   uint32 bufsize, uint16 blksize)
{
	emuxki_mem *mem;
	emuxki_channel **chan;
	uint32 start, end;
	uint8 sample_size;
	status_t error = EFAULT;

	LIST_FOREACH(mem, &voice->stream->card->mem, next) {
		if (mem->log_base != ptr)
			continue;

		voice->buffer = mem;
		sample_size = (voice->b16 + 1) * (voice->stereo + 1);
		voice->trigblk = 0;	/* This shouldn't be needed */
		voice->blkmod = bufsize / blksize;
		if (bufsize % blksize) 	  /* This should not happen */
			voice->blkmod++;
		error = 0;

		if (voice->use & EMU_USE_PLAY) {
			voice->blksize = blksize / sample_size;
			chan = voice->dataloc.chan;
			start = (mem->ptbidx << 12) / sample_size;
			end = start + bufsize / sample_size;
			emuxki_channel_set_bufparms(chan[0],
						     start, end);
			if (voice->stereo)
				emuxki_channel_set_bufparms(chan[1],
				     start, end);
			voice->timerate = (uint32) 48000 *
			                voice->blksize / voice->sample_rate / 2;
			if (voice->timerate < 5)
				error = EINVAL;
		} else {
			voice->blksize = blksize;
		}

		break;
	}

	return error;
}

status_t
emuxki_voice_commit_parms(emuxki_voice *voice)
{
	if (voice->use & EMU_USE_PLAY) {
		emuxki_channel_commit_parms(voice->dataloc.chan[0]);
		if (voice->stereo)
			emuxki_channel_commit_parms(voice->dataloc.chan[1]);
	} else {
		uint32 buffaddr_reg, buffsize_reg, idx_reg;
		switch (voice->dataloc.source) {
			case EMU_RECSRC_MIC:
				buffaddr_reg = EMU_MICBA;
				buffsize_reg = EMU_MICBS;
				idx_reg = EMU_MICIDX;
				break;
			case EMU_RECSRC_ADC:
				buffaddr_reg = EMU_ADCBA;
				buffsize_reg = EMU_ADCBS;
				idx_reg = EMU_ADCIDX;
				break;
			case EMU_RECSRC_FX:
				buffaddr_reg = EMU_FXBA;
				buffsize_reg = EMU_FXBS;
				idx_reg = EMU_FXIDX;
				break;
			default:
				return B_ERROR;
		}
		emuxki_chan_write(&voice->stream->card->config, 0, buffaddr_reg, (uint32)voice->buffer->phy_base);
		emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_NONE);
		emuxki_chan_write(&voice->stream->card->config, 0, buffsize_reg, EMU_RECBS_BUFSIZE_4096);
		
		LOG(("emuxki_voice_commit_parms idx_reg : %u\n", idx_reg));
		
		idx_reg = EMU_RECIDX(idx_reg);
		while(emuxki_chan_read(&voice->stream->card->config, 0, idx_reg))
			snooze(5);
	}
	return B_OK;
}

static uint32
emuxki_voice_curaddr(emuxki_voice *voice)
{
	if (voice->use & EMU_USE_PLAY)
		return (emuxki_chan_read(&voice->stream->card->config,
				     voice->dataloc.chan[0]->num,
				     EMU_CHAN_CCCA_CURRADDR) -
			voice->dataloc.chan[0]->loop.start);
	else {
		uint32 idx_reg = 0;
		switch (voice->dataloc.source) {
			case EMU_RECSRC_MIC:
				idx_reg = IS_AUDIGY(&voice->stream->card->config) ? EMU_A_MICIDX : EMU_MICIDX;
				break;
			case EMU_RECSRC_ADC:
				idx_reg = IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCIDX : EMU_ADCIDX;
				break;
			case EMU_RECSRC_FX:
				idx_reg = EMU_FXIDX;
				break;
			default:
				TRACE(("emuxki_voice_curaddr : BUG!!\n"));
		}
		//TRACE(("emuxki_voice_curaddr idx_reg : %u\n", idx_reg));
		//TRACE(("emuxki_voice_curaddr : %lu\n", emuxki_chan_read(&voice->stream->card->config, 0, idx_reg)));
		return emuxki_chan_read(&voice->stream->card->config,
				     0,
				     idx_reg);
	}
}

static void
emuxki_resched_timer(emuxki_dev *card)
{
	emuxki_voice 	*voice;
	emuxki_stream 	*stream;
	uint16			timerate = 1024;
	uint8			active = 0;
	//int             s = splaudio();

	LOG(("emuxki_resched_timer begin\n"));

	LIST_FOREACH(stream, &card->streams, next) {
		LIST_FOREACH(voice, &stream->voices, next) {
			if ((voice->use & EMU_USE_PLAY) == 0 || 
				(voice->state & EMU_STATE_STARTED) == 0)
				continue;
			active = 1;
			if (voice->timerate < timerate)
				timerate = voice->timerate;
		}
	}
	if (timerate & ~EMU_TIMER_RATE_MASK)
		timerate = 0;
	
	if(card->timerate > timerate) {
		LOG(("emuxki_resched_timer written (old %u, new %u)\n", card->timerate, timerate));
		card->timerate = timerate;
		emuxki_reg_write_16(&card->config, EMU_TIMER, timerate);
	}
	if (!active && (card->timerstate & EMU_TIMER_STATE_ENABLED)) {
		emuxki_inte_disable(&card->config, EMU_INTE_INTERTIMERENB);
		card->timerstate &= ~EMU_TIMER_STATE_ENABLED;
		LOG(("emuxki_resched_timer : timer disabled\n"));
	} else
	  if (active && !(card->timerstate & EMU_TIMER_STATE_ENABLED)) {
	  	emuxki_inte_enable(&card->config, EMU_INTE_INTERTIMERENB);
		card->timerstate |= EMU_TIMER_STATE_ENABLED;
		LOG(("emuxki_resched_timer : timer enabled\n"));
	  }
	LOG(("emuxki_resched_timer state : %x\n", card->timerstate));
	//splx(s);
}

static uint32 
emuxki_voice_adc_rate(emuxki_voice *voice)
{
	switch(voice->sample_rate) {
		case 48000:
			return EMU_ADCCR_SAMPLERATE_48;
			break;
		case 44100:
			return EMU_ADCCR_SAMPLERATE_44;
			break;
		case 32000:
			return EMU_ADCCR_SAMPLERATE_32;
			break;
		case 24000:
			return EMU_ADCCR_SAMPLERATE_24;
			break;
		case 22050:
			return EMU_ADCCR_SAMPLERATE_22;
			break;
		case 16000:
			return EMU_ADCCR_SAMPLERATE_16;
			break;
		case 12000:
			if(IS_AUDIGY(&voice->stream->card->config))
				return EMU_A_ADCCR_SAMPLERATE_12;
			else
				PRINT(("recording sample_rate not supported : %u\n", voice->sample_rate));
			break;
		case 11000:
			if(IS_AUDIGY(&voice->stream->card->config))
				return EMU_A_ADCCR_SAMPLERATE_11;
			else
				return EMU_ADCCR_SAMPLERATE_11;
			break;
		case 8000:
			if(IS_AUDIGY(&voice->stream->card->config))
				return EMU_A_ADCCR_SAMPLERATE_8;
			else
				return EMU_ADCCR_SAMPLERATE_8;
			break;
		default:
			PRINT(("recording sample_rate not supported : %u\n", voice->sample_rate));
	}
	return 0;
}

void
emuxki_voice_start(emuxki_voice *voice)
{
	LOG(("emuxki_voice_start\n"));
	
	if (voice->use & EMU_USE_PLAY) {
		voice->trigblk = 1;
		emuxki_channel_start(voice->dataloc.chan[0]);
		if (voice->stereo)
			emuxki_channel_start(voice->dataloc.chan[1]);
	} else {
						
		switch (voice->dataloc.source) {
			case EMU_RECSRC_MIC:
				emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_MICBUFENABLE);
				break;
			case EMU_RECSRC_ADC: {
				uint32 adccr_value = 0;
				adccr_value = emuxki_voice_adc_rate(voice);
				LOG(("emuxki_voice_start adccr_value : %u\n", adccr_value));
				if(voice->stereo)
					adccr_value |= ( (IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_LCHANENABLE : EMU_ADCCR_LCHANENABLE )
						| ( IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_RCHANENABLE : EMU_ADCCR_RCHANENABLE ));
				else
					adccr_value |= IS_AUDIGY(&voice->stream->card->config) ? EMU_A_ADCCR_LCHANENABLE : EMU_ADCCR_LCHANENABLE;
				
				LOG(("emuxki_voice_start adccr_value : %u, %u\n", adccr_value, EMU_ADCCR_LCHANENABLE | EMU_ADCCR_RCHANENABLE));
				emuxki_chan_write(&voice->stream->card->config, 0, EMU_ADCCR, adccr_value);
				
				emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_ADCBUFENABLE);
				}
				break;
			case EMU_RECSRC_FX:
				if(IS_AUDIGY(&voice->stream->card->config)) {
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC1, 
						voice->recparams.efx_voices[0]);
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC2, 
						voice->recparams.efx_voices[1]);				
				} else {
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_FXWC, 
						voice->recparams.efx_voices[0]);
				}
				emuxki_inte_enable(&voice->stream->card->config, EMU_INTE_EFXBUFENABLE);
				break;
			default:
				PRINT(("emuxki_voice_start BUG\n"));
		}
	}
	voice->state |= EMU_STATE_STARTED;
	if (voice->use & EMU_USE_PLAY) {
		emuxki_resched_timer(voice->stream->card);
	}
}

void
emuxki_voice_halt(emuxki_voice *voice)
{
	LOG(("emuxki_voice_halt\n"));
	
	if (voice->use & EMU_USE_PLAY) {
		emuxki_channel_stop(voice->dataloc.chan[0]);
		if (voice->stereo)
			emuxki_channel_stop(voice->dataloc.chan[1]);
	} else {
			
		switch (voice->dataloc.source) {
			case EMU_RECSRC_MIC:
				emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_MICBUFENABLE);
				break;
			case EMU_RECSRC_ADC: 
				emuxki_chan_write(&voice->stream->card->config, 0, EMU_ADCCR, 0);
				emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_ADCBUFENABLE);
				break;
			case EMU_RECSRC_FX:
				if(IS_AUDIGY(&voice->stream->card->config)) {
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC1, 0);
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_A_FXWC2, 0);
				} else
					emuxki_chan_write(&voice->stream->card->config, 0, EMU_FXWC, 0);
				emuxki_inte_disable(&voice->stream->card->config, EMU_INTE_EFXBUFENABLE);
				break;
			default:
				PRINT(("emuxki_voice_halt BUG\n"));
		}
	}
	voice->state &= ~EMU_STATE_STARTED;
	if (voice->use & EMU_USE_PLAY) {
		emuxki_resched_timer(voice->stream->card);
	}
}

emuxki_voice *
emuxki_voice_new(emuxki_stream *stream, uint8 use, uint8 voicenum)
{
	emuxki_voice *voice;
	//int             s;
	
	LOG(("emuxki_voice_new\n"));

	voice = malloc(sizeof(emuxki_voice));
	if (voice == NULL)
		return (NULL);
	voice->stream = stream;
	voice->use = use;
	voice->state = !EMU_STATE_STARTED;
	voice->stereo = EMU_STEREO_NOTSET;
	voice->b16 = 0;
	voice->sample_rate = 0;
	if (use & EMU_USE_PLAY)
		voice->dataloc.chan[0] = voice->dataloc.chan[1] = NULL;
	else
		voice->dataloc.source = EMU_RECSRC_NOTSET;
	voice->buffer = NULL;
	voice->blksize = 0;
	voice->trigblk = 0;
	voice->blkmod = 0;
	voice->timerate = 0;
	voice->voicenum = voicenum;
	return voice;
}

void
emuxki_voice_delete(emuxki_voice *voice)
{
	if (voice->state & EMU_STATE_STARTED)
		emuxki_voice_halt(voice);
	emuxki_voice_dataloc_destroy(voice);
	free(voice);
}

/*	Emuxki stream functions */

status_t
emuxki_stream_set_audioparms(emuxki_stream *stream, bool stereo, uint8 channels,
     uint8 b16, uint32 sample_rate)
{
	status_t 		error;
	emuxki_voice 	*voice;
	uint8			i, nvoices;
	char 			*buffer;
	uint8 			sample_size, frame_size;
	LOG(("emuxki_stream_set_audioparms\n"));

	if (stream->stereo == stereo && 
		((stream->nmono + 2*stream->nstereo) == channels) &&
		(stream->b16 == b16) && 
		(stream->sample_rate == sample_rate))
		return B_OK;
	
	LIST_FOREACH(voice, &stream->voices, next) {
		if(voice->buffer)
			emuxki_mem_free(stream->card, voice->buffer->log_base);
		emuxki_voice_delete(voice);
	}
	stream->first_voice = NULL;
	LIST_INIT(&(stream->voices));
	
	stream->b16 = b16;
	stream->sample_rate = sample_rate;
	
	if(stereo && (channels % 2 == 0)) {
		stream->stereo = true;
		stream->nstereo = channels / 2;
		stream->nmono = 0;
		nvoices = stream->nstereo;
	} else {
		stream->stereo = false;
		stream->nstereo = 0;
		stream->nmono = channels;
		nvoices = stream->nmono;
	}
	
	sample_size = stream->b16 + 1;
	frame_size = sample_size * (stream->stereo ? 2 : 1);
	
	for(i=0; i<nvoices; i++) {
		voice = emuxki_voice_new(stream, stream->use, i);
		if(voice) {
			if(!stream->first_voice)
				stream->first_voice = voice;
			LIST_INSERT_HEAD((&stream->voices), voice, next);
			if ((error = emuxki_voice_set_audioparms(voice, stream->stereo, stream->b16, stream->sample_rate)))
				return error;
						
			if (stream->use & EMU_USE_PLAY)
				buffer = emuxki_pmem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
			else
				buffer = emuxki_rmem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
			
			emuxki_voice_set_bufparms(voice, buffer, 
				stream->bufframes * frame_size * stream->bufcount, 
				stream->bufframes * frame_size);
		}
	}
		
	return B_OK;
}

status_t 
emuxki_stream_set_recparms(emuxki_stream *stream, emuxki_recsrc_t recsrc,
			     	emuxki_recparams *recparams)
{
	emuxki_voice *voice;
	LOG(("emuxki_stream_set_recparms\n"));
	
	if(stream->use & EMU_USE_RECORD) {
		switch(recsrc) {
			case EMU_RECSRC_MIC:
				break;
			case EMU_RECSRC_ADC:
				break;
			case EMU_RECSRC_FX:
				if(!recparams)
					return B_ERROR;
				LIST_FOREACH(voice, &stream->voices, next) {
					voice->recparams.efx_voices[0] = recparams->efx_voices[0];
					voice->recparams.efx_voices[1] = recparams->efx_voices[1];
				}
				break;
			default:
				return B_ERROR;
				break;
		}
		LIST_FOREACH(voice, &stream->voices, next)
			voice->dataloc.source = recsrc;
	}
	return B_OK;
}

status_t
emuxki_stream_commit_parms(emuxki_stream *stream)
{
	emuxki_voice 	*voice;
	status_t 		error;
	LOG(("emuxki_stream_commit_parms\n"));
	
	LIST_FOREACH(voice, &stream->voices, next)
		if ((error = emuxki_voice_commit_parms(voice)))
			return error;
	
	return B_OK;
}

status_t
emuxki_stream_get_nth_buffer(emuxki_stream *stream, uint8 chan, uint8 buf, 
					char** buffer, size_t *stride)
{
	emuxki_voice *voice = NULL;
	uint8 i, sample_size;
	LOG(("emuxki_stream_get_nth_buffer\n"));
	
	sample_size = stream->b16 + 1;
	if(buf >= stream->bufcount)
		return B_BAD_INDEX;
	
	if(stream->stereo) {
		i = stream->nstereo - 1;
		if(chan/2 > i)
			return B_BAD_INDEX;
		LIST_FOREACH(voice, &stream->voices, next)
			if(i != chan/2)
				i--;
			else 
				break;
		if(voice) {
			*buffer = (char*)voice->buffer->log_base + (buf * stream->bufframes * sample_size * 2);
			if(chan % 2 == 1)
				*buffer += sample_size;
			*stride = sample_size * 2;
		} else
			return B_ERROR;
	} else {
		i = stream->nmono - 1;
		if(chan > i)
			return B_BAD_INDEX;
		LIST_FOREACH(voice, &stream->voices, next)
			if(i != chan)
				i--;
			else 
				break;
		if(voice) {
			*buffer = (char*)voice->buffer->log_base + (buf * stream->bufframes * sample_size);
			*stride = sample_size;
		} else
			return B_ERROR;
	}
	
	return B_OK;
}

void
emuxki_stream_start(emuxki_stream *stream, void (*inth) (void *), void *inthparam)
{
	emuxki_voice *voice;
	LOG(("emuxki_stream_start\n"));
	
	stream->inth = inth;
	stream->inthparam = inthparam;
		
	LIST_FOREACH(voice, &stream->voices, next) {
		emuxki_voice_start(voice);
	}
	stream->state |= EMU_STATE_STARTED;
}

void
emuxki_stream_halt(emuxki_stream *stream)
{
	emuxki_voice *voice;
	LOG(("emuxki_stream_halt\n"));
			
	LIST_FOREACH(voice, &stream->voices, next) {
		emuxki_voice_halt(voice);
	}
	stream->state &= ~EMU_STATE_STARTED;
}

emuxki_stream *
emuxki_stream_new(emuxki_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
{
	emuxki_stream *stream;
	cpu_status status;
	LOG(("emuxki_stream_new\n"));

	stream = malloc(sizeof(emuxki_stream));
	if (stream == NULL)
		return (NULL);
	stream->card = card;
	stream->use = use;
	stream->state = !EMU_STATE_STARTED;
	stream->stereo = EMU_STEREO_NOTSET;
	stream->b16 = 0;
	stream->sample_rate = 0;
	stream->nmono = 0;
	stream->nstereo = 0;
	stream->bufframes = bufframes;
	stream->bufcount = bufcount;
	stream->first_voice = NULL;
	stream->inth = NULL;
	stream->inthparam = NULL;
		
	stream->frames_count = 0;
	stream->real_time = 0;
	stream->update_needed = false;

	/* Init voices list */
	LIST_INIT(&(stream->voices));
	
	status = lock();
	LIST_INSERT_HEAD((&card->streams), stream, next);
	unlock(status);
	
	return stream;
}

void
emuxki_stream_delete(emuxki_stream *stream)
{
	emuxki_voice *voice;
	cpu_status status;
	LOG(("emuxki_stream_delete\n"));
	
	emuxki_stream_halt(stream);	
	
	status = lock();
	LIST_REMOVE(stream, next);
	unlock(status);
	
	while(!LIST_EMPTY(&stream->voices)) {
		voice = LIST_FIRST(&stream->voices);
		LIST_REMOVE(voice, next);
		if(voice->buffer)
			emuxki_mem_free(stream->card, voice->buffer->log_base);
		emuxki_voice_delete(voice);
	}
	
	free(stream);
}

/* Emuxki gprs */
// 87 values from 0.0dB to -xdB (-0.75dB each)
static uint32 db_table[] = {
	2147483647,	1969835071,	1806882308,	1657409659,	1520301995,	1394536435,	1279174712,
	1173356181,	1076291388,	987256190,	905586345,	830672562,	761955951,	698923858,
	641106035,	588071138,	539423503,	494800198,	453868315,	416322483,	381882595,
	350291714,	321314160,	294733747,	270352173,	247987542,	227473005,	208655513,
	191394681,	175561735,	161038555,	147716791,	135497057,	124288190,	114006566,
	104575479,	95924570,	87989300,	80710468,	74033770,	67909395,	62291654,
	57138635,	52411895,	48076170,	44099114,	40451056,	37104780,	34035322,
	31219781,	28637154,	26268172,	24095162,	22101913,	20273552,	18596442,
	17058068,	15646956,	14352576,	13165272,	12076188,	11077196,	10160845,
	9320299,	8549286,	7842054,	7193328,	6598266,	6052431,	5551749,
	5092486,	4671215,	4284793,	3930337,	3605204,	3306967,	3033401,
	2782465,	2552289,	2341153,	2147483,	1969835,	1806882,	1657409,
	1520301,	1394536,	1279174
};

void
emuxki_gpr_set(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values)
{
	uint8 count = gpr->type & EMU_MIX_STEREO ? 2 : 1;
	uint8 i;
	uint32 index;
	
	LOG(("emuxki_set_gpr\n"));
	
	switch(type) {
		case EMU_MIX_MUTE:
			gpr->mute = (values[0] == 1.0);
			if(gpr->mute) {
				for(i=0; i<count; i++)
					emuxki_write_gpr(&card->config, gpr->gpr + i, 0);
				break;
			}
			for(i=0; i<count; i++) {
				values[i] = gpr->current[i];
			}	
		case EMU_MIX_GAIN:
			for(i=0; i<count; i++) {
				if(values[i]>gpr->max_gain || values[i]<gpr->min_gain)
					return;
				index = (int32)(values[i] / gpr->granularity);
				if(index > sizeof(db_table)/sizeof(db_table[0]))
					index = sizeof(db_table)/sizeof(db_table[0]);
				else if(index < 0)
					index = 0;
				LOG(("emuxki_set_gpr gpr: %d \n", gpr->gpr + i));
				LOG(("emuxki_set_gpr values[i]: %g \n", values[i]));
				LOG(("emuxki_set_gpr index: %u \n", index));
				if(!gpr->mute)
					emuxki_write_gpr(&card->config, gpr->gpr + i, db_table[index]);
				gpr->current[i] = index * gpr->granularity;
			}
			break;
	}
}

void
emuxki_gpr_get(emuxki_dev *card, emuxki_gpr *gpr, int32 type, float *values)
{
	uint8 count = gpr->type & EMU_MIX_STEREO ? 2 : 1;
	uint16 i;
		
	LOG(("emuxki_get_gpr\n"));
	
	switch(type) {
		case EMU_MIX_GAIN:
			for(i=0; i<count; i++) {
				values[i] = gpr->current[i];
			}
			break;
		case EMU_MIX_MUTE:
			values[0] = (gpr->mute ? 1.0 : 0.0);
			break;
	}
}

void 
emuxki_gpr_dump(emuxki_dev * card, uint16 count)
{
	uint16      pc;
	uint32		value;
	
	LOG(("emuxki_dump_gprs\n"));

	for(pc = 0; pc < count; pc++) {
		value = emuxki_read_gpr(&card->config, pc);
		LOG(("dsp_gpr pc=%x, value=%x\n", pc, value));
	}
}

static emuxki_gpr *
emuxki_gpr_new(emuxki_dev *card, const char *name, emuxki_gpr_type type, uint16 *gpr_num, 
			float default_value, float default_mute, float min_gain, float max_gain, float granularity)
{
	emuxki_gpr *gpr;
	float	values[2];
	
	LOG(("emuxki_gpr_new\n"));
	
	gpr = &card->gpr[*gpr_num];
	strncpy(gpr->name, name, 32);
	gpr->type = type;
	gpr->gpr = *gpr_num;
	gpr->default_value = default_value;
	gpr->min_gain = min_gain;
	gpr->max_gain = max_gain;
	gpr->granularity = granularity;
	gpr->mute = false;
	(*gpr_num)++;
	if(gpr->type & EMU_MIX_STEREO)
		(*gpr_num)++;
	
	if(default_mute == 1.0) {
		values[0] = default_mute;
		emuxki_gpr_set(card, gpr, EMU_MIX_MUTE, values);
	}
	
	values[0] = gpr->default_value;
	if(gpr->type & EMU_MIX_STEREO)
		values[1] = gpr->default_value;
	emuxki_gpr_set(card, gpr, EMU_MIX_GAIN, values);
	
	
	return gpr;
}

/* Emuxki parameter */

void 
emuxki_parameter_set(emuxki_dev *card, const void* cookie, int32 type, int32 *value)
{
	emuxki_stream *stream;
	emuxki_voice *voice;
	LOG(("emuxki_parameter_set\n"));
	
	switch(type) {
		case EMU_DIGITAL_MODE:
			card->digital_enabled = *value == 1;
			if(IS_AUDIGY(&card->config))
				if(IS_AUDIGY2(&card->config)) {
					// this disables analog, not enough
					emuxki_reg_write_32(&card->config, EMU_A_IOCFG, 
						(card->digital_enabled ? 0 : EMU_A_IOCFG_GPOUT0) | 
							(emuxki_reg_read_32(&card->config, EMU_A_IOCFG) 
							& ~(EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1) ) );
				} else {
					// this disables analog, and enables digital
					emuxki_reg_write_32(&card->config, EMU_A_IOCFG, 
						(card->digital_enabled ? EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1 : 0) | 
							(emuxki_reg_read_32(&card->config, EMU_A_IOCFG) 
							& ~(EMU_A_IOCFG_GPOUT0 | EMU_A_IOCFG_GPOUT1) ) );
				}
			else {
				// this enables digital, not enough
				emuxki_reg_write_32(&card->config, EMU_HCFG, 
					(card->digital_enabled ? EMU_HCFG_GPOUTPUT0 : 0) | 
					(emuxki_reg_read_32(&card->config, EMU_HCFG) & ~EMU_HCFG_GPOUTPUT0));
			}
			
			break;
		case EMU_AUDIO_MODE:
			if(*value!=0 && *value!=1 && *value!=2) {
				PRINT(("emuxki_parameter_set error value unexpected\n"));
				return;
			}
			card->play_mode = (*value + 1) * 2;
			LIST_FOREACH(stream, &card->streams, next) {
				if ((stream->use & EMU_USE_PLAY) == 0 ||
					(stream->state & EMU_STATE_STARTED) == 0)
						continue;
				LIST_FOREACH(voice, &stream->voices, next) {
					emuxki_voice_fxupdate(voice);
					emuxki_channel_commit_fx(voice->dataloc.chan[0]);
					if (voice->stereo)
						emuxki_channel_commit_fx(voice->dataloc.chan[1]);
				}
			}
			break;
	}
}

void 
emuxki_parameter_get(emuxki_dev *card, const void* cookie, int32 type, int32 *value)
{
	LOG(("emuxki_parameter_get\n"));
	
	switch(type) {
		case EMU_DIGITAL_MODE:
			*value = card->digital_enabled ? 1 : 0;
			break;
		case EMU_AUDIO_MODE:
			*value = card->play_mode / 2 - 1;
			break;
	}
}

/* Emuxki interrupt */
static int32 
emuxki_int(void *arg)
{
	emuxki_dev	 *card = arg;
	uint32       ipr, curblk;
	bool gotone = false;
	emuxki_voice *voice;
	emuxki_stream *stream;
	
	while ((ipr = emuxki_reg_read_32(&card->config, EMU_IPR))) {
		gotone = true;
		if (ipr & EMU_IPR_INTERVALTIMER) {
			//TRACE(("EMU_IPR_INTERVALTIMER\n"));
			LIST_FOREACH(stream, &card->streams, next) {
				if ((stream->use & EMU_USE_PLAY) == 0 ||
					(stream->state & EMU_STATE_STARTED) == 0 ||
					(stream->inth == NULL))
						continue;
				
				voice = stream->first_voice;
				//TRACE(("voice %p\n", voice));
				curblk = emuxki_voice_curaddr(voice) /
				       voice->blksize;
				//TRACE(("EMU_IPR_INTERVALTIMER at trigblk %lu\n", curblk));
				//TRACE(("EMU_IPR_INTERVALTIMER at voice->trigblk %lu\n", voice->trigblk));
				if (curblk == voice->trigblk) {
					//TRACE(("EMU_IPR_INTERVALTIMER at trigblk %lu\n", curblk));
					//dump_voice(voice);
					//trace_hardware_regs(&card->config);
					//TRACE(("voice pointer %p\n", voice));
														
					if(stream->inth)
						stream->inth(stream->inthparam);

					voice->trigblk++;
					voice->trigblk %= voice->blkmod;
				}
			}
		}
#if MIDI
		if (ipr & (/*EMU_IPR_MIDITRANSBUFE | */EMU_IPR_MIDIRECVBUFE)) {
			midi_interrupt(card);
		}
		if (ipr & (EMU_IPR_MIDITRANSBUFE)) {
			if(!midi_interrupt(card)) {
				emuxki_inte_disable(&card->config, EMU_INTE_MIDITXENABLE);
				TRACE(("EMU_INTE_MIDITXENABLE disabled\n"));
			}
		}
#endif
		if (ipr & (EMU_IPR_ADCBUFHALFFULL | EMU_IPR_ADCBUFFULL
			| EMU_IPR_MICBUFHALFFULL | EMU_IPR_MICBUFFULL
			| EMU_IPR_EFXBUFHALFFULL | EMU_IPR_EFXBUFFULL)) {
			//TRACE(("EMU_IPR_ADCBUF\n"));
			LIST_FOREACH(stream, &card->streams, next) {
				if ((stream->use & EMU_USE_RECORD) == 0 ||
					(stream->state & EMU_STATE_STARTED) == 0 ||
					(stream->inth == NULL) ||
					(stream->first_voice == NULL))
						continue;
				voice = stream->first_voice;
				curblk = emuxki_voice_curaddr(voice) /
				       voice->blksize;
				//TRACE(("EMU_IPR_ADCBUF at trigblk %lu\n", curblk));
				//TRACE(("EMU_IPR_ADCBUF at voice->trigblk %lu\n", voice->trigblk));
				if (curblk == voice->trigblk) {
					//TRACE(("EMU_IPR_ADCBUF at trigblk %lu\n", curblk));
					//dump_voice(voice);
					//trace_hardware_regs(&card->config);
														
					if(stream->inth)
						stream->inth(stream->inthparam);

					voice->trigblk++;
					voice->trigblk %= voice->blkmod;
				}
			}
		}
		
		/*if (ipr & (EMU_IPR_CHANNELLOOP)) {
			TRACE(("EMU_IPR_CHANNELLOOP pending channel : %u\n", ipr & EMU_IPR_CHNOMASK));
			LIST_FOREACH(stream, &card->streams, next)
			LIST_FOREACH(voice, &stream->voices, next) {
				if ((voice->use & EMU_USE_PLAY) == 0 ||
					(voice->state & EMU_STATE_STARTED) == 0)
						continue;
				TRACE(("EMU_IPR_CHANNELLOOP at trigblk %lu\n", emuxki_voice_curaddr(voice)));
				TRACE(("EMU_IPR_CHANNELLOOP read %x\n", emuxki_chan_read(&voice->card->config, 0, EMU_CLIPL)));
				emuxki_chan_write(&voice->card->config, 0, EMU_CLIPL, emuxki_chan_read(&voice->card->config, 0, EMU_CLIPL));
			}
		}*/
		
		if (ipr & ~(EMU_IPR_RATETRCHANGE | EMU_IPR_INTERVALTIMER 
				| EMU_IPR_MIDITRANSBUFE | EMU_IPR_MIDIRECVBUFE
				| EMU_IPR_ADCBUFHALFFULL | EMU_IPR_ADCBUFFULL
				| EMU_IPR_MICBUFHALFFULL | EMU_IPR_MICBUFFULL
				| EMU_IPR_EFXBUFHALFFULL | EMU_IPR_EFXBUFFULL))
			TRACE(("Got interrupt 0x%08x !!!\n",
			       ipr & ~(EMU_IPR_RATETRCHANGE |
				       EMU_IPR_INTERVALTIMER)));

		emuxki_reg_write_32(&card->config, EMU_IPR, ipr);
	}
	
	if(gotone)
		return B_HANDLED_INTERRUPT;

	TRACE(("Got unhandled interrupt\n"));
	return B_UNHANDLED_INTERRUPT;
}



/*	Emu10k1 driver functions */


/* detect presence of our hardware */
status_t 
init_hardware(void)
{
	int ix=0;
	pci_info info;
	status_t err = ENODEV;
	
	LOG_CREATE();

	PRINT(("emuxki: init_hardware()\n"));

	if (get_module(pci_name, (module_info **)&pci))
		return ENOSYS;

	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
		if (info.vendor_id == CREATIVELABS_VENDOR_ID &&
			(info.device_id == CREATIVELABS_SBLIVE_DEVICE_ID
#if AUDIGY
			|| info.device_id == CREATIVELABS_AUDIGY_DEVICE_ID
#endif
			)) {
			err = B_OK;
		}
		ix++;
	}
		
	put_module(pci_name);

	return err;
}

static void
make_device_names(
	emuxki_dev * card)
{

#if MIDI	
	sprintf(card->midi.name, "midi/emuxki/%ld", card-cards+1);
	names[num_names++] = card->midi.name;
#endif
	
	sprintf(card->name, "audio/multi/emuxki/%ld", card-cards+1);
	names[num_names++] = card->name;

	names[num_names] = NULL;
}


static status_t
emuxki_setup(emuxki_dev * card)
{
	status_t err = B_OK;
	unsigned char cmd;

	PRINT(("setup_emuxki(%p)\n", card));

	make_device_names(card);
	card->config.nabmbar = card->info.u.h0.base_registers[0];
	card->config.irq = card->info.u.h0.interrupt_line;
	card->config.type = 0;
	if(card->info.device_id == CREATIVELABS_AUDIGY_DEVICE_ID)
		card->config.type |= TYPE_AUDIGY;
	if(IS_AUDIGY(&card->config) && card->info.revision == 4)
		card->config.type |= TYPE_AUDIGY2;
	
	PRINT(("%s deviceid = %#04x chiprev = %x model = %x enhanced at %x\n", card->name, card->info.device_id,
		card->info.revision, card->info.u.h0.subsystem_id, card->config.nabmbar));
		
	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
	PRINT(("PCI command before: %x\n", cmd));
	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
	PRINT(("PCI command after: %x\n", cmd));

	dump_hardware_regs(&card->config);

	emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE | EMU_HCFG_LOCKTANKCACHE_MASK| 
		EMU_HCFG_MUTEBUTTONENABLE);
	emuxki_reg_write_32(&card->config, EMU_INTE, EMU_INTE_SAMPLERATER | EMU_INTE_PCIERRENABLE);
	
	dump_hardware_regs(&card->config);
	
#if MIDI
	if ((err = (*mpu401->create_device)(card->config.nabmbar + (IS_AUDIGY(&card->config) ? EMU_A_MUDATA1 : EMU_MUDATA), 
		&card->midi.driver, 0, midi_interrupt_op, &card->midi)) < B_OK)
		return (err);
		
	card->midi.card = card;
#endif

	/* reset the codec */	
	PRINT(("codec reset\n"));
	emuxki_codec_write(&card->config, 0x00, 0x0000);
	snooze(50000); // 50 ms

	ac97_init(&card->config);
	ac97_amp_enable(&card->config, true);

	PRINT(("codec vendor id      = %#08x\n",ac97_get_vendor_id(&card->config)));
	PRINT(("codec description     = %s\n",ac97_get_vendor_id_description(&card->config)));
	PRINT(("codec 3d enhancement = %s\n",ac97_get_3d_stereo_enhancement(&card->config)));
	
	if(IS_AUDIGY2(&card->config)) {
		emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_AUDIOENABLE |
			EMU_HCFG_AC3ENABLE_CDSPDIF | EMU_HCFG_AC3ENABLE_GPSPDIF| 
			EMU_HCFG_JOYENABLE | EMU_HCFG_AUTOMUTE);
	} else if(IS_AUDIGY(&card->config)) {
		emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_AUDIOENABLE |
			EMU_HCFG_JOYENABLE | EMU_HCFG_AUTOMUTE);
	} else {
		emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_AUDIOENABLE |
			EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_JOYENABLE | EMU_HCFG_AUTOMUTE);
	}
	emuxki_inte_enable(&card->config, EMU_INTE_VOLINCRENABLE | EMU_INTE_VOLDECRENABLE 
		| EMU_INTE_MUTEENABLE | EMU_INTE_FXDSPENABLE);
	
	if(IS_AUDIGY2(&card->config)) {
		emuxki_reg_write_32(&card->config, EMU_A_IOCFG, 
			EMU_A_IOCFG_GPOUT0 | emuxki_reg_read_32(&card->config, EMU_A_IOCFG));
	}
	
	dump_hardware_regs(&card->config);
	
	/*PRINT(("codec master output = %#04x\n",emuxki_codec_read(&card->config, 0x02)));
	PRINT(("codec aux output    = %#04x\n",emuxki_codec_read(&card->config, 0x04)));
	PRINT(("codec mono output   = %#04x\n",emuxki_codec_read(&card->config, 0x06)));
	PRINT(("codec pcm output    = %#04x\n",emuxki_codec_read(&card->config, 0x18)));
	PRINT(("codec line in	    = %#04x\n",emuxki_codec_read(&card->config, 0x10)));
	PRINT(("codec record line in= %#04x\n",emuxki_codec_read(&card->config, 0x1a)));
	PRINT(("codec record gain   = %#04x\n",emuxki_codec_read(&card->config, 0x1c)));*/
	
	/*PRINT(("adc index	    = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCIDX, 0)));
	PRINT(("micro index 	= %#08x\n",emuxki_chan_read(&card->config, EMU_MICIDX, 0)));
	PRINT(("fx index   		= %#08x\n",emuxki_chan_read(&card->config, EMU_FXIDX, 0)));
	PRINT(("adc addr	    = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCBA, 0)));
	PRINT(("micro addr 		= %#08x\n",emuxki_chan_read(&card->config, EMU_MICBA, 0)));
	PRINT(("fx addr   		= %#08x\n",emuxki_chan_read(&card->config, EMU_FXBA, 0)));
	PRINT(("adc size	    = %#08x\n",emuxki_chan_read(&card->config, EMU_ADCBS, 0)));
	PRINT(("micro size 		= %#08x\n",emuxki_chan_read(&card->config, EMU_MICBS, 0)));
	PRINT(("fx size   		= %#08x\n",emuxki_chan_read(&card->config, EMU_FXBS, 0)));

	PRINT(("EMU_ADCCR   		= %#08x\n",emuxki_chan_read(&card->config, EMU_ADCCR, 0)));
	PRINT(("EMU_FXWC   		= %#08x\n",emuxki_chan_read(&card->config, EMU_FXWC, 0)));
	PRINT(("EMU_FXWC   		= %#08x\n",emuxki_reg_read_32(&card->config, EMU_FXWC)));*/

	PRINT(("writing codec registers\n"));
	// TODO : to move with AC97
	/* enable master output */
	emuxki_codec_write(&card->config, AC97_MASTER_VOLUME, 0x0000);
	/* enable aux output */
	emuxki_codec_write(&card->config, AC97_AUX_OUT_VOLUME, 0x0000);
	/* enable mono output */
	//emuxki_codec_write(&card->config, AC97_MONO_VOLUME, 0x0004);
	/* enable pcm output */
	emuxki_codec_write(&card->config, AC97_PCM_OUT_VOLUME, 0x0808);
	/* enable line in */
	//emuxki_codec_write(&card->config, AC97_LINE_IN_VOLUME, 0x8808);
	/* set record line in */
	emuxki_codec_write(&card->config, AC97_RECORD_SELECT, 0x0404);
	/* set record gain */
	//emuxki_codec_write(&card->config, AC97_RECORD_GAIN, 0x0000);

	PRINT(("codec master output = %#04x\n",emuxki_codec_read(&card->config, AC97_MASTER_VOLUME)));
	PRINT(("codec aux output    = %#04x\n",emuxki_codec_read(&card->config, AC97_AUX_OUT_VOLUME)));
	PRINT(("codec mono output   = %#04x\n",emuxki_codec_read(&card->config, AC97_MONO_VOLUME)));
	PRINT(("codec pcm output    = %#04x\n",emuxki_codec_read(&card->config, AC97_PCM_OUT_VOLUME)));
	PRINT(("codec line in	    = %#04x\n",emuxki_codec_read(&card->config, AC97_LINE_IN_VOLUME)));
	PRINT(("codec record line in= %#04x\n",emuxki_codec_read(&card->config, AC97_RECORD_SELECT)));
	PRINT(("codec record gain   = %#04x\n",emuxki_codec_read(&card->config, AC97_RECORD_GAIN)));
	
	if (emuxki_codec_read(&card->config, AC97_EXTENDED_AUDIO_ID) & 0x0080) {
		card->config.type |= TYPE_LIVE_5_1;
		emuxki_chan_write(&card->config, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE);
		emuxki_codec_write(&card->config, AC97_SURROUND_VOLUME, 0x0000);
	}
	
	if ((err = emuxki_init(card)))
		return (err);
	
	if(IS_AUDIGY(&card->config) || IS_LIVE_5_1(&card->config)) {
		card->play_mode = 6;	// mode 5.1
	} else {
		card->play_mode = 4;	// mode 4.0
	}
	
	PRINT(("installing interrupt : %x\n", card->config.irq));
	install_io_interrupt_handler(card->config.irq, emuxki_int, card, 0);
	
	PRINT(("init_driver done\n"));

	return err;
}

void 
emuxki_dump_fx(emuxki_dev * card)
{
	uint16      pc = 0;
	uint8		op;
	uint16		r,a,x,y,zero;
	
	LOG(("emuxki_dump_fx\n"));

	zero = IS_AUDIGY(&card->config) ? EMU_A_DSP_CST(0) : EMU_DSP_CST(0);
	
	while (pc < 512) {
		emuxki_dsp_getop(&card->config, &pc, &op, &r, &a, &x, &y);
		if(op!=EMU_DSP_OP_ACC3 || r!=zero || a!=zero || x!=zero || y!=zero) {
			LOG(("dsp_op pc=%u, op=%x, r=%x, a=%x, x=%x, y=%x\n",
				pc, op, r, a, x, y));
		}
	}
}

static void
emuxki_initfx(emuxki_dev * card)
{
	uint16       pc, gpr;
	emuxki_gpr *a_front_gpr, *a_rear_gpr, *a_center_sub_gpr = NULL;
	emuxki_gpr *p_ac97_in_gpr, *p_cd_in_gpr, *r_ac97_in_gpr, *r_cd_in_gpr, *r_fx_out_gpr;
	emuxki_gpr *d_front_gpr, *d_rear_gpr, *d_center_sub_gpr;

	/* Set all GPRs to 0 */
	for (pc = 0; pc < 256; pc++) {
		emuxki_write_gpr(&card->config, pc, 0);
		card->gpr[pc].gpr = -1;
	}
	
	for (pc = 0; pc < 160; pc++) {
		emuxki_chan_write(&card->config, 0, EMU_TANKMEMDATAREGBASE + pc, 0);
		emuxki_chan_write(&card->config, 0, EMU_TANKMEMADDRREGBASE + pc, 0);
	}
	pc = 0;
	gpr = EMU_GPR_FIRST_MIX;	// we reserve 16 gprs for processing 
#define EMU_DSP_TMPGPR_FRONT_LEFT 0
#define EMU_DSP_TMPGPR_FRONT_RIGHT 1
#define EMU_DSP_TMPGPR_REAR_LEFT 2
#define EMU_DSP_TMPGPR_REAR_RIGHT 3
#define EMU_DSP_TMPGPR_CENTER 4
#define EMU_DSP_TMPGPR_SUB 5
#define EMU_DSP_TMPGPR_DSP_IN_L	6
#define EMU_DSP_TMPGPR_DSP_IN_R	7
	
	a_front_gpr = emuxki_gpr_new(card, "Analog Front", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	a_rear_gpr = emuxki_gpr_new(card, "Analog Rear", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	if(IS_AUDIGY(&card->config) || IS_LIVE_5_1(&card->config))
		a_center_sub_gpr = emuxki_gpr_new(card, "Analog Center/Sub", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	
	d_front_gpr = emuxki_gpr_new(card, "Digital Front", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	d_rear_gpr = emuxki_gpr_new(card, "Digital Rear", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	d_center_sub_gpr = emuxki_gpr_new(card, "Digital Center/Sub", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	
	/* playback in gprs */
	p_ac97_in_gpr = emuxki_gpr_new(card, "AC97 Record In", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 1.0, -46.5, 0.0, -0.75);
	p_cd_in_gpr = emuxki_gpr_new(card, "CD Spdif In", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_PLAYBACK, &gpr, 0.0, 1.0, -46.5, 0.0, -0.75);
	
	/* record in gprs */
	r_ac97_in_gpr = emuxki_gpr_new(card, "AC97 Record In", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_RECORD, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	r_cd_in_gpr = emuxki_gpr_new(card, "CD Spdif In", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_RECORD, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
	r_fx_out_gpr = emuxki_gpr_new(card, "FX 0/1", 
			EMU_MIX_GAIN|EMU_MIX_STEREO|EMU_MIX_MUTE|EMU_MIX_RECORD, &gpr, 0.0, 0.0, -46.5, 0.0, -0.75);
		
	card->gpr_count = gpr;
			
	if(IS_AUDIGY(&card->config)) {
		/* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * P_AC97_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_INL(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(p_ac97_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_INR(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(p_ac97_in_gpr->gpr + 1));
		
		/* DSP_IN_GPR(l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * P_CD_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(p_cd_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(p_cd_in_gpr->gpr + 1));
		
		/* Front GPR(l/r) = DSP_IN_GPR(l/r) + FX(0/1) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(0), EMU_A_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(1), EMU_A_DSP_CST(4));
		
		/* Rear GPR(l/r) = DSP_IN_GPR(l/r) + FX(2/3) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(2), EMU_A_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(3), EMU_A_DSP_CST(4));
				  
		/* Center/Sub GPR = 0 + FX(4/5) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER),
				  EMU_A_DSP_CST(0),
				  EMU_DSP_FX(4), EMU_A_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB),
				  EMU_A_DSP_CST(0),
				  EMU_DSP_FX(5), EMU_A_DSP_CST(4));
		
		/* Analog Front Output l/r = 0 + Front GPR(l/r) * A_FRONT_GPR(l/r) */  
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_FRONT),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_A_DSP_GPR(a_front_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_FRONT),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_A_DSP_GPR(a_front_gpr->gpr+1));
		
		/* Analog Rear Output l/r = 0 + Rear GPR(l/r) * A_REAR_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_REAR),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_A_DSP_GPR(a_rear_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_REAR),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_A_DSP_GPR(a_rear_gpr->gpr+1));
		
		/* Analog Center/Sub = 0 + Center/Sub GPR(l/r) * A_CENTER_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_A_CENTER),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_A_DSP_GPR(a_center_sub_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_A_CENTER),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_A_DSP_GPR(a_center_sub_gpr->gpr+1));
				  
		/* Digital Front Output l/r = 0 + Front GPR(l/r) * D_FRONT_GPR(l/r) */  
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_FRONT),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_A_DSP_GPR(d_front_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_FRONT),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_A_DSP_GPR(d_front_gpr->gpr+1));
		
		/* Digital Rear Output l/r = 0 + Rear GPR(l/r) * D_REAR_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_REAR),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_A_DSP_GPR(d_rear_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_REAR),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_A_DSP_GPR(d_rear_gpr->gpr+1));
		
		/* Digital Center/Sub = 0 + Center/Sub GPR(l/r) * D_CENTER_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_D_CENTER),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_A_DSP_GPR(d_center_sub_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_D_CENTER),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_A_DSP_GPR(d_center_sub_gpr->gpr+1));
				  
		/* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * R_AC97_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_INL(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(r_ac97_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_INR(EMU_DSP_IN_AC97), EMU_A_DSP_GPR(r_ac97_in_gpr->gpr + 1));
				  
		/* DSP_IN_GPR (l/r) = DSP_IN_GPR(l/r) + FX(0/1) * R_FX_OUT_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(0), EMU_A_DSP_GPR(r_fx_out_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(1), EMU_A_DSP_GPR(r_fx_out_gpr->gpr + 1));
				  
		/* DSP_IN_GPR(l/r) = 0 + DSP_IN_GPR(l/r) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), EMU_A_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_CST(0),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), EMU_A_DSP_CST(4));
				  
		/* ADC recording buffer (l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * R_CD_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTL(EMU_A_DSP_OUT_ADC),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_A_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(r_cd_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_A_DSP_OUTR(EMU_A_DSP_OUT_ADC),
				  EMU_A_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_A_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_A_DSP_GPR(r_cd_in_gpr->gpr + 1));
		
		
				
		/* zero out the rest of the microcode */
		while (pc < 512)
			emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3,
				  EMU_A_DSP_CST(0), EMU_A_DSP_CST(0),
				  EMU_A_DSP_CST(0), EMU_A_DSP_CST(0));
		
		emuxki_chan_write(&card->config, 0, EMU_A_DBG, 0);	/* Is it really necessary ? */	
	
	} else {
		/* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * P_AC97_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_CST(0),
				  EMU_DSP_INL(EMU_DSP_IN_AC97), EMU_DSP_GPR(p_ac97_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_CST(0),
				  EMU_DSP_INR(EMU_DSP_IN_AC97), EMU_DSP_GPR(p_ac97_in_gpr->gpr + 1));
		
		/* DSP_IN_GPR(l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * P_CD_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(p_cd_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(p_cd_in_gpr->gpr + 1));
	
		/* Front GPR(l/r) = DSP_IN_GPR(l/r) + FX(0/1) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(0), EMU_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(1), EMU_DSP_CST(4));
		
		/* Rear GPR(l/r) = DSP_IN_GPR(l/r) + FX(2/3) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(2), EMU_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(3), EMU_DSP_CST(4));
				  
		/* Center/Sub GPR = 0 + FX(4/5) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER),
				  EMU_DSP_CST(0),
				  EMU_DSP_FX(4), EMU_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB),
				  EMU_DSP_CST(0),
				  EMU_DSP_FX(5), EMU_DSP_CST(4));
		
		/* Analog Front Output l/r = 0 + Front GPR(l/r) * A_FRONT_GPR(l/r) */  
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_A_FRONT),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_DSP_GPR(a_front_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_A_FRONT),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_DSP_GPR(a_front_gpr->gpr+1));
		
		/* Analog Rear Output l/r = 0 + Rear GPR(l/r) * A_REAR_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_AD_REAR),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_DSP_GPR(a_rear_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_AD_REAR),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_DSP_GPR(a_rear_gpr->gpr+1));
		
		/* Analog Center/Sub = 0 + Center/Sub GPR(l/r) * A_CENTER_GPR(l/r) */
		if(IS_LIVE_5_1(&card->config)) {
			emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
					  EMU_DSP_OUT_A_CENTER,
					  EMU_DSP_CST(0),
					  EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_DSP_GPR(a_center_sub_gpr->gpr));
			emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
					  EMU_DSP_OUT_A_SUB,
					  EMU_DSP_CST(0),
					  EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_DSP_GPR(a_center_sub_gpr->gpr+1));
		}
			  
		/* Digital Front Output l/r = 0 + Front GPR(l/r) * D_FRONT_GPR(l/r) */  
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_D_FRONT),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_LEFT), EMU_DSP_GPR(d_front_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_D_FRONT),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_FRONT_RIGHT), EMU_DSP_GPR(d_front_gpr->gpr+1));
		
		/* Digital Rear Output l/r = 0 + Rear GPR(l/r) * D_REAR_GPR(l/r) */
		/*emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_D_REAR),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_LEFT), EMU_DSP_GPR(d_rear_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_D_REAR),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_REAR_RIGHT), EMU_DSP_GPR(d_rear_gpr->gpr+1));*/
		
		/* Digital Center/Sub = 0 + Center/Sub GPR(l/r) * D_CENTER_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_D_CENTER),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_CENTER), EMU_DSP_GPR(d_center_sub_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_D_CENTER),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_SUB), EMU_DSP_GPR(d_center_sub_gpr->gpr+1));
		
		/* DSP_IN_GPR(l/r) = 0 + AC97In(l/r) * R_AC97_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_CST(0),
				  EMU_DSP_INL(EMU_DSP_IN_AC97), EMU_DSP_GPR(r_ac97_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_CST(0),
				  EMU_DSP_INR(EMU_DSP_IN_AC97), EMU_DSP_GPR(r_ac97_in_gpr->gpr + 1));
				  
		/* DSP_IN_GPR (l/r) = DSP_IN_GPR(l/r) + FX(0/1) * R_FX_OUT_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_FX(0), EMU_DSP_GPR(r_fx_out_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_FX(1), EMU_DSP_GPR(r_fx_out_gpr->gpr + 1));
				  
		/* DSP_IN_GPR(l/r) = 0 + DSP_IN_GPR(l/r) * 4 */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L), EMU_DSP_CST(4));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACINTS,
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_CST(0),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R), EMU_DSP_CST(4));
				  
		/* ADC recording buffer (l/r) = DSP_IN_GPR(l/r) + CDIn(l/r) * R_CD_IN_GPR(l/r) */
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTL(EMU_DSP_OUT_ADC),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_L),
				  EMU_DSP_INL(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(r_cd_in_gpr->gpr));
		emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_MACS,
				  EMU_DSP_OUTR(EMU_DSP_OUT_ADC),
				  EMU_DSP_GPR(EMU_DSP_TMPGPR_DSP_IN_R),
				  EMU_DSP_INR(EMU_DSP_IN_CDSPDIF), EMU_DSP_GPR(r_cd_in_gpr->gpr + 1));
		
		/* zero out the rest of the microcode */
		while (pc < 512)
			emuxki_dsp_addop(&card->config, &pc, EMU_DSP_OP_ACC3,
				  EMU_DSP_CST(0), EMU_DSP_CST(0),
				  EMU_DSP_CST(0), EMU_DSP_CST(0));
				  
		emuxki_chan_write(&card->config, 0, EMU_DBG, 0);	/* Is it really necessary ? */
	}

	emuxki_dump_fx(card);
}

status_t
emuxki_init(emuxki_dev * card)
{
	uint16       i;
	uint32       spcs, *ptb;
	uint32	 	 silentpage;
	
	/* disable any channel interrupt */
	emuxki_chan_write(&card->config, 0, EMU_CLIEL, 0);
	emuxki_chan_write(&card->config, 0, EMU_CLIEH, 0);
	emuxki_chan_write(&card->config, 0, EMU_SOLEL, 0);
	emuxki_chan_write(&card->config, 0, EMU_SOLEH, 0);

	/* Set recording buffers sizes to zero */
	emuxki_chan_write(&card->config, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_MICBA, 0);
	emuxki_chan_write(&card->config, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_FXBA, 0);
	emuxki_chan_write(&card->config, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_ADCBA, 0);
	
	if(IS_AUDIGY(&card->config)) {
		emuxki_chan_write(&card->config, 0, EMU_SPBYPASS, EMU_SPBYPASS_24_BITS);
		emuxki_chan_write(&card->config, 0, EMU_AC97SLOT, EMU_AC97SLOT_CENTER | EMU_AC97SLOT_LFE);
	}

	/* Initialize all channels to stopped and no effects */
	for (i = 0; i < EMU_NUMCHAN; i++) {
		emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSV, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_IP, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_VTFT, 0xffff);
		emuxki_chan_write(&card->config, i, EMU_CHAN_CVCF, 0xffff);
		emuxki_chan_write(&card->config, i, EMU_CHAN_PTRX, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_CPF, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_CCR, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_PSST, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_DSL, 0x10);	/* Why 16 ? */
		emuxki_chan_write(&card->config, i, EMU_CHAN_CCCA, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_Z1, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_Z2, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_FXRT, 0x32100000);
		emuxki_chan_write(&card->config, i, EMU_CHAN_ATKHLDM, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSM, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_IFATN, 0xffff);
		emuxki_chan_write(&card->config, i, EMU_CHAN_PEFE, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_FMMOD, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_TREMFRQ, 24);
		emuxki_chan_write(&card->config, i, EMU_CHAN_FM2FRQ2, 24);
		emuxki_chan_write(&card->config, i, EMU_CHAN_TEMPENV, 0);
		/*** these are last so OFF prevents writing ***/
		emuxki_chan_write(&card->config, i, EMU_CHAN_LFOVAL2, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_LFOVAL1, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_ATKHLDV, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_ENVVOL, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_ENVVAL, 0);
	}

	/* set digital outputs format */
	spcs = (EMU_SPCS_CLKACCY_1000PPM | EMU_SPCS_SAMPLERATE_48 |
	      EMU_SPCS_CHANNELNUM_LEFT | EMU_SPCS_SOURCENUM_UNSPEC |
		EMU_SPCS_GENERATIONSTATUS | 0x00001200 /* Cat code. */ |
		0x00000000 /* IEC-958 Mode */ | EMU_SPCS_EMPHASIS_NONE |
		EMU_SPCS_COPYRIGHT);
	emuxki_chan_write(&card->config, 0, EMU_SPCS0, spcs);
	emuxki_chan_write(&card->config, 0, EMU_SPCS1, spcs);
	emuxki_chan_write(&card->config, 0, EMU_SPCS2, spcs);
	
	if(IS_AUDIGY2(&card->config)) {
		emuxki_chan_write(&card->config, 0, EMU_A2_SPDIF_SAMPLERATE, EMU_A2_SPDIF_UNKNOWN);
		
		emuxki_reg_write_32(&card->config, EMU_A2_PTR, EMU_A2_SRCSEL);
		emuxki_reg_write_32(&card->config, EMU_A2_DATA, 
			EMU_A2_SRCSEL_ENABLE_SPDIF | EMU_A2_SRCSEL_ENABLE_SRCMULTI);
		
		emuxki_reg_write_32(&card->config, EMU_A2_PTR, EMU_A2_SRCMULTI);
		emuxki_reg_write_32(&card->config, EMU_A2_DATA, EMU_A2_SRCMULTI_ENABLE_INPUT);
	}
	
	/* Let's play with sound processor */
	emuxki_initfx(card);
	
	/* allocate memory for our Page Table */
	card->ptb_area = alloc_mem(&card->ptb_phy_base, &card->ptb_log_base, 
		EMU_MAXPTE * sizeof(uint32), "emuxki ptb");
		
	/* This is necessary unless you like Metallic noise... */
	card->silentpage_area = alloc_mem(&card->silentpage_phy_base, &card->silentpage_log_base, 
		EMU_PTESIZE, "emuxki sp");
		
	if (card->ptb_area < B_OK || card->silentpage_area < B_OK) {
		PRINT(("couldn't allocate memory\n"));
		if (card->ptb_area > B_OK)
			delete_area(card->ptb_area);
		if (card->silentpage_area > B_OK)
			delete_area(card->silentpage_area);
		return B_ERROR;	
	}
	
	/* Zero out the silent page */
	/* This might not be always true, it might be 128 for 8bit channels */
	memset(card->ptb_log_base, 0, EMU_PTESIZE);
	
	/*
	 * Set all the PTB Entries to the silent page We shift the physical
	 * address by one and OR it with the page number. I don't know what
	 * the ORed index is for, might be a very useful unused feature...
	 */
	silentpage = ((uint32)card->silentpage_phy_base) << 1;
	ptb = card->ptb_log_base;
	for (i = 0; i < EMU_MAXPTE; i++)
		ptb[i] = silentpage | i;

	/* Write PTB address and set TCB to none */
	emuxki_chan_write(&card->config, 0, EMU_PTB, (uint32)card->ptb_phy_base);
	emuxki_chan_write(&card->config, 0, EMU_TCBS, 0);	/* This means 16K TCB */
	emuxki_chan_write(&card->config, 0, EMU_TCB, 0);	/* No TCB use for now */

	/*
	 * Set channels MAPs to the silent page.
	 * I don't know what MAPs are for.
	 */
	silentpage |= EMU_CHAN_MAP_PTI_MASK;
	for (i = 0; i < EMU_NUMCHAN; i++) {
		emuxki_chan_write(&card->config, i, EMU_CHAN_MAPA, silentpage);
		emuxki_chan_write(&card->config, i, EMU_CHAN_MAPB, silentpage);
		card->channel[i] = NULL;
	}

	/* Init streams list */
	LIST_INIT(&(card->streams));
	
	/* Init mems list */
	LIST_INIT(&(card->mem));

	/* Timer is stopped */
	card->timerstate &= ~EMU_TIMER_STATE_ENABLED;
	card->timerate = 0xffff;

	return B_OK;
}



status_t
init_driver(void)
{
	int ix=0;
	
	pci_info info;
	num_cards = 0;
	
	PRINT(("init_driver()\n"));
	load_driver_symbols("emuxki");

	if (get_module(pci_name, (module_info **) &pci))
		return ENOSYS;
		
	if (get_module(mpu401_name, (module_info **) &mpu401)) {
		put_module(pci_name);
		return ENOSYS;
	}

	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
		if (info.vendor_id == CREATIVELABS_VENDOR_ID &&
			(info.device_id == CREATIVELABS_SBLIVE_DEVICE_ID 
#if AUDIGY
			|| info.device_id == CREATIVELABS_AUDIGY_DEVICE_ID
#endif
			)) {
			if (num_cards == NUM_CARDS) {
				PRINT(("Too many emuxki cards installed!\n"));
				break;
			}
			memset(&cards[num_cards], 0, sizeof(emuxki_dev));
			cards[num_cards].info = info;
			if (emuxki_setup(&cards[num_cards])) {
				PRINT(("Setup of emuxki %ld failed\n", num_cards+1));
			}
			else {
				num_cards++;
			}
		}
		ix++;
	}
	if (!num_cards) {
		put_module(mpu401_name);
		put_module(pci_name);
		PRINT(("emuxki: no suitable cards found\n"));
		return ENODEV;
	}

	return B_OK;
}


static void
emuxki_shutdown(emuxki_dev *card)
{
	uint32       i;

	PRINT(("shutdown(%p)\n", card));
	remove_io_interrupt_handler(card->config.irq, emuxki_int, card);
	
	emuxki_reg_write_32(&card->config, EMU_HCFG, EMU_HCFG_LOCKSOUNDCACHE |
		  EMU_HCFG_LOCKTANKCACHE_MASK | EMU_HCFG_MUTEBUTTONENABLE);
	emuxki_reg_write_32(&card->config, EMU_INTE, 0);
	
	dump_hardware_regs(&card->config);
	
	/* Disable any Channels interrupts */
	emuxki_chan_write(&card->config, 0, EMU_CLIEL, 0);
	emuxki_chan_write(&card->config, 0, EMU_CLIEH, 0);
	emuxki_chan_write(&card->config, 0, EMU_SOLEL, 0);
	emuxki_chan_write(&card->config, 0, EMU_SOLEH, 0);
	
	/* Stop all channels */
	/* This shouldn't be necessary, i'll remove once everything works */
	for (i = 0; i < EMU_NUMCHAN; i++)
		emuxki_chan_write(&card->config, i, EMU_CHAN_DCYSUSV, 0);
	for (i = 0; i < EMU_NUMCHAN; i++) {
		emuxki_chan_write(&card->config, i, EMU_CHAN_VTFT, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_CVCF, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_PTRX, 0);
		emuxki_chan_write(&card->config, i, EMU_CHAN_CPF, 0);
	}
	
	/*
	 * deallocate Emu10k1 caches and recording buffers Again it will be
	 * removed because it will be done in voice shutdown.
	 */
	emuxki_chan_write(&card->config, 0, EMU_MICBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_MICBA, 0);
	emuxki_chan_write(&card->config, 0, EMU_FXBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_FXBA, 0);
	if(IS_AUDIGY(&card->config)) {
		emuxki_chan_write(&card->config, 0, EMU_A_FXWC1, 0);
		emuxki_chan_write(&card->config, 0, EMU_A_FXWC2, 0);
	} else {
		emuxki_chan_write(&card->config, 0, EMU_FXWC, 0);
	}
	emuxki_chan_write(&card->config, 0, EMU_ADCBS, EMU_RECBS_BUFSIZE_NONE);
	emuxki_chan_write(&card->config, 0, EMU_ADCBA, 0);

	/*
	 * I don't know yet how i will handle tank cache buffer,
	 * I don't even clearly  know what it is for
	 */
	emuxki_chan_write(&card->config, 0, EMU_TCB, 0);	/* 16K again */
	emuxki_chan_write(&card->config, 0, EMU_TCBS, 0);

	emuxki_chan_write(&card->config, 0, EMU_DBG, 0x8000);	/* necessary ? */
	
	PRINT(("freeing ptb_area\n"));
	if (card->ptb_area > B_OK)
		delete_area(card->ptb_area);
	PRINT(("freeing silentpage_area\n"));
	if (card->silentpage_area > B_OK)
		delete_area(card->silentpage_area);
}


void
uninit_driver(void)
{
	int ix, cnt = num_cards;
	num_cards = 0;

	PRINT(("uninit_driver()\n"));
	
	for (ix=0; ix<cnt; ix++) {
		emuxki_shutdown(&cards[ix]);
	}
	memset(&cards, 0, sizeof(cards));
	put_module(mpu401_name);
	put_module(pci_name);
}


const char **
publish_devices(void)
{
	int ix = 0;
	PRINT(("publish_devices()\n"));

	for (ix=0; names[ix]; ix++) {
		PRINT(("publish %s\n", names[ix]));
	}
	return (const char **)names;
}


device_hooks *
find_device(const char * name)
{
	int ix;

	PRINT(("emuxki: find_device(%s)\n", name));

	for (ix=0; ix<num_cards; ix++) {
#if MIDI
		if (!strcmp(cards[ix].midi.name, name)) {
			return &midi_hooks;
		}
#endif
		if (!strcmp(cards[ix].name, name)) {
			return &multi_hooks;
		}
	}
	PRINT(("emuxki: find_device(%s) failed\n", name));
	return NULL;
}

int32	api_version = B_CUR_DRIVER_API_VERSION;



