/*
 *  Copyright (c) by Jaroslav Kysela <perex@jcu.cz>,
 *                   Thomas Sailer <sailer@ife.ee.ethz.ch>
 *  Routines for control of Ensoniq AudioPCI ES1370/ES1371 chips
 *
 *  BUGS:
 *    --
 *
 *  TODO:
 *    --
 *
 *   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 "info.h"
#include "sndpci.h"
#include "control.h"
#include "audiopci.h"
#include "dma.h"

/*
 *  constants
 */

static const unsigned int snd_es1370_fixed_rates[] =
	{5512, 11025, 22050, 44100};
static const unsigned int snd_es1370_sample_shift[] =
	{0, 1, 1, 2};

/*
 *  common I/O routines
 */

static unsigned int snd_es1371_wait_src_ready(ensoniq_t * ensoniq)
{
	unsigned int t, r;

	for (t = 0; t < 500; t++) {
		if (!((r = inl(ES_REG(ensoniq, 1371_SMPRATE))) & ES_1371_SRC_RAM_BUSY))
			return r;
		snd_delay(1);
	}
	snd_printk("es1371: wait source ready timeout 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_SMPRATE), r);
	return 0;
}

static unsigned int snd_es1371_src_read(ensoniq_t * ensoniq, unsigned short reg)
{
	unsigned int r;

	r = snd_es1371_wait_src_ready(ensoniq) &
	    (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
	     ES_1371_DIS_P2 | ES_1371_DIS_R1);
	r |= ES_1371_SRC_RAM_ADDRO(reg);
	outl(r, ES_REG(ensoniq, 1371_SMPRATE));
	return ES_1371_SRC_RAM_DATAI(snd_es1371_wait_src_ready(ensoniq));
}

static void snd_es1371_src_write(ensoniq_t * ensoniq,
				 unsigned short reg, unsigned short data)
{
	unsigned int r;

	r = snd_es1371_wait_src_ready(ensoniq) &
	    (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
	     ES_1371_DIS_P2 | ES_1371_DIS_R1);
	r |= ES_1371_SRC_RAM_ADDRO(reg) | ES_1371_SRC_RAM_DATAO(data);
	outl(r | ES_1371_SRC_RAM_WE, ES_REG(ensoniq, 1371_SMPRATE));
}

static void snd_es1370_codec_write(void *private_data,
				   unsigned short reg, unsigned short val)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	signed long time = 500;

#if 0
	snd_printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x)\n", reg, val, ES_1370_CODEC_WRITE(reg, val));
#endif
	do {
		snd_mutex_lock(ensoniq, reg);
		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
			snd_mutex_unlock(ensoniq, reg);
			return;
		}
		snd_mutex_unlock(ensoniq, reg);
		time--;
		snd_delay(50);
	} while (time > 0);
#if 0
	snd_printk("es1370: codec write timeout\n");
#endif
}

static void snd_es1371_codec_write(void *private_data,
				   unsigned short reg, unsigned short val)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	unsigned int t, x, tmp = 0;
#if 0
	snd_printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x)\n", reg, val, ES_1371_CODEC_WRITE(reg, val));
#endif

	for (t = 0; t < 0x1000; t++) {
		snd_mutex_lock(ensoniq, reg);
		if (!(tmp = inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
			/* save the current state for latter */
			x = inl(ES_REG(ensoniq, 1371_SMPRATE));
			outl(snd_es1371_wait_src_ready(ensoniq) &
			     (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
			      ES_1371_DIS_P2 | ES_1371_DIS_R1),
			     ES_REG(ensoniq, 1371_SMPRATE));
			/* wait for a SAFE time to write addr/data and then do it, dammit */
			for (t = 0; t < 0x1000; t++)
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00070000) == 0x00010000)
					break;
			outl(ES_1371_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1371_CODEC));
			/* restore SRC reg */
			snd_es1371_wait_src_ready(ensoniq);
			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
			snd_mutex_unlock(ensoniq, reg);
			return;
		}
		snd_mutex_unlock(ensoniq, reg);
		snd_delay(10);
	}
	snd_printk("es1371: codec write timeout at 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
}

static unsigned short snd_es1371_codec_read(void *private_data,
					    unsigned short reg)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	unsigned int t, x, fail = 0, val, tmp = 0;

__again:
	for (t = 0; t < 0x1000; t++) {
		snd_mutex_lock(ensoniq, reg);
		if (!(tmp = inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
			/* save the current state for latter */
			x = inl(ES_REG(ensoniq, 1371_SMPRATE));
			outl(snd_es1371_wait_src_ready(ensoniq) &
			     (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
			      ES_1371_DIS_P2 | ES_1371_DIS_R1),
			     ES_REG(ensoniq, 1371_SMPRATE));
			/* wait for a SAFE time to write addr/data and then do it, dammit */
			for (t = 0; t < 0x1000; t++)
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00070000) == 0x00010000)
					break;
#if 0
			outl(ES_1371_CODEC_READS(AC97_RESET), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - RESET: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_MASTER), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - MASTER: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_HEADPHONE), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - HEADPHONE: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_MASTER_MONO), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - MASTER MONO: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_MASTER_TONE), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - MASTER TONE: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_PC_BEEP), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - PC BEEP: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_PHONE), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - PHONE: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_MIC), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - MIC: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_LINE), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - LINE: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_CD), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - CD: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_VIDEO), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - VIDEO: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_AUX), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - AUX: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_PCM), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - PCM: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_REC_SEL), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - REC SEL: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_REC_GAIN), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - REC GAIN: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_REC_GAIN_MIC), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - REC GAIN MIC: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_GENERAL_PURPOSE), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - GENERAL PURPOSE: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_3D_CONTROL), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - 3D CONTROL: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_RESERVED), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - RESERVED: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_POWERDOWN), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - POWERDOWN: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_VENDOR_ID1), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - VENDOR ID1: \t0x%X\n", val );

			outl(ES_1371_CODEC_READS(AC97_VENDOR_ID2), ES_REG(ensoniq, 1371_CODEC));
			val = ES_1371_CODEC_READ(x = inl(ES_REG(ensoniq, 1371_CODEC)));
			snd_printk("AC'97 CODEC REG - VENDOR ID2: \t0x%X\n", val );
#endif

			outl(ES_1371_CODEC_READS(reg), ES_REG(ensoniq, 1371_CODEC));

			/* restore SRC reg */
			snd_es1371_wait_src_ready(ensoniq);
			outl(x, ES_REG(ensoniq, 1371_SMPRATE));
			for (t = 0; t < 0x1000; t++) {
				if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
					val = ES_1371_CODEC_READ(x);
#if 0
	snd_printd("CODEC READ: reg = 0x%x, val = 0x%x (0x%x)\n", reg, val, x);
#endif
				snd_mutex_unlock(ensoniq, reg);
				return val;
				}
			}
			snd_mutex_unlock(ensoniq, reg);
			if (++fail > 10) {
//				snd_printk("es1371: codec read timeout (final) at 0x%x, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC)));
				return 0;
			}
			goto __again;
		}
		snd_mutex_unlock(ensoniq, reg);
		snd_delay(10);
	}
//	snd_printk("es1371: codec read timeout at 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), inl(ES_REG(ensoniq, 1371_CODEC)));
	return 0;
}

static unsigned int snd_es1370_fixed_rate(unsigned int rate)
{
	int idx;

	for (idx = 0; idx < 3; idx++) {
		if (((snd_es1370_fixed_rates[idx] + snd_es1370_fixed_rates[idx + 1]) >> 1) > rate)
			return idx;
	}
	return 3;
}

static unsigned int snd_es1371_adc_rate(ensoniq_t * ensoniq,
				        unsigned int rate, int set)
{
	unsigned int n, truncm, freq, result;

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

	n = rate / 3000;
	if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9)))
		n--;
	truncm = (21 * n - 1) | 1;
	freq = ((48000UL << 15) / rate) * n;
	result = (48000UL << 15) / (freq / n);
	if (set) {
		if (rate >= 24000) {
			if (truncm > 239)
				truncm = 239;
			snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
				(((239 - truncm) >> 1) << 9) | (n << 4));
		} else {
			if (truncm > 119)
				truncm = 119;
			snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N,
			0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4));
		}
		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS,
				     (snd_es1371_src_read(ensoniq, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) |
				     ((freq >> 5) & 0xfc00));
		snd_es1371_src_write(ensoniq, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, n << 8);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, n << 8);
	}
	return result;
}

static unsigned int snd_es1371_dac1_rate(ensoniq_t * ensoniq,
					 unsigned int rate, int set)
{
	unsigned int freq, r, result;

	if (rate > 48000)
		rate = 48000;
	if (rate < 4000)
		rate = 4000;
	freq = (rate << 15) / 3000;
	result = (freq * 3000) >> 15;
	if (set) {
		r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1)) | ES_1371_DIS_P1;
		outl(r, ES_REG(ensoniq, 1371_SMPRATE));
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS,
				     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS) & 0x00ff) |
				     ((freq >> 5) & 0xfc00));
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
		r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P2 | ES_1371_DIS_R1));
		outl(r, ES_REG(ensoniq, 1371_SMPRATE));
	}
	return result;
}

static unsigned int snd_es1371_dac2_rate(ensoniq_t * ensoniq,
					 unsigned int rate, int set)
{
	unsigned int freq, r, result;

	if (rate > 48000)
		rate = 48000;
	if (rate < 4000)
		rate = 4000;
	freq = (rate << 15) / 3000;
	result = (freq * 3000) >> 15;
	if (set) {
		r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1)) | ES_1371_DIS_P2;
		outl(r, ES_REG(ensoniq, 1371_SMPRATE));
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS,
				     (snd_es1371_src_read(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS) & 0x00ff) |
				     ((freq >> 5) & 0xfc00));
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff);
		r = (snd_es1371_wait_src_ready(ensoniq) & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 | ES_1371_DIS_R1));
		outl(r, ES_REG(ensoniq, 1371_SMPRATE));
	}
	return result;
}


static void snd_ensoniq_trigger(snd_pcm1_t * pcm1, int up, unsigned int what)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	snd_mutex_lock(ensoniq, reg);
	if (up) {
		ensoniq->ctrl |= what;
	} else {
		ensoniq->ctrl &= ~what;
	}
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

/*
 *  PCM part
 */

static int snd_ensoniq_playback1_ioctl(snd_pcm1_t * pcm1,
				       unsigned int cmd, unsigned long *arg)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
		snd_mutex_lock(ensoniq, reg);
		if (!ensoniq->es1371) {
			pcm1->playback.real_rate = snd_es1370_fixed_rates[snd_es1370_fixed_rate(pcm1->playback.rate)];
		} else {
			pcm1->playback.real_rate = snd_es1371_dac1_rate(ensoniq, pcm1->playback.rate, 0);
		}
		snd_mutex_unlock(ensoniq, reg);
		return 0;
	}
	return -ENXIO;
}

static int snd_ensoniq_playback2_ioctl(snd_pcm1_t * pcm1,
				       unsigned int cmd, unsigned long *arg)
{
	unsigned int pclkdiv;
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
		snd_mutex_lock(ensoniq, reg);
		if (!ensoniq->es1371) {
			if (ensoniq->u.es1370.pclkdiv_lock)
				pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
			else
				pclkdiv = ES_1370_SRTODIV(pcm1->playback.rate);
			pcm1->playback.real_rate = ES_1370_DIVTOSR(pclkdiv);
		} else {
			pcm1->playback.real_rate = snd_es1371_dac2_rate(ensoniq, pcm1->playback.rate, 0);
		}
		snd_mutex_unlock(ensoniq, reg);
		return 0;
	}
	return -ENXIO;
}

static int snd_ensoniq_record_ioctl(snd_pcm1_t * pcm1,
				    unsigned int cmd, unsigned long *arg)
{
	unsigned int pclkdiv;
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
		snd_mutex_lock(ensoniq, reg);
		if (!ensoniq->es1371) {
			if (ensoniq->u.es1370.pclkdiv_lock)
				pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
			else
				pclkdiv = ES_1370_SRTODIV(pcm1->record.rate);
			pcm1->record.real_rate = ES_1370_DIVTOSR(pclkdiv);
		} else {
			pcm1->record.real_rate = snd_es1371_adc_rate(ensoniq, pcm1->record.rate, 0);
		}
		snd_mutex_unlock(ensoniq, reg);
		return 0;
	}
	return -ENXIO;
}

static void snd_ensoniq_playback1_trigger(snd_pcm1_t * pcm1, int up)
{
	snd_ensoniq_trigger(pcm1, up, ES_DAC1_EN);
}

static void snd_ensoniq_playback2_trigger(snd_pcm1_t * pcm1, int up)
{
	snd_ensoniq_trigger(pcm1, up, ES_DAC2_EN);
}

static void snd_ensoniq_record_trigger(snd_pcm1_t * pcm1, int up)
{
	snd_ensoniq_trigger(pcm1, up, ES_ADC_EN);
}

static void snd_ensoniq_playback1_prepare(snd_pcm1_t * pcm1,
					  unsigned char *buffer,
					  unsigned int size,
					  unsigned int offset,
					  unsigned int count)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	unsigned int mode = 0;

	if (pcm1->playback.mode & SND_PCM1_MODE_16)
		mode |= 0x02;
	if (pcm1->playback.voices > 1)
		mode |= 0x01;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~ES_DAC1_EN;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
	outl(virt_to_bus(buffer), ES_REG(ensoniq, DAC1_FRAME));
	outl((size >> 2) - 1, ES_REG(ensoniq, DAC1_SIZE));
	ensoniq->sctrl &= ~(ES_P1_LOOP_SEL | ES_P1_PAUSE | ES_P1_SCT_RLD | ES_P1_MODEM);
	ensoniq->sctrl |= ES_P1_INT_EN | ES_P1_MODEO(mode);
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	outl((count >> snd_es1370_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT));
	if (!ensoniq->es1371) {
		ensoniq->ctrl &= ~ES_1370_WTSRSELM;
		ensoniq->ctrl |= ES_1370_WTSRSEL(snd_es1370_fixed_rate(pcm1->playback.rate));
	} else {
		snd_es1371_dac1_rate(ensoniq, pcm1->playback.real_rate, 1);
	}
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static void snd_ensoniq_playback2_prepare(snd_pcm1_t * pcm1,
					  unsigned char *buffer,
					  unsigned int size,
					  unsigned int offset,
					  unsigned int count)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	unsigned int mode = 0;

	if (pcm1->playback.mode & SND_PCM1_MODE_16)
		mode |= 0x02;
	if (pcm1->playback.voices > 1)
		mode |= 0x01;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~ES_DAC2_EN;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
	outl(virt_to_bus(buffer), ES_REG(ensoniq, DAC2_FRAME));
	outl((size >> 2) - 1, ES_REG(ensoniq, DAC2_SIZE));
	ensoniq->sctrl &= ~(ES_P2_LOOP_SEL | ES_P2_PAUSE | ES_P2_DAC_SEN |
			    ES_P2_END_INCM | ES_P2_ST_INCM | ES_P2_MODEM);
	ensoniq->sctrl |= ES_P2_INT_EN | ES_P2_MODEO(mode) |
	    ES_P2_END_INCO(mode ? 2 : 1) |
	    ES_P2_ST_INCO(0);
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	outl((count >> snd_es1370_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT));
	if (!ensoniq->es1371) {
		if (ensoniq->u.es1370.pclkdiv_lock == 0 || ensoniq->u.es1370.pclkdiv_lock == ES_MODE_PLAY2) {
			ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
			ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(pcm1->playback.rate));
			ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
		}
	} else {
		snd_es1371_dac2_rate(ensoniq, pcm1->playback.real_rate, 1);
	}
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static void snd_ensoniq_record_prepare(snd_pcm1_t * pcm1,
				       unsigned char *buffer,
				       unsigned int size,
				       unsigned int offset,
				       unsigned int count)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	unsigned int mode = 0;

	if (pcm1->record.mode & SND_PCM1_MODE_16)
		mode |= 0x02;
	if (pcm1->record.voices > 1)
		mode |= 0x01;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~ES_ADC_EN;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
	outl(virt_to_bus(buffer), ES_REG(ensoniq, ADC_FRAME));
	outl((size >> 2) - 1, ES_REG(ensoniq, ADC_SIZE));
	ensoniq->sctrl &= ~(ES_R1_LOOP_SEL | ES_R1_MODEM);
	ensoniq->sctrl |= ES_R1_INT_EN | ES_R1_MODEO(mode);
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	outl((count >> snd_es1370_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT));
	if (!ensoniq->es1371) {
		if (ensoniq->u.es1370.pclkdiv_lock == 0 || ensoniq->u.es1370.pclkdiv_lock == ES_MODE_RECORD) {
			ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
			ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(pcm1->record.rate));
			ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_RECORD;
		}
	} else {
		snd_es1371_adc_rate(ensoniq, pcm1->record.real_rate, 1);
	}
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static int snd_ensoniq_playback1_open(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	int err;

	if (ensoniq->mode & ES_MODE_PLAY1)
		return -EBUSY;
	ensoniq->mode |= ES_MODE_PLAY1;
	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_PLAYBACK, ensoniq->dma1ptr, "Ensoniq AudioPCI - DAC1")) < 0) {
		ensoniq->mode &= ~ES_MODE_PLAY1;
		return err;
	}
	return 0;
}

static int snd_ensoniq_playback2_open(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	int err;

	if (ensoniq->mode & ES_MODE_PLAY2)
		return -EBUSY;
	ensoniq->mode |= ES_MODE_PLAY2;
	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_PLAYBACK, ensoniq->dma2ptr, "Ensoniq AudioPCI - DAC2")) < 0) {
		ensoniq->mode &= ES_MODE_PLAY2;
		return err;
	}
	return 0;
}

static int snd_ensoniq_record_open(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;
	int err;

	if ( ensoniq->mode & ES_MODE_RECORD )
		return -EBUSY;
	ensoniq->mode |= ES_MODE_RECORD;
	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_RECORD, ensoniq->dma3ptr, "Ensoniq AudioPCI - ADC")) < 0) {
		ensoniq->mode &= ~ES_MODE_RECORD;
		return err;
	}
	return 0;
}

static void snd_ensoniq_playback1_close(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	snd_pcm1_dma_free(pcm1, SND_PCM1_PLAYBACK, ensoniq->dma1ptr);
	atomic_clr(&ensoniq->mode, ES_MODE_PLAY1 );
}

static void snd_ensoniq_playback2_close(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	snd_pcm1_dma_free(pcm1, SND_PCM1_PLAYBACK, ensoniq->dma2ptr);
	if (!ensoniq->es1371)
		ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
	atomic_clr(&ensoniq->mode, ES_MODE_PLAY2);
}

static void snd_ensoniq_record_close(snd_pcm1_t * pcm1)
{
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	snd_pcm1_dma_free(pcm1, SND_PCM1_RECORD, ensoniq->dma3ptr);
	if (!ensoniq->es1371)
		ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_RECORD;
	atomic_clr(&ensoniq->mode, ES_MODE_RECORD);
}

static unsigned int snd_ensoniq_playback1_pointer(snd_pcm1_t * pcm1,
						  unsigned int used_size)
{
	unsigned int ptr;
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	ptr = ES_REG_CURR_COUNT(inl(ES_REG(ensoniq, DAC1_COUNT))) + 1;
	ptr <<= snd_es1370_sample_shift[ES_P1_MODEI(ensoniq->sctrl)];
	return used_size - ptr;
}

static unsigned int snd_ensoniq_playback2_pointer(snd_pcm1_t * pcm1,
						  unsigned int used_size)
{
	unsigned int ptr;
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	ptr = ES_REG_CURR_COUNT(inl(ES_REG(ensoniq, DAC2_COUNT))) + 1;
	ptr <<= snd_es1370_sample_shift[ES_P2_MODEI(ensoniq->sctrl)];
	return used_size - ptr;
}

static unsigned int snd_ensoniq_record_pointer(snd_pcm1_t * pcm1,
					       unsigned int used_size)
{
	unsigned int ptr;
	ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

	ptr = ES_REG_CURR_COUNT(inl(ES_REG(ensoniq, ADC_COUNT))) + 1;
	ptr <<= snd_es1370_sample_shift[ES_R1_MODEI(ensoniq->sctrl)];
	return used_size - ptr;
}

static struct snd_stru_pcm1_hardware snd_ensoniq_playback1 =
{
	NULL,			/* private data */
	NULL,			/* private_free */
	SND_PCM1_HW_AUTODMA | SND_PCM1_HW_BLOCKPTR,	/* flags */
	SND_PCM_FMT_MU_LAW | SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* formats */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	31,			/* align value */
	32,			/* minimal fragment */
	4000,			/* min. rate */
	48000,			/* max. rate */
	2,			/* max. voices */
	snd_ensoniq_playback1_open,
	snd_ensoniq_playback1_close,
	snd_ensoniq_playback1_ioctl,
	snd_ensoniq_playback1_prepare,
	snd_ensoniq_playback1_trigger,
	snd_ensoniq_playback1_pointer,
	snd_pcm1_playback_dma_ulaw_loud,
	snd_pcm1_dma_move,
	snd_pcm1_playback_dma_neutral
};

static struct snd_stru_pcm1_hardware snd_ensoniq_playback2 =
{
	NULL,			/* private data */
	NULL,			/* private_free */
	SND_PCM1_HW_AUTODMA | SND_PCM1_HW_BLOCKPTR,	/* flags */
	SND_PCM_FMT_MU_LAW | SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* formats */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	31,			/* align value */
	32,			/* minimal fragment */
	4000,			/* min. rate */
	48000,			/* max. rate */
	2,			/* max. voices */
	snd_ensoniq_playback2_open,
	snd_ensoniq_playback2_close,
	snd_ensoniq_playback2_ioctl,
	snd_ensoniq_playback2_prepare,
	snd_ensoniq_playback2_trigger,
	snd_ensoniq_playback2_pointer,
	snd_pcm1_playback_dma_ulaw_loud,
	snd_pcm1_dma_move,
	snd_pcm1_playback_dma_neutral
};

static struct snd_stru_pcm1_hardware snd_ensoniq_record =
{
	NULL,			/* private data */
	NULL,			/* private_free */
	SND_PCM1_HW_AUTODMA | SND_PCM1_HW_BLOCKPTR,	/* flags */
	SND_PCM_FMT_MU_LAW | SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* formats */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	31,			/* align value */
	32,			/* minimal fragment */
	4000,			/* min. rate */
	48000,			/* max. rate */
	2,			/* max. voices */
	snd_ensoniq_record_open,
	snd_ensoniq_record_close,
	snd_ensoniq_record_ioctl,
	snd_ensoniq_record_prepare,
	snd_ensoniq_record_trigger,
	snd_ensoniq_record_pointer,
	snd_pcm1_record_dma_ulaw,
	snd_pcm1_dma_move,
	NULL
};

static void snd_ensoniq_pcm_free(void *private_data)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	ensoniq->pcm = NULL;
}

snd_pcm_t *snd_ensoniq_pcm(ensoniq_t * ensoniq)
{
	snd_pcm_t *pcm;
	snd_pcm1_t *pcm1;

	pcm = snd_pcm1_new_device(ensoniq->card, "ES1370/1");
	if (!pcm)
		return NULL;
	pcm1 = (snd_pcm1_t *) pcm->private_data;
	memcpy(&pcm1->playback.hw, &snd_ensoniq_playback2, sizeof(snd_ensoniq_playback2));
	memcpy(&pcm1->record.hw, &snd_ensoniq_record, sizeof(snd_ensoniq_record));
	if (!ensoniq->es1371)
		pcm1->playback.hw.max_rate = 44100;
	pcm1->private_data = ensoniq;
	pcm1->private_free = snd_ensoniq_pcm_free;
	pcm->info_flags = SND_PCM_INFO_CODEC | SND_PCM_INFO_MMAP | SND_PCM_INFO_TRIGGER |
	    SND_PCM_INFO_PLAYBACK | SND_PCM_INFO_RECORD | SND_PCM_INFO_DUPLEX;
	strcpy(pcm->name, ensoniq->es1371 ? "ES1371 DAC2/ADC" : "ES1370 DAC2/ADC");
	return ensoniq->pcm = pcm;
}

static void snd_ensoniq_pcm_free2(void *private_data)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	ensoniq->pcm2 = NULL;
}

snd_pcm_t *snd_ensoniq_pcm2(ensoniq_t * ensoniq)
{
	snd_pcm_t *pcm;
	snd_pcm1_t *pcm1;

	pcm = snd_pcm1_new_device(ensoniq->card, "ES1370/2");
	if (!pcm)
		return NULL;
	pcm1 = (snd_pcm1_t *) pcm->private_data;
	memcpy(&pcm1->playback.hw, &snd_ensoniq_playback1, sizeof(snd_ensoniq_playback1));
	if (!ensoniq->es1371)
		pcm1->playback.hw.max_rate = 44100;
	pcm1->private_data = ensoniq;
	pcm1->private_free = snd_ensoniq_pcm_free2;
	pcm->info_flags = SND_PCM_INFO_CODEC | SND_PCM_INFO_MMAP |
	    SND_PCM_INFO_PLAYBACK;
	strcpy(pcm->name, ensoniq->es1371 ? "ES1371 DAC1" : "ES1370 DAC1");
	return ensoniq->pcm2 = pcm;
}

/*
 *  Mixer section
 */
static int snd_es1370_get_switch(snd_kmixer_t * mixer,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;
	unsigned int mask;

	mask = kswitch->private_value ? ES_1370_XCTL0 : ES_1370_XCTL1;
	uswitch->type = SND_SW_TYPE_BOOLEAN;
	snd_mutex_lock(ensoniq, reg);
	uswitch->value.enable = ensoniq->ctrl & mask ? 1 : 0;
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_es1370_set_switch(snd_kmixer_t * mixer,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;
	unsigned int mask;

	mask = kswitch->private_value ? ES_1370_XCTL0 : ES_1370_XCTL1;
	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~mask;
	if (uswitch->value.enable)
		ensoniq->ctrl |= mask;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static snd_kswitch_t snd_es1370_mixer_switch1 =
{
	SND_MIXER_SW_MIC_IMPEDANCE,
	(snd_get_switch_t *)snd_es1370_get_switch,
	(snd_set_switch_t *)snd_es1370_set_switch,
	0,
	NULL,
	NULL
};

static snd_kswitch_t snd_es1370_mixer_switch2 =
{
	SND_MIXER_SW_LINE_TO_OUTPUT,
	(snd_get_switch_t *)snd_es1370_get_switch,
	(snd_set_switch_t *)snd_es1370_set_switch,
	1,
	NULL,
	NULL
};

static void snd_ensoniq_mixer_free_ac97(ac97_t * ac97)
{
	((ensoniq_t *) ac97->private_data)->mixer = NULL;
	snd_free(ac97, sizeof(ac97_t));
}

static void snd_ensoniq_mixer_free_ak4531(ak4531_t * ak4531)
{
	((ensoniq_t *) ak4531->private_data)->mixer = NULL;
	snd_free(ak4531, sizeof(ak4531_t));
}

snd_kmixer_t *snd_ensoniq_mixer(ensoniq_t * ensoniq)
{
	ac97_t *ac97;
	ak4531_t *ak4531;
	snd_kmixer_t *mixer;

	if (ensoniq->es1371) {
		ac97 = snd_calloc(sizeof(ac97_t));
		if (!ac97)
			return NULL;
		ac97->write = snd_es1371_codec_write;
		ac97->read = snd_es1371_codec_read;
		ac97->private_data = ensoniq;
		ac97->private_free = snd_ensoniq_mixer_free_ac97;
		mixer = snd_ac97_mixer(ensoniq->card, ac97);
		if (!mixer) {
			snd_free(ac97, sizeof(ac97));
			return NULL;
		}
	} else {
		/* try reset AK4531 */
		outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
		snd_delay(10);
		outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
		snd_delay(10);

		ak4531 = snd_calloc(sizeof(ak4531_t));
		if (!ak4531)
			return NULL;
		ak4531->write = snd_es1370_codec_write;
		ak4531->private_data = ensoniq;
		ak4531->private_free = snd_ensoniq_mixer_free_ak4531;
		mixer = snd_ak4531_mixer(ensoniq->card, ak4531);
		if (!mixer) {
			snd_free(ak4531, sizeof(ak4531));
			return NULL;
		}
		snd_mixer_switch_new(mixer, &snd_es1370_mixer_switch1, ensoniq);
		snd_mixer_switch_new(mixer, &snd_es1370_mixer_switch2, ensoniq);
	}
	return ensoniq->mixer = mixer;
}

/*
 *  General Switches...
 */

static int snd_ensoniq_get_ctl_switch(snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t *uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	snd_mutex_lock(ensoniq, reg);
	uswitch->value.enable = ensoniq->ctrl & ES_JYSTK_EN ? 1 : 0;
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_ensoniq_set_ctl_switch(snd_card_t * card,
				      snd_kswitch_t * kswitch,
				      snd_switch_t *uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;

	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~ES_JYSTK_EN;
	if (uswitch->value.enable)
		ensoniq->ctrl |= ES_JYSTK_EN;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_es1371_get_ctl_switch(snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t *uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;

	uswitch->type = SND_SW_TYPE_WORD;
	snd_mutex_lock(ensoniq, reg);
	uswitch->low = 0x200;
	uswitch->high = 0x218;
	uswitch->value.data16[0] = (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200;
	uswitch->value.data16[1] = 0x200;
	uswitch->value.data16[2] = 0x208;
	uswitch->value.data16[3] = 0x210;
	uswitch->value.data16[4] = 0x218;
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_es1371_set_ctl_switch(snd_card_t * card,
				     snd_kswitch_t * kswitch,
				     snd_switch_t *uswitch)
{
	ensoniq_t *ensoniq = (ensoniq_t *) kswitch->private_data;
	unsigned short port;

	if (uswitch->type != SND_SW_TYPE_WORD)
		return -EINVAL;
	port = uswitch->value.data16[0];
	if (port < 0x200 || port > 0x218)
		return -EINVAL;
	snd_mutex_lock(ensoniq, reg);
	ensoniq->ctrl &= ~ES_1371_JOY_ASELM;
	if (port & 0x0008)
		ensoniq->ctrl |= ES_1371_JOY_ASEL(1);
	if (port & 0x0010)
		ensoniq->ctrl |= ES_1371_JOY_ASEL(2);
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static snd_kswitch_t snd_ensoniq_ctl_switch =
{
	SND_CTL_SW_JOYSTICK,
	(snd_get_switch_t *)snd_ensoniq_get_ctl_switch,
	(snd_set_switch_t *)snd_ensoniq_set_ctl_switch,
	0,
	NULL,
	NULL
};

static snd_kswitch_t snd_es1371_ctl_switch =
{
	SND_CTL_SW_JOYSTICK_ADDRESS,
	(snd_get_switch_t *)snd_es1371_get_ctl_switch,
	(snd_set_switch_t *)snd_es1371_set_ctl_switch,
	0,
	NULL,
	NULL
};

/*

 */
static void snd_ensoniq_proc_read(snd_info_buffer_t * buffer,
				  void *private_data)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;

	snd_iprintf(buffer, "Ensoniq AudioPCI %s\n\n", ensoniq->es1371 ? "ES1371" : "ES1370");
	snd_iprintf(buffer, "Joystick enable  : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
	if (!ensoniq->es1371) {
		snd_iprintf(buffer, "MIC impedance    : %s\n", ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
		snd_iprintf(buffer, "Line In to AOUT  : %s\n", ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
	} else {
		snd_iprintf(buffer, "Joystick port    : %s\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
	}
}

static void snd_ensoniq_proc_init(ensoniq_t * ensoniq)
{
	snd_info_entry_t *entry;

	if ((entry = snd_info_create_entry(ensoniq->card, "audiopci")) != NULL) {
		entry->private_data = ensoniq;
		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
		entry->t.text.read_size = 256;
		entry->t.text.read = snd_ensoniq_proc_read;
		if (snd_info_register(entry) < 0) {
			snd_info_free_entry(entry);
			entry = NULL;
		}
	}
	ensoniq->proc_entry = entry;
}

static void snd_ensoniq_proc_done(ensoniq_t * ensoniq)
{
	if (ensoniq->proc_entry) {
		snd_info_unregister(ensoniq->proc_entry);
		ensoniq->proc_entry = NULL;
	}
}
/*

 */

ensoniq_t *snd_ensoniq_create(snd_card_t * card,
			      struct snd_pci_dev *pci,
			      snd_dma_t *dma1ptr,
			      snd_dma_t *dma2ptr,
			      snd_dma_t *dma3ptr,
			      snd_irq_t *irqptr)
{
	int idx;
	ensoniq_t *ensoniq;
	unsigned short cmd;
	unsigned char revision;
	unsigned char ltimer;
	

	ensoniq = (ensoniq_t *) snd_calloc(sizeof(ensoniq_t));
	if (!ensoniq)
		return NULL;
	ensoniq->es1371 = pci->device == PCI_DEVICE_ID_ENSONIQ_ES1371;
	snd_mutex_prepare(ensoniq, reg);
	ensoniq->card = card;
	ensoniq->pci = pci;
	ensoniq->dma1ptr = dma1ptr;
	ensoniq->dma2ptr = dma2ptr;
	ensoniq->dma3ptr = dma3ptr;
	ensoniq->irqptr = irqptr;
	ensoniq->port = PCI_IO_ADDR(pci->base_address[0]);

	snd_pci_read_config_word(pci, PCI_COMMAND, &cmd);
	if ((cmd & (PCI_COMMAND_IO|PCI_COMMAND_MASTER)) != (PCI_COMMAND_IO|PCI_COMMAND_MASTER)) {
		cmd |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
		snd_pci_write_config_word(pci, PCI_COMMAND, cmd);
	}

	snd_pci_read_config_byte(pci, PCI_REVISION_ID, &revision);

	snd_pci_read_config_byte(pci, PCI_LATENCY_TIMER, &ltimer);
	if (ltimer < 32)
		ltimer = 32;
	snd_pci_write_config_byte(pci, PCI_LATENCY_TIMER, ltimer);

	snd_ensoniq_proc_init(ensoniq);
	if (!ensoniq->es1371) {
		ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000)) | ES_1370_WTSRSEL(snd_es1370_fixed_rate(22050));
		ensoniq->sctrl = 0;
		/* initialize the chips */
		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	} else {
		ensoniq->ctrl = 0;
		ensoniq->sctrl = 0;
		/* initialize the chips */
		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
		
		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
		outl(0, ES_REG(ensoniq, 1371_LEGACY));
		/* AC'97 warm reset to start the bitclk */
		if(( revision == 7 ) || ( revision >= 9 )) {
			outl( ES_1371_AC97_WARM, ES_REG(ensoniq, STATUS));
			snd_delay(20);
		} else {
			outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
			snd_delay(2);
			outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
		}
		
		/* Init the sample rate converter */
		outl(ES_1371_SRC_DISABLE, ES_REG(ensoniq, 1371_SMPRATE));
		for (idx = 0; idx < 0x80; idx++)
			snd_es1371_src_write(ensoniq, idx, 0);
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4);
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10);
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4);
		snd_es1371_src_write(ensoniq, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC, 1 << 12);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_ADC + 1, 1 << 12);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1, 1 << 12);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC1 + 1, 1 << 12);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2, 1 << 12);
		snd_es1371_src_write(ensoniq, ES_SMPREG_VOL_DAC2 + 1, 1 << 12);
		snd_es1371_adc_rate(ensoniq, 22050, 1);
		snd_es1371_dac1_rate(ensoniq, 22050, 1);
		snd_es1371_dac2_rate(ensoniq, 22050, 1);
		/* WARNING:
		 * enabling the sample rate converter without properly programming
		 * its parameters causes the chip to lock up (the SRC busy bit will
		 * be stuck high, and I've found no way to rectify this other than
		 * power cycle) - Thomas Sailer
		 */
		outl(0, ES_REG(ensoniq, 1371_SMPRATE));
		/* try reset codec directly */
		outl(ES_1371_CODEC_WRITE(0, 0), ES_REG(ensoniq, 1371_CODEC));
	}
	outb(ensoniq->uartc = 0x00, ES_REG(ensoniq, UART_CONTROL));
	outb(0x00, ES_REG(ensoniq, UART_RES));
	snd_control_switch_new(card, &snd_ensoniq_ctl_switch, ensoniq);
	if (ensoniq->es1371)
		snd_control_switch_new(card, &snd_es1371_ctl_switch, ensoniq);
	return ensoniq;
}

void snd_ensoniq_free(ensoniq_t * ensoniq)
{
	snd_ensoniq_proc_done(ensoniq);
	if (!ensoniq->es1371) {
		outl(ES_1370_SERR_DISABLE, ES_REG(ensoniq, CONTROL));	/* switch everything off */
		outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
	} else {
		outl(0, ES_REG(ensoniq, CONTROL));	/* switch everything off */
		outl(0, ES_REG(ensoniq, SERIAL));	/* clear serial interface */
	}
	snd_free(ensoniq, sizeof(ensoniq_t));
}

/*
 *  MIDI section
 */

static void snd_ensoniq_midi_interrupt(ensoniq_t * ensoniq,
				       snd_rawmidi_t * rmidi)
{
	unsigned char status, mask = 0, byte;

	snd_mutex_lock(ensoniq, reg);
	if (ensoniq->uartm & ES_MODE_INPUT)
		mask |= ES_RXRDY;
	if (ensoniq->uartm & ES_MODE_OUTPUT)
		mask |= ES_TXRDY;
	while ((status = inb(ES_REG(ensoniq, UART_STATUS))) & mask) {
		if (status & ES_RXRDY) {
			byte = inb(ES_REG(ensoniq, UART_DATA));
			if (mask & ES_RXRDY)
				rmidi->input.data(rmidi, &byte, 1);
		} else if (status & ES_TXRDY) {
			if (mask & ES_TXRDY) {
				snd_mutex_unlock(ensoniq, reg);
				if (rmidi->output.data(rmidi, &byte, 1) != 1) {
					snd_mutex_lock(ensoniq, reg);
					ensoniq->uartc &= ~ES_TXINTENM;
					mask &= ~ES_TXRDY;
				} else {
					snd_mutex_lock(ensoniq, reg);
					outb(byte, ES_REG(ensoniq, UART_DATA));
				}
			} else {
				ensoniq->uartc &= ~ES_TXINTENM;
			}
		}
	}
	outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static int snd_ensoniq_midi_input_open(snd_rawmidi_t * rmidi)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;

	snd_mutex_lock(ensoniq, reg);
	ensoniq->uartm |= ES_MODE_INPUT;
	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
	}
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_ensoniq_midi_input_close(snd_rawmidi_t * rmidi)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;

	snd_mutex_lock(ensoniq, reg);
	if (!(ensoniq->uartm & ES_MODE_OUTPUT)) {
		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
	} else {
		outb(ensoniq->uartc &= ~ES_RXINTEN, ES_REG(ensoniq, UART_CONTROL));
	}
	ensoniq->uartm &= ~ES_MODE_INPUT;
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_ensoniq_midi_output_open(snd_rawmidi_t * rmidi)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;

	snd_mutex_lock(ensoniq, reg);
	ensoniq->uartm |= ES_MODE_OUTPUT;
	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
		outb(ES_CNTRL(3), ES_REG(ensoniq, UART_CONTROL));
		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
		outl(ensoniq->ctrl |= ES_UART_EN, ES_REG(ensoniq, CONTROL));
	}
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static int snd_ensoniq_midi_output_close(snd_rawmidi_t * rmidi)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;

	snd_mutex_lock(ensoniq, reg);
	if (!(ensoniq->uartm & ES_MODE_INPUT)) {
		outb(ensoniq->uartc = 0, ES_REG(ensoniq, UART_CONTROL));
		outl(ensoniq->ctrl &= ~ES_UART_EN, ES_REG(ensoniq, CONTROL));
	} else {
		outb(ensoniq->uartc &= ~ES_TXINTENM, ES_REG(ensoniq, UART_CONTROL));
	}
	ensoniq->uartm &= ~ES_MODE_OUTPUT;
	snd_mutex_unlock(ensoniq, reg);
	return 0;
}

static void snd_ensoniq_midi_input_trigger(snd_rawmidi_t * rmidi, int up)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;

	snd_mutex_lock(ensoniq, reg);
	if (up)
		ensoniq->uartc |= ES_RXINTEN;
	else
		ensoniq->uartc &= ~ES_RXINTEN;
	outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static void snd_ensoniq_midi_output_trigger(snd_rawmidi_t * rmidi, int up)
{
	ensoniq_t *ensoniq = (ensoniq_t *) rmidi->private_data;
	unsigned char byte;

 	snd_mutex_lock(ensoniq, reg);
	if (up) {
		ensoniq->uartc |= ES_TXINTENO(1);
		/* fill UART FIFO buffer at first, and turn Tx interrupts only if necessary */
		while (ES_TXINTENI(ensoniq->uartc) == 1 &&
		       (inb(ES_REG(ensoniq, UART_STATUS)) & ES_TXRDY)) {
			snd_mutex_unlock(ensoniq, reg);
			if (rmidi->output.data(rmidi, &byte, 1) != 1) {
				snd_mutex_lock(ensoniq, reg);
				ensoniq->uartc &= ~ES_TXINTENM;
			} else {
				snd_mutex_lock(ensoniq, reg);
				outb(byte, ES_REG(ensoniq, UART_DATA));
			}
		}
	} else {
		ensoniq->uartc &= ~ES_TXINTENM;
	}
	outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
	snd_mutex_unlock(ensoniq, reg);
}

static struct snd_stru_rawmidi_direction_hw snd_ensoniq_midi_output =
{
	0,			/* flags */
	NULL,			/* private_data */
	NULL,			/* private_free */
	snd_ensoniq_midi_output_open,	/* open */
	snd_ensoniq_midi_output_close,	/* close */
	snd_ensoniq_midi_output_trigger,/* trigger */
	{NULL},			/* io.write */
	NULL,			/* abort */
};

static struct snd_stru_rawmidi_direction_hw snd_ensoniq_midi_input =
{
	0,			/* flags */
	NULL,			/* private_data */
	NULL,			/* private_free */
	snd_ensoniq_midi_input_open,	/* open */
	snd_ensoniq_midi_input_close,	/* close */
	snd_ensoniq_midi_input_trigger,	/* trigger */
	{NULL},			/* io.read */
	NULL,			/* abort */
};

snd_rawmidi_t *snd_ensoniq_midi(ensoniq_t * ensoniq)
{
	snd_rawmidi_t *rmidi;

	if ((rmidi = snd_rawmidi_new_device(ensoniq->card, "ES1370/1")) == NULL)
		return NULL;
	strcpy(rmidi->name, ensoniq->es1371 ? "ES1371" : "ES1370");
	memcpy(&rmidi->output.hw, &snd_ensoniq_midi_output, sizeof(snd_ensoniq_midi_output));
	memcpy(&rmidi->input.hw, &snd_ensoniq_midi_input, sizeof(snd_ensoniq_midi_input));
	rmidi->info_flags |= SND_RAWMIDI_INFO_OUTPUT | SND_RAWMIDI_INFO_INPUT | SND_RAWMIDI_INFO_DUPLEX;
	rmidi->private_data = ensoniq;
	ensoniq->rmidi = rmidi;
	return rmidi;
}

/*
 *  Interrupt handler
 */

void snd_ensoniq_interrupt(ensoniq_t * ensoniq)
{
	snd_pcm1_t *pcm1;
	unsigned int status, sctrl;

	status = inl(ES_REG(ensoniq, STATUS));
	if (!(status & ES_INTR))
		return;

	snd_mutex_lock(ensoniq, reg);
	sctrl = ensoniq->sctrl;
	if (status & ES_DAC1)
		sctrl &= ~ES_P1_INT_EN;
	if (status & ES_DAC2)
		sctrl &= ~ES_P2_INT_EN;
	if (status & ES_ADC)
		sctrl &= ~ES_R1_INT_EN;
	outl(sctrl, ES_REG(ensoniq, SERIAL));
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	snd_mutex_unlock(ensoniq, reg);

	if (ensoniq->pcm) {
		if (status & ES_DAC2) {
			pcm1 = (snd_pcm1_t *) ensoniq->pcm->private_data;
			if (pcm1 && pcm1->playback.ack)
				pcm1->playback.ack(pcm1);
		}
		if (status & ES_ADC) {
			pcm1 = (snd_pcm1_t *) ensoniq->pcm->private_data;
			if (pcm1 && pcm1->record.ack)
				pcm1->record.ack(pcm1);
		}
	}
	if (ensoniq->pcm2) {
		if (status & ES_DAC1) {
			pcm1 = (snd_pcm1_t *) ensoniq->pcm2->private_data;
			if (pcm1 && pcm1->playback.ack)
				pcm1->playback.ack(pcm1);
		}
	}
	if (ensoniq->rmidi) {
		if (status & ES_UART)
			snd_ensoniq_midi_interrupt(ensoniq, ensoniq->rmidi);
	}
}

