/*
 *  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 "../../include/driver.h"
#include "../../include/control.h"
#include "../../include/info.h"
#include "../../include/audiopci.h"

#ifndef CHIP1371
#undef CHIP1370
#define CHIP1370
#endif

#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
 */

#define POLL_COUNT	0xa000

#ifdef CHIP1370
static unsigned char bugbuf[16] __attribute__ ((aligned (16)));
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 = 0;

	for (t = 0; t < POLL_COUNT; t++) {
		r = inl(ES_REG(ensoniq, 1371_SMPRATE));
		if ((r & ES_1371_SRC_RAM_BUSY) == 0)
			return r;
	}
	snd_printk("es1371: wait source ready timeout 0x%lx [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 temp, i, orig, r;

	/* wait for ready */
	temp = orig = snd_es1371_wait_src_ready(ensoniq);

	/* expose the SRC state bits */
	r = temp & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
		    ES_1371_DIS_P2 | ES_1371_DIS_R1);
	r |= ES_1371_SRC_RAM_ADDRO(reg) | 0x10000;
	outl(r, ES_REG(ensoniq, 1371_SMPRATE));

	/* now, wait for busy and the correct time to read */
	temp = snd_es1371_wait_src_ready(ensoniq);
	
	if ((temp & 0x00870000) != 0x00010000) {
		/* wait for the right state */
		for (i = 0; i < POLL_COUNT; i++) {
			temp = inl(ES_REG(ensoniq, 1371_SMPRATE));
			if ((temp & 0x00870000) == 0x00010000)
				break;
		}
	}

	/* hide the state bits */	
	r = orig & (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 temp;
}

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 = snd_magic_cast(ensoniq_t, private_data, );
	unsigned long flags;
	long time = HZ / 10;

#if 0
	printk("CODEC WRITE: reg = 0x%x, val = 0x%x (0x%x), creg = 0x%lx\n", reg, val, ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
#endif
	do {
		spin_lock_irqsave(&ensoniq->reg_lock, flags);
		if (!(inl(ES_REG(ensoniq, STATUS)) & ES_1370_CSTAT)) {
			outw(ES_1370_CODEC_WRITE(reg, val), ES_REG(ensoniq, 1370_CODEC));
			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
			return;
		}
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
		set_current_state(TASK_INTERRUPTIBLE);
		time -= schedule_timeout(1) ? 1 : 0;
	} while (time > 0);
	snd_printk("es1370: codec write timeout, status = 0x%x\n", inl(ES_REG(ensoniq, STATUS)));
}

#endif /* CHIP1370 */

#ifdef CHIP1371

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

	for (t = 0; t < POLL_COUNT; t++) {
		spin_lock_irqsave(&ensoniq->reg_lock, flags);
		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
			/* save the current state for latter */
			x = snd_es1371_wait_src_ready(ensoniq);
			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
			     ES_REG(ensoniq, 1371_SMPRATE));
			/* wait for not busy (state 0) first to avoid
			   transition states */
			for (t = 0; t < POLL_COUNT; t++) {
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
					break;
			}
			/* wait for a SAFE time to write addr/data and then do it, dammit */
			for (t = 0; t < POLL_COUNT; t++) {
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 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));
			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
			return;
		}
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	}
	snd_printk("es1371: codec write timeout at 0x%lx [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 = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	unsigned long flags;
	unsigned int t, x, fail = 0;

      __again:
	for (t = 0; t < POLL_COUNT; t++) {
		spin_lock_irqsave(&ensoniq->reg_lock, flags);
		if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP)) {
			/* save the current state for latter */
			x = snd_es1371_wait_src_ready(ensoniq);
			outl((x & (ES_1371_SRC_DISABLE | ES_1371_DIS_P1 |
			           ES_1371_DIS_P2 | ES_1371_DIS_R1)) | 0x00010000,
			     ES_REG(ensoniq, 1371_SMPRATE));
			/* wait for not busy (state 0) first to avoid
			   transition states */
			for (t = 0; t < POLL_COUNT; t++) {
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 0x00000000)
					break;
			}
			/* wait for a SAFE time to write addr/data and then do it, dammit */
			for (t = 0; t < POLL_COUNT; t++) {
				if ((inl(ES_REG(ensoniq, 1371_SMPRATE)) & 0x00870000) == 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));
			/* wait for WIP again */
			for (t = 0; t < POLL_COUNT; t++) {
				if (!(inl(ES_REG(ensoniq, 1371_CODEC)) & ES_1371_CODEC_WIP))
					break;		
			}
			/* now wait for the stinkin' data (RDY) */
			for (t = 0; t < POLL_COUNT; t++) {
				if ((x = inl(ES_REG(ensoniq, 1371_CODEC))) & ES_1371_CODEC_RDY) {
					spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
					return ES_1371_CODEC_READ(x);
				}
			}
			spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
			if (++fail > 10) {
				snd_printk("es1371: codec read timeout (final) at 0x%lx, reg = 0x%x [0x%x]\n", ES_REG(ensoniq, 1371_CODEC), reg, inl(ES_REG(ensoniq, 1371_CODEC)));
				return 0;
			}
			goto __again;
		}
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	}
	snd_printk("es1371: codec read timeout at 0x%lx [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 + 1500) / 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) + 1500) / 3000;
	result = ((freq * 3000) + (1 << 14)) >> 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) + 1500) / 3000;
	result = ((freq * 3000) + (1 << 14)) >> 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 int snd_ensoniq_trigger(ensoniq_t *ensoniq, int cmd, unsigned int what, unsigned int pause)
{
	unsigned long flags;
	int result = 0;
	snd_pcm_subchn_t *master = NULL;
	snd_pcm_subchn_t *psubchn1 = NULL;
	snd_pcm_subchn_t *psubchn2 = NULL;
	snd_pcm_subchn_t *csubchn = NULL;
                
	if (cmd == SND_PCM_TRIGGER_SYNC_GO) {
		unsigned int what1 = 0;
	
		psubchn1 = ensoniq->playback_subchn;
		psubchn2 = ensoniq->playback2_subchn;
		csubchn = ensoniq->capture_subchn;
		if (what & ES_DAC1_EN) {
			master = psubchn1;
		} else if (what & ES_DAC2_EN) {
			master = psubchn2;
		} else {
			master = csubchn;
		}
		cmd = SND_PCM_TRIGGER_GO;
		if (psubchn1 && !memcmp(&master->runtime->sync_group, &psubchn1->runtime->sync_group, sizeof(snd_pcm_sync_t))) {
			what1 |= ES_DAC1_EN;
		} else {
			psubchn1 = NULL;
		}
		if (psubchn2 && !memcmp(&master->runtime->sync_group, &psubchn2->runtime->sync_group, sizeof(snd_pcm_sync_t))) {
			what1 |= ES_DAC2_EN;
		} else {
			psubchn2 = NULL;
		}
		if (csubchn && !memcmp(&master->runtime->sync_group, &csubchn->runtime->sync_group, sizeof(snd_pcm_sync_t))) {
			what1 |= ES_ADC_EN;
		} else {
			csubchn = NULL;
		}
		if ((what1 & what) == 0)
			return -EINVAL;
		if (what1 == what)
			goto __standard;
		what = what1;
		if (psubchn1 && !snd_pcm_playback_data(psubchn1))
			return -EINVAL;
		if (psubchn2 && !snd_pcm_playback_data(psubchn2))
			return -EINVAL;
		if (csubchn && !snd_pcm_capture_empty(csubchn))
			return -EINVAL;
		if (psubchn1 && (result = snd_pcm_channel_go_pre(psubchn1, SND_PCM_TRIGGER_GO)) < 0)
			goto __cleanup;
		if (psubchn2 && (result = snd_pcm_channel_go_pre(psubchn2, SND_PCM_TRIGGER_GO)) < 0)
			goto __cleanup;
		if (csubchn && (result = snd_pcm_channel_go_pre(csubchn, SND_PCM_TRIGGER_GO)) < 0)
			goto __cleanup;
		result = 0;
	}
      __standard:
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (cmd == SND_PCM_TRIGGER_GO) {
		ensoniq->ctrl |= what;
		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	} else if (cmd == SND_PCM_TRIGGER_STOP) {
		ensoniq->ctrl &= ~what;
		outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	} else if (pause != -1 && cmd == SND_PCM_TRIGGER_PAUSE_PUSH) {
		ensoniq->sctrl |= pause;
		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	} else if (pause != -1 && cmd == SND_PCM_TRIGGER_PAUSE_RELEASE) {
		ensoniq->sctrl &= ~pause;
		outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	} else {
		result = -EINVAL;
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
      __cleanup:
	if (psubchn1)
		snd_pcm_channel_go_post(psubchn1, SND_PCM_TRIGGER_GO, result);
	if (psubchn2)
		snd_pcm_channel_go_post(psubchn2, SND_PCM_TRIGGER_GO, result);
	if (csubchn)
		snd_pcm_channel_go_post(csubchn, SND_PCM_TRIGGER_GO, result);
	return result;
}

/*
 *  PCM part
 */

static void snd_ensoniq_dma_transfer_limit(ensoniq_t *ensoniq, snd_pcm_subchn_t *subchn)
{
	unsigned int mode = 0, size, size1;
	snd_pcm_runtime_t *runtime = subchn->runtime;

	if (snd_pcm_format_width(runtime->format.format) == 16)
		mode |= 0x02;
	if (runtime->format.voices > 1)
		mode |= 0x01;
	size = size1 = snd_pcm_lib_transfer_size(subchn);
	size >>= snd_ensoniq_sample_shift[mode];
	while (size & 0xffff0000) {
		size1 >>= 1;
		size >>= 1;
	}
	snd_pcm_lib_set_buffer_size(subchn, size1);
}

static int snd_ensoniq_playback1_ioctl(void *private_data,
				       snd_pcm_subchn_t * subchn,
				       unsigned int cmd,
				       unsigned long *arg)
{
	int result;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	result = snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
	if (result < 0)
		return result;
	if (cmd == SND_PCM_IOCTL1_PARAMS) {
#ifdef CHIP1370 
		subchn->runtime->format.rate = snd_es1370_fixed_rates[snd_es1370_fixed_rate(subchn->runtime->format.rate)];
#else

		subchn->runtime->format.rate = snd_es1371_dac1_rate(ensoniq, subchn->runtime->format.rate, 0);
#endif
		snd_ensoniq_dma_transfer_limit(ensoniq, subchn);
	}
	return 0;
}

static int snd_ensoniq_playback2_ioctl(void *private_data,
				       snd_pcm_subchn_t * subchn,
				       unsigned int cmd,
				       unsigned long *arg)
{
	int result;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	result = snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
	if (result < 0)
		return result;
	if (cmd == SND_PCM_IOCTL1_PARAMS) {
#ifdef CHIP1370
		unsigned long flags;
		unsigned int pclkdiv;
		
		spin_lock_irqsave(&ensoniq->reg_lock, flags);
		if (ensoniq->u.es1370.pclkdiv_lock)
			pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
		else
			pclkdiv = ES_1370_SRTODIV(subchn->runtime->format.rate);
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
		subchn->runtime->format.rate = ES_1370_DIVTOSR(pclkdiv);
#else
		subchn->runtime->format.rate = snd_es1371_dac2_rate(ensoniq, subchn->runtime->format.rate, 0);
#endif
		snd_ensoniq_dma_transfer_limit((ensoniq_t *) private_data, subchn);
	}
	return 0;
}

static int snd_ensoniq_capture_ioctl(void *private_data,
				    snd_pcm_subchn_t * subchn,
				    unsigned int cmd,
				    unsigned long *arg)
{
	int result;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	result = snd_pcm_lib_ioctl(private_data, subchn, cmd, arg);
	if (result < 0)
		return result;
	if (cmd == SND_PCM_IOCTL1_PARAMS) {
#ifdef CHIP1370
		unsigned long flags;
		unsigned int pclkdiv;

		spin_lock_irqsave(&ensoniq->reg_lock, flags);
		if (ensoniq->u.es1370.pclkdiv_lock)
			pclkdiv = ES_1370_PCLKDIVI(ensoniq->ctrl);
		else
			pclkdiv = ES_1370_SRTODIV(subchn->runtime->format.rate);
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
		subchn->runtime->format.rate = ES_1370_DIVTOSR(pclkdiv);
#else
		subchn->runtime->format.rate = snd_es1371_adc_rate(ensoniq, subchn->runtime->format.rate, 0);
#endif
		snd_ensoniq_dma_transfer_limit((ensoniq_t *) private_data, subchn);
	}
	return 0;
}

static int snd_ensoniq_playback1_trigger(void *private_data,
					 snd_pcm_subchn_t * pcm,
					 int cmd)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	return snd_ensoniq_trigger(ensoniq, cmd, ES_DAC1_EN, ES_P1_PAUSE);
}

static int snd_ensoniq_playback2_trigger(void *private_data,
					 snd_pcm_subchn_t * subchn,
					 int cmd)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	return snd_ensoniq_trigger(ensoniq, cmd, ES_DAC2_EN, ES_P2_PAUSE);
}

static int snd_ensoniq_capture_trigger(void *private_data,
				       snd_pcm_subchn_t * subchn,
				       int cmd)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	return snd_ensoniq_trigger(ensoniq, cmd, ES_ADC_EN, -1);
}

static int snd_ensoniq_playback1_prepare(void *private_data,
					 snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	snd_pcm_runtime_t *runtime = subchn->runtime;
	unsigned int mode = 0;

	ensoniq->p1_dma_size = snd_pcm_lib_transfer_size(subchn);
	ensoniq->p1_frag_size = snd_pcm_lib_transfer_fragment(subchn);
	if (snd_pcm_format_width(runtime->format.format) == 16)
		mode |= 0x02;
	if (runtime->format.voices > 1)
		mode |= 0x01;
	spin_lock_irqsave(&ensoniq->reg_lock, 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(runtime->dma_area->buf), ES_REG(ensoniq, DAC1_FRAME));
	outl((ensoniq->p1_dma_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((ensoniq->p1_frag_size >> 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(runtime->format.rate));
#else
	snd_es1371_dac1_rate(ensoniq, runtime->format.rate, 1);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

static int snd_ensoniq_playback2_prepare(void *private_data,
					 snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	snd_pcm_runtime_t *runtime = subchn->runtime;
	unsigned int mode = 0;

	ensoniq->p2_dma_size = snd_pcm_lib_transfer_size(subchn);
	ensoniq->p2_frag_size = snd_pcm_lib_transfer_fragment(subchn);
	if (snd_pcm_format_width(runtime->format.format) == 16)
		mode |= 0x02;
	if (runtime->format.voices > 1)
		mode |= 0x01;
	spin_lock_irqsave(&ensoniq->reg_lock, 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(runtime->dma_area->buf), ES_REG(ensoniq, DAC2_FRAME));
	outl((ensoniq->p2_dma_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 ? 2 : 1) | ES_P2_ST_INCO(0);
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	outl((ensoniq->p2_frag_size >> 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(runtime->format.rate));
		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_PLAY2;
	}
#else
	snd_es1371_dac2_rate(ensoniq, runtime->format.rate, 1);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

static int snd_ensoniq_capture_prepare(void *private_data,
				       snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	snd_pcm_runtime_t *runtime = subchn->runtime;
	unsigned int mode = 0;

	ensoniq->c_dma_size = snd_pcm_lib_transfer_size(subchn);
	ensoniq->c_frag_size = snd_pcm_lib_transfer_fragment(subchn);
	if (snd_pcm_format_width(runtime->format.format) == 16)
		mode |= 0x02;
	if (runtime->format.voices > 1)
		mode |= 0x01;
	spin_lock_irqsave(&ensoniq->reg_lock, 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(runtime->dma_area->buf), ES_REG(ensoniq, ADC_FRAME));
	outl((ensoniq->c_dma_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((ensoniq->c_frag_size >> 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_CAPTURE) {
		ensoniq->ctrl &= ~ES_1370_PCLKDIVM;
		ensoniq->ctrl |= ES_1370_PCLKDIVO(ES_1370_SRTODIV(runtime->format.rate));
		ensoniq->u.es1370.pclkdiv_lock |= ES_MODE_CAPTURE;
	}
#else
	snd_es1371_adc_rate(ensoniq, runtime->format.rate, 1);
#endif
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

static unsigned int snd_ensoniq_playback1_pointer(void *private_data,
						  snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	unsigned int ptr;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC1_EN) {	
		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC1_SIZE)));
	} else {
		ptr = 0;
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return ptr;
}

static unsigned int snd_ensoniq_playback2_pointer(void *private_data,
						  snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	unsigned int ptr;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (inl(ES_REG(ensoniq, CONTROL)) & ES_DAC2_EN) {
		outl(ES_MEM_PAGEO(ES_PAGE_DAC), ES_REG(ensoniq, MEM_PAGE));
		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, DAC2_SIZE)));
	} else {
		ptr = 0;
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return ptr;
}

static unsigned int snd_ensoniq_capture_pointer(void *private_data,
						snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	unsigned int ptr;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (inl(ES_REG(ensoniq, CONTROL)) & ES_ADC_EN) {
		outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
		ptr = ES_REG_FCURR_COUNTI(inl(ES_REG(ensoniq, ADC_SIZE)));
	} else {
		ptr = 0;
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return ptr;
}

static snd_pcm_hardware_t snd_ensoniq_playback1 =
{
	SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_STREAM |
	SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_INTERLEAVE |
	SND_PCM_CHNINFO_BLOCK_TRANSFER |
	SND_PCM_CHNINFO_MMAP_VALID |
	SND_PCM_CHNINFO_PAUSE,	/* flags */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_48000,
	4000,			/* min. rate */
	48000,			/* max. rate */
	1,			/* min. voices */
	2,			/* max. voices */
	64,			/* min. fragment size */
	(128*1024),		/* max. fragment size */
	31,			/* fragment align */
	0,			/* FIFO size (unknown) */
	4,			/* transfer block size */
	snd_ensoniq_playback1_ioctl,
	snd_ensoniq_playback1_prepare,
	snd_ensoniq_playback1_trigger,
	snd_ensoniq_playback1_pointer
};

static snd_pcm_hardware_t snd_ensoniq_playback2 =
{
	SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_STREAM |
	SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_INTERLEAVE |
	SND_PCM_CHNINFO_BLOCK_TRANSFER |
	SND_PCM_CHNINFO_MMAP_VALID |
	SND_PCM_CHNINFO_PAUSE,	/* flags */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
#ifndef CHIP1370
	SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_48000,
#else
	SND_PCM_RATE_KNOT | 	/* 5512Hz rate */
	SND_PCM_RATE_11025 | SND_PCM_RATE_22050 | SND_PCM_RATE_44100,
#endif
	4000,			/* min. rate */
	48000,			/* max. rate */
	1,			/* min. voices */
	2,			/* max. voices */
	64,			/* min. fragment size */
	(128*1024),		/* max. fragment size */
	31,			/* fragment align */
	0,			/* FIFO size (unknown) */
	4,			/* transfer block size */
	snd_ensoniq_playback2_ioctl,
	snd_ensoniq_playback2_prepare,
	snd_ensoniq_playback2_trigger,
	snd_ensoniq_playback2_pointer
};

static snd_pcm_hardware_t snd_ensoniq_capture =
{
	SND_PCM_CHNINFO_MMAP | SND_PCM_CHNINFO_STREAM |
	SND_PCM_CHNINFO_BLOCK | SND_PCM_CHNINFO_INTERLEAVE |
	SND_PCM_CHNINFO_BLOCK_TRANSFER |
	SND_PCM_CHNINFO_MMAP_VALID,	/* flags */
	SND_PCM_FMT_U8 | SND_PCM_FMT_S16_LE,	/* hardware formats */
	SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_48000,
	4000,			/* min. rate */
	48000,			/* max. rate */
	1,			/* min. voices */
	2,			/* max. voices */
	64,			/* min. fragment size */
	(128*1024),		/* max. fragment size */
	31,			/* fragment align */
	0,			/* FIFO size (unknown) */
	4,			/* transfer block size */
	snd_ensoniq_capture_ioctl,
	snd_ensoniq_capture_prepare,
	snd_ensoniq_capture_trigger,
	snd_ensoniq_capture_pointer
};

static int snd_ensoniq_playback1_open(void *private_data,
				      snd_pcm_subchn_t * subchn)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	int err;

	if ((err = snd_pcm_dma_alloc(subchn, ensoniq->dma1ptr,
				     "Ensoniq AudioPCI - DAC1")) < 0)
		return err;
	ensoniq->mode |= ES_MODE_PLAY1;
	ensoniq->playback_subchn = subchn;
	subchn->runtime->hw = &snd_ensoniq_playback1;
	snd_pcm_set_sync(subchn);
#ifdef CHIP1370
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1370.ak4531->me_playback);
#else
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1371.ac97->me_playback);
#endif
	return 0;
}

static int snd_ensoniq_playback2_open(void *private_data,
				      snd_pcm_subchn_t * subchn)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	int err;

	if ((err = snd_pcm_dma_alloc(subchn, ensoniq->dma2ptr,
				     "Ensoniq AudioPCI - DAC2")) < 0)
		return err;
	ensoniq->mode |= ES_MODE_PLAY2;
	ensoniq->playback2_subchn = subchn;
	subchn->runtime->hw = &snd_ensoniq_playback2;
	snd_pcm_set_sync(subchn);
#ifdef CHIP1370
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1370.ak4531->me_playback1);
#else
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1371.ac97->me_playback);
#endif
	return 0;
}

static int snd_ensoniq_capture_open(void *private_data,
				    snd_pcm_subchn_t * subchn)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);
	int err;

	if ((err = snd_pcm_dma_alloc(subchn, ensoniq->dma3ptr,
				     "Ensoniq AudioPCI - ADC")) < 0)
		return err;
	ensoniq->mode |= ES_MODE_CAPTURE;
	ensoniq->capture_subchn = subchn;
	subchn->runtime->hw = &snd_ensoniq_capture;	
	snd_pcm_set_sync(subchn);
#ifdef CHIP1370
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1370.ak4531->me_capture);
#else
	snd_pcm_set_mixer(subchn, ensoniq->mixer->device, ensoniq->u.es1371.ac97->me_capture);
#endif
	return 0;
}

static int snd_ensoniq_playback1_close(void *private_data,
				       snd_pcm_subchn_t * subchn)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	ensoniq->playback_subchn = NULL;
	snd_pcm_dma_free(subchn);
	ensoniq->mode &= ~ES_MODE_PLAY1;
	return 0;
}

static int snd_ensoniq_playback2_close(void *private_data,
				       snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	ensoniq->playback2_subchn = NULL;
	snd_pcm_dma_free(subchn);
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
#ifdef CHIP1370
	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_PLAY2;
#endif
	ensoniq->mode &= ~ES_MODE_PLAY2;
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

static int snd_ensoniq_capture_close(void *private_data,
				     snd_pcm_subchn_t * subchn)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, -ENXIO);

	ensoniq->capture_subchn = NULL;
	snd_pcm_dma_free(subchn);
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
#ifdef CHIP1370
	ensoniq->u.es1370.pclkdiv_lock &= ~ES_MODE_CAPTURE;
#endif
	ensoniq->mode &= ~ES_MODE_CAPTURE;
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

int snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
{
	snd_pcm_t *pcm;
	int err;

	*rpcm = NULL;
#ifdef CHIP1370
	err = snd_pcm_new(ensoniq->card, "ES1370/1", device, 1, 1, &pcm);
#else
	err = snd_pcm_new(ensoniq->card, "ES1371/1", device, 1, 1, &pcm);
#endif
	if (err < 0)
		return err;

	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].private_data = ensoniq;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].open = snd_ensoniq_playback2_open;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].close = snd_ensoniq_playback2_close;

	pcm->chn[SND_PCM_CHANNEL_CAPTURE].private_data = ensoniq;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].open = snd_ensoniq_capture_open;
	pcm->chn[SND_PCM_CHANNEL_CAPTURE].close = snd_ensoniq_capture_close;

	pcm->private_data = ensoniq;
	pcm->private_free = snd_ensoniq_pcm_free;
	pcm->info_flags = SND_PCM_INFO_PLAYBACK | SND_PCM_INFO_CAPTURE |
			  SND_PCM_INFO_DUPLEX;
#ifdef CHIP1370
	strcpy(pcm->name, "ES1370 DAC2/ADC");
#else
	strcpy(pcm->name, "ES1371 DAC2/ADC");
#endif
	*rpcm = ensoniq->pcm = pcm;
	return 0;
}

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

int snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t ** rpcm)
{
	snd_pcm_t *pcm;
	int err;

	*rpcm = NULL;
#ifdef CHIP1370
	err = snd_pcm_new(ensoniq->card, "ES1370/2", device, 1, 0, &pcm);
#else
	err = snd_pcm_new(ensoniq->card, "ES1371/2", device, 1, 0, &pcm);
#endif
	if (err < 0)
		return err;

	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].private_data = ensoniq;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].open = snd_ensoniq_playback1_open;
	pcm->chn[SND_PCM_CHANNEL_PLAYBACK].close = snd_ensoniq_playback1_close;

	pcm->private_data = ensoniq;
	pcm->private_free = snd_ensoniq_pcm_free2;
	pcm->info_flags = SND_PCM_INFO_PLAYBACK;
#ifdef CHIP1370
	strcpy(pcm->name, "ES1370 DAC1");
#else
	strcpy(pcm->name, "ES1371 DAC1");
#endif
	*rpcm = ensoniq->pcm2 = pcm;
	return 0;
}

/*
 *  Mixer section
 */

#ifdef CHIP1371

static int snd_es1371_get_spdif(snd_kmixer_t * mixer,
				snd_kswitch_t * kswitch,
				snd_switch_t * uswitch)
{
	unsigned long flags;
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	uswitch->value.enable = ensoniq->ctrl & ES_1371_SPDIF_EN ? 1 : 0;
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	change = (ensoniq->ctrl & ES_1371_SPDIF_EN) != (uswitch->value.enable ? ES_1371_SPDIF_EN : 0);
	ensoniq->ctrl &= ~ES_1371_SPDIF_EN;
	ensoniq->cssr &= ~ES_1371_ST_SPDIF_EN;
	if (uswitch->value.enable) {
		ensoniq->ctrl |= ES_1371_SPDIF_EN;
		ensoniq->cssr |= ES_1371_ST_SPDIF_EN;
	}
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return change;
}

static snd_kswitch_t snd_es1371_mixer_spdif =
{
	name:	"S/PDIF output",
	get:	(snd_get_switch_t *)snd_es1371_get_spdif,
	set:	(snd_set_switch_t *)snd_es1371_set_spdif,
};

static void snd_ensoniq_mixer_free_ac97(void *private_data)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, );
	ensoniq->mixer = NULL;
}

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

	*rmixer = NULL;
	memset(&ac97, 0, sizeof(ac97));
	ac97.write = snd_es1371_codec_write;
	ac97.read = snd_es1371_codec_read;
	ac97.private_data = ensoniq;
	ac97.private_free = snd_ensoniq_mixer_free_ac97;
	if ((err = snd_ac97_mixer(ensoniq->card, device, &ac97, pcm_count, pcm_devs, &mixer)) < 0) {
		return err;
	}
	ensoniq->u.es1371.ac97 = snd_magic_cast(ac97_t, mixer->private_data, -ENXIO);
	if (ensoniq->rev >= ES1371REV_ES1373_A)
		snd_mixer_switch_new(mixer, &snd_es1371_mixer_spdif, ensoniq);
	*rmixer = ensoniq->mixer = mixer;
	return 0;
}

#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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);
	unsigned int mask;

	mask = kswitch->private_value ? ES_1370_XCTL0 : ES_1370_XCTL1;
	uswitch->type = SND_SW_TYPE_BOOLEAN;
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	uswitch->value.enable = ensoniq->ctrl & mask ? 1 : 0;
	spin_unlock_irqrestore(&ensoniq->reg_lock, 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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);
	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;
	spin_lock_irqsave(&ensoniq->reg_lock, 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));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return change;
}

static snd_kswitch_t snd_es1370_mixer_switch1 =
{
	name:           "MIC +5V bias",
	get:            (snd_get_switch_t *)snd_es1370_get_switch,
	set:            (snd_set_switch_t *)snd_es1370_set_switch,
	private_value:  0,
};

static snd_kswitch_t snd_es1370_mixer_switch2 =
{
	name:           "Line-In to Output",
	get:            (snd_get_switch_t *)snd_es1370_get_switch,
	set:            (snd_set_switch_t *)snd_es1370_set_switch,
	private_value:  1,
};

static void snd_ensoniq_mixer_free_ak4531(void *private_data)
{
	ensoniq_t *ensoniq = snd_magic_cast(ensoniq_t, private_data, );
	ensoniq->mixer = NULL;
}

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

	*rmixer = NULL;
	/* try reset AK4531 */
	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x02), ES_REG(ensoniq, 1370_CODEC));
	udelay(100);
	outw(ES_1370_CODEC_WRITE(AK4531_RESET, 0x03), ES_REG(ensoniq, 1370_CODEC));
	udelay(100);

	memset(&ak4531, 0, sizeof(ak4531));
	ak4531.write = snd_es1370_codec_write;
	ak4531.private_data = ensoniq;
	ak4531.private_free = snd_ensoniq_mixer_free_ak4531;
	if ((err = snd_ak4531_mixer(ensoniq->card, device, &ak4531, pcm_count, pcm_devs, &mixer)) < 0)
		return err;
	ensoniq->u.es1370.ak4531 = snd_magic_cast(ak4531_t, mixer->private_data, -ENXIO);
	snd_mixer_switch_new(mixer, &snd_es1370_mixer_switch1, ensoniq);
	snd_mixer_switch_new(mixer, &snd_es1370_mixer_switch2, ensoniq);
	*rmixer = ensoniq->mixer = mixer;
	return 0;
}

#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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);

	uswitch->type = SND_SW_TYPE_BOOLEAN;
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	uswitch->value.enable = ensoniq->ctrl & ES_JYSTK_EN ? 1 : 0;
	spin_unlock_irqrestore(&ensoniq->reg_lock, 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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);
	int change = 0;

	if (uswitch->type != SND_SW_TYPE_BOOLEAN)
		return -EINVAL;
	spin_lock_irqsave(&ensoniq->reg_lock, 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));
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return change;
}

static snd_kswitch_t snd_ensoniq_ctl_switch =
{
	name:	SND_CTL_SW_JOYSTICK,
	get:	(snd_get_switch_t *)snd_ensoniq_get_ctl_switch,
	set:	(snd_set_switch_t *)snd_ensoniq_set_ctl_switch,
};

#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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);

	uswitch->type = SND_SW_TYPE_WORD;
	uswitch->subtype = SND_SW_SUBTYPE_HEXA;
	spin_lock_irqsave(&ensoniq->reg_lock, 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;
	spin_unlock_irqrestore(&ensoniq->reg_lock, 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 = snd_magic_cast(ensoniq_t, kswitch->private_data, -ENXIO);
	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);
	spin_lock_irqsave(&ensoniq->reg_lock, 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));
	spin_unlock_irqrestore(&ensoniq->reg_lock, 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 = snd_magic_cast(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;
	}
}

/*

 */

int snd_ensoniq_create(snd_card_t * card,
		       struct pci_dev *pci,
		       snd_dma_t * dma1ptr,
		       snd_dma_t * dma2ptr,
		       snd_dma_t * dma3ptr,
		       snd_irq_t * irqptr,
		       ensoniq_t ** rensoniq)
{
	ensoniq_t *ensoniq;
	unsigned short cmdw;
	unsigned char cmdb;
#ifdef CHIP1371
	int idx;
#endif
	int err;
	static snd_device_ops_t ops = {
		(snd_dev_free_t *)snd_ensoniq_free,
		NULL,
		NULL
	};

	*rensoniq = NULL;
	ensoniq = snd_magic_kcalloc(ensoniq_t, 0, GFP_KERNEL);
	if (ensoniq == NULL)
		return -ENOMEM;
	spin_lock_init(&ensoniq->reg_lock);
	ensoniq->card = card;
	ensoniq->pci = pci;
	ensoniq->dma1ptr = dma1ptr;
	ensoniq->dma2ptr = dma2ptr;
	ensoniq->dma3ptr = dma3ptr;
	ensoniq->irqptr = irqptr;
	ensoniq->port = pci_resource_start(pci, 0);
	pci_read_config_word(pci, PCI_COMMAND, &cmdw);
	if ((cmdw & PCI_COMMAND_IO) != PCI_COMMAND_IO) {
		cmdw |= PCI_COMMAND_IO;
		pci_write_config_word(pci, PCI_COMMAND, cmdw);
	}
	pci_set_master(pci);
	pci_read_config_byte(pci, PCI_LATENCY_TIMER, &cmdb);
	if (cmdb < 64)
		cmdb = 64;
	pci_write_config_byte(pci, PCI_LATENCY_TIMER, cmdb);
	pci_read_config_byte(pci, PCI_REVISION_ID, &cmdb);
	ensoniq->rev = cmdb;
	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &cmdw);
	ensoniq->subsystem_vendor_id = cmdw;
	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &cmdw);
	ensoniq->subsystem_device_id = cmdw;
	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));
	outl(ES_MEM_PAGEO(ES_PAGE_ADC), ES_REG(ensoniq, MEM_PAGE));
	outl(virt_to_bus(bugbuf), ES_REG(ensoniq, PHANTOM_FRAME));
	outl(0, ES_REG(ensoniq, PHANTOM_COUNT));
#else
	ensoniq->ctrl = 0;
	ensoniq->sctrl = 0;
	ensoniq->cssr = 0;
	if ((ensoniq->subsystem_vendor_id == 0x107b &&
	     ensoniq->subsystem_device_id == 0x2150) ||	/* Gateway Solo 2150 */
	    (ensoniq->subsystem_vendor_id == 0x13bd &&
	     ensoniq->subsystem_device_id == 0x100c))   /* EV1938 on Mebius PC-MJ100V */
		ensoniq->ctrl |= ES_1371_GPIO_OUT(1);	/* turn amplifier on */
	/* initialize the chips */
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	outl(ensoniq->sctrl, ES_REG(ensoniq, SERIAL));
	outl(0, ES_REG(ensoniq, 1371_LEGACY));
	if (pci->vendor == PCI_VENDOR_ID_ENSONIQ &&
	    ((pci->device == PCI_DEVICE_ID_ENSONIQ_CT5880 &&
	      ensoniq->rev == CT5880REV_CT5880_C) ||
	     (pci->device == PCI_DEVICE_ID_ENSONIQ_ES1371 &&
	      ensoniq->rev == ES1371REV_CT5880_A) ||
	     (pci->device == PCI_DEVICE_ID_ENSONIQ_ES1371 &&
	      ensoniq->rev == ES1371REV_ES1373_8))) {
		unsigned long tmo;
                signed long tmo2;

		ensoniq->cssr |= ES_1371_ST_AC97_RST;
		outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
		/* need to delay around 20ms(bleech) to give
		   some CODECs enough time to wakeup */
		tmo = jiffies + (HZ / 50) + 1;
		while (1) {
			tmo2 = tmo - jiffies;
			if (tmo2 <= 0)
				break;
			schedule_timeout(tmo2);
		}
	}
	/* AC'97 warm reset to start the bitclk */
	outl(ensoniq->ctrl | ES_1371_SYNC_RES, ES_REG(ensoniq, CONTROL));
	udelay(20);
	outl(ensoniq->ctrl, ES_REG(ensoniq, CONTROL));
	/* Init the sample rate converter */
	snd_es1371_wait_src_ready(ensoniq);	
	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
	 */
	snd_es1371_wait_src_ready(ensoniq);
	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));
	outl(ensoniq->cssr, ES_REG(ensoniq, STATUS));
	snd_control_switch_new(card, &snd_ensoniq_ctl_switch, ensoniq);
#ifdef CHIP1371
	snd_control_switch_new(card, &snd_es1371_ctl_switch, ensoniq);
#endif
	synchronize_irq();

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

	*rensoniq = ensoniq;
	return 0;
}

int 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
	synchronize_irq();
	snd_magic_kfree(ensoniq);
	return 0;
}

/*
 *  MIDI section
 */

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

	/* do Rx at first */
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	mask = ensoniq->uartm & ES_MODE_INPUT ? ES_RXRDY : 0;
	while (mask) {
		status = inb(ES_REG(ensoniq, UART_STATUS));
		if ((status & mask) == 0)
			break;
		byte = inb(ES_REG(ensoniq, UART_DATA));
		spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
		snd_rawmidi_receive(rmidi, &byte, 1);
		spin_lock_irqsave(&ensoniq->reg_lock, flags);
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);

	/* do Tx at second */
	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	mask = ensoniq->uartm & ES_MODE_OUTPUT ? ES_TXRDY : 0;
	while (mask) {
		status = inb(ES_REG(ensoniq, UART_STATUS));
		if ((status & mask) == 0)
			break;
		if (snd_rawmidi_transmit(rmidi, &byte, 1) != 1) {
			ensoniq->uartc &= ~ES_TXINTENM;
			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
			mask &= ~ES_TXRDY;
		} else {
			outb(byte, ES_REG(ensoniq, UART_DATA));
		}
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, 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));
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, 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;
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, 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));
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, 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;
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
	return 0;
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (up) {
		if ((ensoniq->uartc & ES_RXINTEN) == 0) {
			/* empty input FIFO */
			for (idx = 0; idx < 32; idx++)
				inb(ES_REG(ensoniq, UART_DATA));
			ensoniq->uartc |= ES_RXINTEN;
			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
		}
	} else {
		if (ensoniq->uartc & ES_RXINTEN) {
			ensoniq->uartc &= ~ES_RXINTEN;
			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
		}
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}

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

	spin_lock_irqsave(&ensoniq->reg_lock, flags);
	if (up) {
		if (ES_TXINTENI(ensoniq->uartc) == 0) {
			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)) {
				if (snd_rawmidi_transmit(rmidi, &byte, 1) != 1) {
					ensoniq->uartc &= ~ES_TXINTENM;
				} else {
					outb(byte, ES_REG(ensoniq, UART_DATA));
				}
			}
			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
		}
	} else {
		if (ES_TXINTENI(ensoniq->uartc) == 1) {
			ensoniq->uartc &= ~ES_TXINTENM;
			outb(ensoniq->uartc, ES_REG(ensoniq, UART_CONTROL));
		}
	}
	spin_unlock_irqrestore(&ensoniq->reg_lock, flags);
}

static struct snd_stru_rawmidi_channel_hw snd_ensoniq_midi_output =
{
	open:           snd_ensoniq_midi_output_open,
	close:          snd_ensoniq_midi_output_close,
	trigger:        snd_ensoniq_midi_output_trigger,
};

static struct snd_stru_rawmidi_channel_hw snd_ensoniq_midi_input =
{
	open:           snd_ensoniq_midi_input_open,
	close:          snd_ensoniq_midi_input_close,
	trigger:        snd_ensoniq_midi_input_trigger,
};

int snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi)
{
	snd_rawmidi_t *rmidi;
	int err;

	*rrawmidi = NULL;
	if ((err = snd_rawmidi_new(ensoniq->card, "ES1370/1", device, &rmidi)) < 0)
		return err;
#ifdef CHIP1370
	strcpy(rmidi->name, "ES1370");
#else
	strcpy(rmidi->name, "ES1371");
#endif
	memcpy(&rmidi->chn[SND_RAWMIDI_CHANNEL_OUTPUT].hw, &snd_ensoniq_midi_output, sizeof(snd_ensoniq_midi_output));
	memcpy(&rmidi->chn[SND_RAWMIDI_CHANNEL_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;
	*rrawmidi = ensoniq->rmidi = rmidi;
	return 0;
}

/*
 *  Interrupt handler
 */

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

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

	spin_lock(&ensoniq->reg_lock);
	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));
	spin_unlock(&ensoniq->reg_lock);

	if (ensoniq->rmidi) {
		if (status & ES_UART)
			snd_ensoniq_midi_interrupt(ensoniq, ensoniq->rmidi);
	}
	if (ensoniq->pcm) {
		if (status & ES_DAC2)
			snd_pcm_transfer_done(ensoniq->playback2_subchn);
		if (status & ES_ADC)
			snd_pcm_transfer_done(ensoniq->capture_subchn);
	}
	if (ensoniq->pcm2) {
		if (status & ES_DAC1)
			snd_pcm_transfer_done(ensoniq->playback_subchn);
	}
}

#ifdef CHIP1370
EXPORT_SYMBOL(snd_ensoniq_1370_create);
EXPORT_SYMBOL(snd_ensoniq_1370_interrupt);
EXPORT_SYMBOL(snd_ensoniq_1370_pcm);
EXPORT_SYMBOL(snd_ensoniq_1370_pcm2);
EXPORT_SYMBOL(snd_ensoniq_1370_midi);
EXPORT_SYMBOL(snd_ensoniq_1370_mixer);
#endif
#ifdef CHIP1371
EXPORT_SYMBOL(snd_ensoniq_1371_create);
EXPORT_SYMBOL(snd_ensoniq_1371_interrupt);
EXPORT_SYMBOL(snd_ensoniq_1371_pcm);
EXPORT_SYMBOL(snd_ensoniq_1371_pcm2);
EXPORT_SYMBOL(snd_ensoniq_1371_midi);
EXPORT_SYMBOL(snd_ensoniq_1371_mixer);
#endif

/*
 *  INIT part
 */

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

static void __exit alsa_ens137x_exit(void)
{
}

module_init(alsa_ens137x_init)
module_exit(alsa_ens137x_exit)
