/*
 *  Copyright (c) by Jaroslav Kysela <perex@suse.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.
 *
 */

#define SND_MAIN_OBJECT_FILE
#include "driver.h"
#include "control.h"
#include "info.h"
#include "audiopci.h"

#ifdef CHIP1370
#define snd_ensoniq_free snd_ensoniq_1370_free
#define snd_ensoniq_create snd_ensoniq_1370_create
#define snd_ensoniq_pcm snd_ensoniq_1370_pcm
#define snd_ensoniq_pcm2 snd_ensoniq_1370_pcm2
#define snd_ensoniq_midi snd_ensoniq_1370_midi
#define snd_ensoniq_interrupt snd_ensoniq_1370_interrupt
#endif
#ifdef CHIP1371
#define snd_ensoniq_free snd_ensoniq_1371_free
#define snd_ensoniq_create snd_ensoniq_1371_create
#define snd_ensoniq_pcm snd_ensoniq_1371_pcm
#define snd_ensoniq_pcm2 snd_ensoniq_1371_pcm2
#define snd_ensoniq_midi snd_ensoniq_1371_midi
#define snd_ensoniq_interrupt snd_ensoniq_1371_interrupt
#endif

/*
 *  constants
 */

#ifdef CHIP1370
static const unsigned int snd_es1370_fixed_rates[] =
	{5512, 11025, 22050, 44100};
#endif
static const unsigned int snd_ensoniq_sample_shift[] =
	{0, 1, 1, 2};

/*
 *  common I/O routines
 */

#ifdef CHIP1371

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));
}

#endif /* CHIP1371 */

#ifdef CHIP1370

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

#if 0
	printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x)\n", reg, val, ES_1370_CODEC_WRITE(reg, val));
#endif
	do {
		snd_spin_lock(ensoniq, reg, &flags);
		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
			snd_spin_unlock(ensoniq, reg, &flags);
			return;
		}
		snd_spin_unlock(ensoniq, reg, &flags);
		snd_schedule(ensoniq, codec, 1);
		time -= snd_timeout(ensoniq, codec) ? 1 : 0;
	} while (time > 0);
	snd_printk("es1370: codec write timeout\n");
}

#endif /* CHIP1370 */

#ifdef CHIP1371

static void snd_es1371_codec_write(void *private_data,
				   unsigned short reg, unsigned short val)
{
	ensoniq_t *ensoniq = (ensoniq_t *) private_data;
	unsigned long flags;
	unsigned int t, x;

	for (t = 0; t < 0x1000; t++) {
		snd_spin_lock(ensoniq, reg, &flags);
		if (!(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_spin_unlock(ensoniq, reg, &flags);
			return;
		}
		snd_spin_unlock(ensoniq, reg, &flags);
	}
	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 long flags;
	unsigned int t, x, fail = 0;

      __again:
	for (t = 0; t < 0x1000; t++) {
		snd_spin_lock(ensoniq, reg, &flags);
		if (!(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_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) {
					snd_spin_unlock(ensoniq, reg, &flags);
					return ES_1371_CODEC_READ(x);
				}
			}
			snd_spin_unlock(ensoniq, reg, &flags);
			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_spin_unlock(ensoniq, reg, &flags);
	}
	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;
}

#endif /* CHIP1371 */

#ifdef CHIP1370

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;
}

#endif /* CHIP1370 */

#ifdef CHIP1371

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;
}

#endif /* CHIP1371 */

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

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

/*
 *  PCM part
 */

static int snd_ensoniq_playback1_ioctl(snd_pcm1_t * pcm1,
				       unsigned int cmd, unsigned long *arg)
{

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
#ifdef CHIP1370 
		pcm1->playback.real_rate = snd_es1370_fixed_rates[snd_es1370_fixed_rate(pcm1->playback.rate)];
#else
		{
			ensoniq_t *ensoniq = (ensoniq_t *) pcm1->private_data;

			pcm1->playback.real_rate = snd_es1371_dac1_rate(ensoniq, pcm1->playback.rate, 0);
		}
#endif
		return 0;
	}
	return -ENXIO;
}

static int snd_ensoniq_playback2_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:
#ifdef CHIP1370
		{
			unsigned long flags;
			unsigned int pclkdiv;
		
			snd_spin_lock(ensoniq, reg, &flags);
			if (ensoniq->u.es1370.pclkdiv_lock)
				pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
			else
				pclkdiv = ES_1370_SRTODIV(pcm1->playback.rate);
			snd_spin_unlock(ensoniq, reg, &flags);
			pcm1->playback.real_rate = ES_1370_DIVTOSR(pclkdiv);
		}
#else
		pcm1->playback.real_rate = snd_es1371_dac2_rate(ensoniq, pcm1->playback.rate, 0);
#endif
		return 0;
	}
	return -ENXIO;
}

static int snd_ensoniq_record_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:
#ifdef CHIP1370
		{
			unsigned long flags;
			unsigned int pclkdiv;

			snd_spin_lock(ensoniq, reg, &flags);
			if (ensoniq->u.es1370.pclkdiv_lock)
				pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
			else
				pclkdiv = ES_1370_SRTODIV(pcm1->record.rate);
			snd_spin_unlock(ensoniq, reg, &flags);
			pcm1->record.real_rate = ES_1370_DIVTOSR(pclkdiv);
		}
#else
		pcm1->record.real_rate = snd_es1371_adc_rate(ensoniq, pcm1->record.rate, 0);
#endif
		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)
{
	unsigned long flags;
	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_spin_lock(ensoniq, reg, &flags);
	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_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC1_COUNT));
#ifdef CHIP1370
	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);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
}

static void snd_ensoniq_playback2_prepare(snd_pcm1_t * pcm1,
					  unsigned char *buffer,
					  unsigned int size,
					  unsigned int offset,
					  unsigned int count)
{
	unsigned long flags;
	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_spin_lock(ensoniq, reg, &flags);
	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_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, DAC2_COUNT));
#ifdef CHIP1370
	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);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
}

static void snd_ensoniq_record_prepare(snd_pcm1_t * pcm1,
				       unsigned char *buffer,
				       unsigned int size,
				       unsigned int offset,
				       unsigned int count)
{
	unsigned long flags;
	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_spin_lock(ensoniq, reg, &flags);
	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_ensoniq_sample_shift[mode]) - 1, ES_REG(ensoniq, ADC_COUNT));
#ifdef CHIP1370
	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);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
}

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

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

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

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

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

	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_RECORD, ensoniq->dma3ptr, "Ensoniq AudioPCI - ADC")) < 0)
		return err;
	ensoniq->mode |= ES_MODE_RECORD;
	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);
	ensoniq->mode &= ~ES_MODE_PLAY1;
}

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

	snd_pcm1_dma_free(pcm1, SND_PCM1_PLAYBACK, ensoniq->dma2ptr);
	snd_spin_lock(ensoniq, reg, &flags);
#ifdef CHIP1370
	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
#endif
	ensoniq->mode &= ~ES_MODE_PLAY2;
	snd_spin_unlock(ensoniq, reg, &flags);
}

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

	snd_pcm1_dma_free(pcm1, SND_PCM1_RECORD, ensoniq->dma3ptr);
	snd_spin_lock(ensoniq, reg, &flags);
#ifdef CHIP1370
	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_RECORD;
#endif
	ensoniq->mode &= ~ES_MODE_RECORD;
	snd_spin_unlock(ensoniq, reg, &flags);
}

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_ensoniq_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_ensoniq_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_ensoniq_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 */
	3,			/* align value */
	6,			/* 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 */
	3,			/* align value */
	6,			/* 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 */
	3,			/* align value */
	6,			/* 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;

#ifdef CHIP1370
	pcm = snd_pcm1_new_device(ensoniq->card, "ES1370/1");
#else
	pcm = snd_pcm1_new_device(ensoniq->card, "ES1371/1");
#endif
	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));
	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_PLAYBACK | SND_PCM_INFO_RECORD | SND_PCM_INFO_DUPLEX;
#ifdef CHIP1370
	strcpy(pcm->name, "ES1370 DAC2/ADC");
#else
	strcpy(pcm->name, "ES1371 DAC2/ADC");
#endif
	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;

#ifdef CHIP1370
	pcm = snd_pcm1_new_device(ensoniq->card, "ES1370/2");
#else
	pcm = snd_pcm1_new_device(ensoniq->card, "ES1371/2");
#endif
	if (!pcm)
		return NULL;
	pcm1 = (snd_pcm1_t *) pcm->private_data;
	memcpy(&pcm1->playback.hw, &snd_ensoniq_playback1, sizeof(snd_ensoniq_playback1));
	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;
#ifdef CHIP1370
	strcpy(pcm->name, "ES1370 DAC1");
#else
	strcpy(pcm->name, "ES1371 DAC1");
#endif
	return ensoniq->pcm2 = pcm;
}

/*
 *  Mixer section
 */

#ifdef CHIP1371

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

snd_kmixer_t *snd_ensoniq_1371_mixer(ensoniq_t * ensoniq, int pcm_count, int *pcm_devs)
{
	ac97_t *ac97;
	snd_kmixer_t *mixer;

	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, pcm_count, pcm_devs);
	if (!mixer) {
		snd_free(ac97, sizeof(ac97));
		return NULL;
	}
	return ensoniq->mixer = mixer;
}

#endif /* CHIP1371 */

#ifdef CHIP1370

static int snd_es1370_get_switch(snd_kmixer_t * mixer,
				 snd_kswitch_t * kswitch,
				 snd_switch_t * uswitch)
{
	unsigned long flags;
	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_spin_lock(ensoniq, reg, &flags);
	uswitch->value.enable = ensoniq->ctrl & mask ? 1 : 0;
	snd_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

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

static snd_kswitch_t snd_es1370_mixer_switch1 =
{
	"MIC +5V bias",
	(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 =
{
	"Line-In 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_ak4531(ak4531_t * ak4531)
{
	((ensoniq_t *) ak4531->private_data)->mixer = NULL;
	snd_free(ak4531, sizeof(ak4531_t));
}

snd_kmixer_t *snd_ensoniq_1370_mixer(ensoniq_t * ensoniq, int pcm_count, int *pcm_devs)
{
	ak4531_t *ak4531;
	snd_kmixer_t *mixer;

	/* 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, pcm_count, pcm_devs);
	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;
}

#endif /* CHIP1370 */

/*
 *  General Switches...
 */

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

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

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

	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	snd_spin_lock(ensoniq, reg, &flags);
	change = (ensoniq->ctrl & ES_JYSTK_EN) != (uswitch->value.enable ? ES_JYSTK_EN : 0);
	ensoniq->ctrl &= ~ES_JYSTK_EN;
	if (uswitch->value.enable)
		ensoniq->ctrl |= ES_JYSTK_EN;
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
	return change;
}

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
};

#ifdef CHIP1371

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

	uswitch->type = SND_SW_TYPE_WORD;
	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

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

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
};

#endif /* CHIP1371 */

/*

 */

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

#ifdef CHIP1370
	snd_iprintf(buffer, "Ensoniq AudioPCI ES1370\n\n");
#else
	snd_iprintf(buffer, "Ensoniq AudioPCI ES1371\n\n");
#endif
	snd_iprintf(buffer, "Joystick enable  : %s\n", ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
#ifdef CHIP1370
	snd_iprintf(buffer, "MIC +5V bias     : %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    : 0x%x\n", (ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
#endif
}

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)
{
#ifdef CHIP1371
	int idx;
#endif
	ensoniq_t *ensoniq;
	unsigned short cmdw;
	unsigned char cmdb;

	ensoniq = (ensoniq_t *) snd_calloc(sizeof(ensoniq_t));
	if (!ensoniq)
		return NULL;
	snd_spin_prepare(ensoniq, reg);
	snd_sleep_prepare(ensoniq, codec);
	ensoniq->card = card;
	ensoniq->pci = pci;
	ensoniq->dma1ptr = dma1ptr;
	ensoniq->dma2ptr = dma2ptr;
	ensoniq->dma3ptr = dma3ptr;
	ensoniq->irqptr = irqptr;
	ensoniq->port = pci->base_address[0] & ~PCI_BASE_ADDRESS_SPACE;
	snd_pci_read_config_word(pci, PCI_COMMAND, &cmdw);
	if ((cmdw & (PCI_COMMAND_IO|PCI_COMMAND_MASTER)) != (PCI_COMMAND_IO|PCI_COMMAND_MASTER)) {
		cmdw |= PCI_COMMAND_IO | PCI_COMMAND_MASTER;
		snd_pci_write_config_word(pci, PCI_COMMAND, cmdw);
	}
	snd_pci_read_config_byte(pci, PCI_LATENCY_TIMER, &cmdb);
	if (cmdb < 32)
		cmdb = 32;
	snd_pci_write_config_byte(pci, PCI_LATENCY_TIMER, cmdb);
	snd_ensoniq_proc_init(ensoniq);
#ifdef CHIP1370
#if 0
	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_SERR_DISABLE | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
#else	/* get microphone working */
	ensoniq->ctrl = ES_1370_CDC_EN | ES_1370_PCLKDIVO(ES_1370_SRTODIV(8000));
#endif
	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 */
	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));
#endif
	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);
#ifdef CHIP1371
	snd_control_switch_new(card, &snd_es1371_ctl_switch, ensoniq);
#endif
	return ensoniq;
}

void snd_ensoniq_free(ensoniq_t * ensoniq)
{
	snd_ensoniq_proc_done(ensoniq);
#ifdef CHIP1370
	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 */
#endif
	snd_free(ensoniq, sizeof(ensoniq_t));
}

/*
 *  MIDI section
 */

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

	snd_spin_lock(ensoniq, reg, &flags);
	if (ensoniq->uartm & ES_MODE_INPUT)
		mask |= ES_RXRDY;
	if (ensoniq->uartm & ES_MODE_OUTPUT)
		mask |= ES_TXRDY;
	while (1) {
		status = inb(ES_REG(ensoniq, UART_STATUS));
		if ((status & mask) == 0)
			break;
		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_spin_unlock(ensoniq, reg, &flags);
				if (rmidi->output.data(rmidi, &byte, 1) != 1) {
					snd_spin_lock(ensoniq, reg, &flags);
					ensoniq->uartc &= ~ES_TXINTENM;
					mask &= ~ES_TXRDY;
				} else {
					snd_spin_lock(ensoniq, reg, &flags);
					outb(byte, ES_REG(ensoniq, UART_DATA));
				}
			} else {
				ensoniq->uartc &= ~ES_TXINTENM;
			}
		}
	}
	outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
}

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

	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);
	return 0;
}

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

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

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

	snd_spin_lock(ensoniq, reg, &flags);
	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)) {
			unsigned char byte;
			snd_spin_unlock(ensoniq, reg, &flags);
			if (rmidi->output.data(rmidi, &byte, 1) != 1) {
				snd_spin_lock(ensoniq, reg, &flags);
				ensoniq->uartc &= ~ES_TXINTENM;
			} else {
				snd_spin_lock(ensoniq, reg, &flags);
				outb(byte, ES_REG(ensoniq, UART_DATA));
			}
		}
	} else {
		ensoniq->uartc &= ~ES_TXINTENM;
	}
	outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
	snd_spin_unlock(ensoniq, reg, &flags);
}

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;
#ifdef CHIP1370
	strcpy(rmidi->name, "ES1370");
#else
	strcpy(rmidi->name, "ES1371");
#endif
	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;
	unsigned long flags;

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

	snd_spin_lock(ensoniq, reg, &flags);
	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_spin_unlock(ensoniq, reg, &flags);

	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);
	}
}

/*
 *  INIT part
 */

#ifndef LINUX_2_1
#ifdef CHIP1370
extern struct symbol_table snd_symbol_table_ens1370_export;
#else
extern struct symbol_table snd_symbol_table_ens1371_export;
#endif
#endif

int init_module(void)
{
#ifndef LINUX_2_1
#ifdef CHIP1370
	if (register_symtab(&snd_symbol_table_ens1370_export) < 0)
#else
	if (register_symtab(&snd_symbol_table_ens1371_export) < 0)
#endif
		return -ENOMEM;
#endif
	return 0;
}

void cleanup_module(void)
{
}
