/*
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>
 *  Routines for control of EMU8000 chip
 *
 *  This is only prepared envelope for developers of EMU8000 code.
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "driver.h"
#include "emu8000.h"

/*

 */

#define EMU8000_CMD( addr, reg, chn ) \
				(((addr) << 16) | ((reg) << 5) | (chn))

#define EMU8000_DATA0	0x620
#define EMU8000_DATA1	0xa20
#define EMU8000_DATA2	0xa22
#define EMU8000_DATA3	0xe20
#define EMU8000_PTR	0xe22

#define EMU8000_U1	EMU8000_CMD( EMU8000_DATA3, 0, 0xe0 )
#define EMU8000_HWCF1	EMU8000_CMD( EMU8000_DATA1, 1, 29 )
#define EMU8000_HWCF2	EMU8000_CMD( EMU8000_DATA1, 1, 30 )
#define EMU8000_HWCF3	EMU8000_CMD( EMU8000_DATA1, 1, 31 )

static void snd_emu8000_poke(emu8000_t * emu,
			     unsigned int cmd, unsigned short val)
{
	out16(emu->port + 0x802, (unsigned short) cmd);
	out16((emu->port - 0x620) + (cmd >> 16), val);
}

static unsigned short snd_emu8000_peek(emu8000_t * emu, unsigned int cmd)
{
#if 1
	out16(emu->port + 0x802, (unsigned short) cmd);
	return in16((emu->port - 0x620) + (cmd >> 16));
#else
	unsigned short res;

	out16(emu->port + 0x802, (unsigned short) cmd);
	res = in16((emu->port - 0x620) + (cmd >> 16));
	snd_printd("res = 0x%x, cmd = 0x%x, cmd port = 0x%x, read port = 0x%x\n", res, cmd,
	       emu->port + 0x802,
	       (emu->port - 0x620) + (cmd >> 16));
	return res;
#endif
}

static int snd_emu8000_detect(emu8000_t * emu)
{
#if 0
	printk("0x620 = 0x%x\n", inw(0x620));
	printk("0x622 = 0x%x\n", inw(0x622));
	printk("0x624 = 0x%x\n", inw(0x624));
	printk("0xa20 = 0x%x\n", inw(0xa20));
	printk("0xe20 = 0x%x\n", inw(0xe20));
#endif
	if ((snd_emu8000_peek(emu, EMU8000_U1) & 0x000f) != 0x000c)
		return -ENODEV;
	if ((snd_emu8000_peek(emu, EMU8000_HWCF1) & 0x007e) != 0x0058)
		return -ENODEV;
	if ((snd_emu8000_peek(emu, EMU8000_HWCF2) & 0x0003) != 0x0003)
		return -ENODEV;
	snd_emu8000_poke(emu, EMU8000_HWCF1, 0x0059);
	snd_emu8000_poke(emu, EMU8000_HWCF2, 0x0020);
	snd_emu8000_poke(emu, EMU8000_HWCF3, 0x0000);
	return 0;
}

/*

 */

static void snd_emu8000_free(void *private_data)
{
	snd_free(private_data, sizeof(emu8000_t));
}

snd_synth_t *snd_emu8000_new_device(snd_card_t * card,
				    unsigned short base)
{
	snd_synth_t *synth;
	emu8000_t *emu;

	if ((synth = snd_synth_new_device(card, "EMU8000")) == NULL)
		return NULL;
	emu = snd_calloc(sizeof(emu8000_t));
	if (!emu) {
		snd_synth_free(synth);
		return NULL;
	}
	strcpy(synth->name, "EMU-8000");
	emu->port = base;
	synth->private_data = emu;
	synth->private_free = snd_emu8000_free;
	if (snd_emu8000_detect(emu) < 0) {
		snd_synth_free(synth);
		return NULL;
	}
	return synth;
}

