/*
 *  Routines for HAL2 soundcards
 *  Copyright (c) by Ulf Carlsson <ulfc@thepuffingroup.com>
 *
 *
 *   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 "hal2.h"
#include "control.h" 

#include <asm/sgihpc.h>
#include <asm/addrspace.h>

#undef SND_HAL2_DEBUG

#define H2_INDIRECT_WAIT(regs)	while(regs->isr & H2_ISR_TSTATUS);

#define H2_READ_ADDR(addr)	(addr | (1<<7))
#define H2_WRITE_ADDR(addr)	(addr)

inline void snd_hal2_isr_write(snd_hal2_card_t *hal2, unsigned short val)
{
	hal2->hal2.ctl_regs->isr = val;
}

inline unsigned short snd_hal2_isr_look(snd_hal2_card_t *hal2)
{
	return hal2->hal2.ctl_regs->isr;
}

inline unsigned short snd_hal2_rev_look(snd_hal2_card_t *hal2)
{
	return hal2->hal2.ctl_regs->rev;
}

unsigned short snd_hal2_i_look16(snd_hal2_card_t *hal2, unsigned short addr)
{
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	return (regs->idr0 & 0xffff);
}

unsigned long snd_hal2_i_look32(snd_hal2_card_t *hal2, unsigned short addr)
{
	unsigned long ret;
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	ret = regs->idr0 & 0xffff;
	regs->iar = H2_READ_ADDR(addr | 0x1);
	H2_INDIRECT_WAIT(regs);
	ret |= (regs->idr0 & 0xffff) << 16;
	return ret;
}

void snd_hal2_i_write16(snd_hal2_card_t *hal2, unsigned short addr,
			unsigned short val)
{
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->idr0 = val;
	regs->idr1 = 0;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}

void snd_hal2_i_write32(snd_hal2_card_t *hal2, unsigned short addr,
			unsigned long val)
{
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->idr0 = val & 0xffff;
	regs->idr1 = val >> 16;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}

static void snd_hal2_i_setbit16(snd_hal2_card_t *hal2, unsigned short addr,
				unsigned short bit)
{
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	regs->idr0 = regs->idr0 | bit;
	regs->idr1 = 0;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}

#if 0
static void snd_hal2_i_setbit32(snd_hal2_card_t *hal2, unsigned short addr,
				unsigned long bit)
{
	unsigned long tmp;
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	tmp = regs->idr0 | (regs->idr1 << 16) | bit;
	regs->idr0 = tmp & 0xffff;
	regs->idr1 = tmp >> 16;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}
#endif

static void snd_hal2_i_clearbit16(snd_hal2_card_t *hal2, unsigned short addr,
				  unsigned short bit)
{
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	regs->idr0 = regs->idr0 & ~bit;
	regs->idr1 = 0;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}

#if 0
static void snd_hal2_i_clearbit32(snd_hal2_card_t *hal2, unsigned short addr,
				  unsigned long bit)
{
	unsigned long tmp;
	snd_hal2_ctl_regs_t *regs = hal2->hal2.ctl_regs;

	regs->iar = H2_READ_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
	tmp = (regs->idr0 | (regs->idr1 << 16)) & ~bit;
	regs->idr0 = tmp & 0xffff;
	regs->idr1 = tmp >> 16;
	regs->idr2 = 0;
	regs->idr3 = 0;
	regs->iar = H2_WRITE_ADDR(addr);
	H2_INDIRECT_WAIT(regs);
}
#endif

void snd_hal2_dump_regs(snd_hal2_card_t *hal2)
{
	printk("isr: %08hx ", snd_hal2_isr_look(hal2));
	printk("rev: %08hx\n", snd_hal2_rev_look(hal2));
	printk("relay: %04hx\n", snd_hal2_i_look16(hal2, H2I_RELAY_C));
	printk("port en: %04hx ", snd_hal2_i_look16(hal2, H2I_DMA_PORT_EN));
	printk("dma end: %04hx ", snd_hal2_i_look16(hal2, H2I_DMA_END));
	printk("dma drv: %04hx\n", snd_hal2_i_look16(hal2, H2I_DMA_DRV));
	printk("syn ctl: %04hx ", snd_hal2_i_look16(hal2, H2I_SYNTH_C));
	printk("aesrx ctl: %04hx ", snd_hal2_i_look16(hal2, H2I_AESRX_C));
	printk("aestx ctl: %04hx ", snd_hal2_i_look16(hal2, H2I_AESTX_C));
	printk("dac ctl1: %04hx ", snd_hal2_i_look16(hal2, H2I_DAC_C1));
	printk("dac ctl2: %08lx ", snd_hal2_i_look32(hal2, H2I_DAC_C2));
	printk("adc ctl1: %04hx ", snd_hal2_i_look16(hal2, H2I_ADC_C1));
	printk("adc ctl2: %08lx ", snd_hal2_i_look32(hal2, H2I_ADC_C2));
	printk("syn map: %04hx\n", snd_hal2_i_look16(hal2, H2I_SYNTH_MAP_C));
	printk("bres1 ctl1: %04hx ", snd_hal2_i_look16(hal2, H2I_BRES1_C1));
	printk("bres1 ctl2: %04lx ", snd_hal2_i_look32(hal2, H2I_BRES1_C2));
	printk("bres2 ctl1: %04hx ", snd_hal2_i_look16(hal2, H2I_BRES2_C1));
	printk("bres2 ctl2: %04lx ", snd_hal2_i_look32(hal2, H2I_BRES2_C2));
	printk("bres3 ctl1: %04hx ", snd_hal2_i_look16(hal2, H2I_BRES3_C1));
	printk("bres3 ctl2: %04lx\n", snd_hal2_i_look32(hal2, H2I_BRES3_C2));
}

static int snd_hal2_compute_rate(snd_hal2_card_t *hal2, unsigned int rate)
{
	/* We default to 44.1 kHz and if it isn't possible to fall back to
	 * 48.0 kHz with the needed adjustments of real_rate.
	 */

#ifdef SND_HAL2_DEBUG
	snd_printk("rate: %d\n", rate);
#endif
	if (rate < 1)
		rate = 1;	/* this is fixed later.. */

	/* Note: This is NOT the way they set up the bresenham clock generators
	 * in the specification. I've tried to implement that method but it
	 * doesn't work. It's probably another silly bug in the spec.
	 *
	 * I accidently discovered this method while I was testing and it seems
	 * to work very well with all frequencies, and thee shall follow rule #1
	 * of programming :-)
	 */
	
	if (44100 % rate == 0) {
		unsigned short inc;

		inc = 44100 / rate;

		hal2->hal2.master = 44100;
		hal2->hal2.inc = inc;
		hal2->hal2.mod = 1;
	} else {
		unsigned short inc;
		inc = 48000 / rate;
		if (inc < 1) inc = 1;

		rate = 48000 / inc;

		hal2->hal2.master = 48000;
		hal2->hal2.inc = inc;
		hal2->hal2.mod = 1;
	}

#ifdef SND_HAL2_DEBUG
	snd_printk("real_rate: %d\n", rate);
#endif
	return rate;
}

static void snd_hal2_set_rate(snd_hal2_card_t *hal2)
{
	unsigned int master = hal2->hal2.master;
	int inc = hal2->hal2.inc;
	int mod = hal2->hal2.mod;

#ifdef SND_HAL2_DEBUG
	snd_printk("master: %d inc: %d mod: %d\n", master, inc, mod);
#endif
	snd_hal2_i_write16(hal2, H2I_BRES1_C1, (master == 44100) ? 1 : 0);
	snd_hal2_i_write32(hal2, H2I_BRES1_C2, ((0xffff & (mod - inc - 1)) << 16) | 1);
}


static void snd_hal2_free_card(void *ptr)
{
	snd_hal2_card_t *hal2 = (snd_hal2_card_t *) ptr;
	snd_free(hal2, sizeof(snd_hal2_card_t));
}

snd_hal2_card_t *snd_hal2_new_card(snd_card_t * card, snd_irq_t * irqptr,
				   snd_dma_t * dma1ptr, snd_dma_t * dma2ptr,
				   snd_hal2_ctl_regs_t *ctl_regs,
				   snd_hal2_aes_regs_t *aes_regs,
				   snd_hal2_vol_regs_t *vol_regs,
				   snd_hal2_syn_regs_t *syn_regs)
{
	snd_hal2_card_t *hal2;

	hal2 = (snd_hal2_card_t *) snd_calloc(sizeof(snd_hal2_card_t));
	if (!hal2)
		return NULL;
	hal2->card = card;
	hal2->hal2.irqptr = irqptr;
	hal2->hal2.dac.dmaptr = dma1ptr;
	if (dma2ptr) {
		hal2->hal2.adc.dmaptr = dma2ptr;
	}
	hal2->hal2.ctl_regs = ctl_regs;
	hal2->hal2.aes_regs = aes_regs;
	hal2->hal2.vol_regs = vol_regs;
	hal2->hal2.syn_regs = syn_regs;
	card->private_data = hal2;
	card->private_free = snd_hal2_free_card;
	return hal2;
}

void snd_hal2_interrupt(snd_hal2_card_t *hal2)
{
	snd_pcm1_t *pcm1;
	
	if (hal2 && hal2->pcm) {
		if (hal2->hal2.dac.pbus.pbus &&
		    hal2->hal2.dac.pbus.pbus->pbdma_ctrl & 0x01) {
			pcm1 = (snd_pcm1_t *) hal2->pcm->private_data;
			if (pcm1 && pcm1->playback.ack)
				pcm1->playback.ack(pcm1);
		}
		if (hal2->hal2.adc.pbus.pbus &&
		    hal2->hal2.adc.pbus.pbus->pbdma_ctrl & 0x01){
			pcm1 = (snd_pcm1_t *) hal2->pcm->private_data;
			if (pcm1 && pcm1->record.ack)
				pcm1->record.ack(pcm1);
		}
	}
}

static int snd_hal2_playback_ioctl(snd_pcm1_t *pcm1, unsigned int cmd,
				   unsigned long *arg)
{
	snd_hal2_card_t *hal2 = (snd_hal2_card_t *) pcm1->private_data;

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
		pcm1->playback.real_rate = 
			snd_hal2_compute_rate(hal2, pcm1->playback.rate);
		return 0;
	}
	return -ENXIO;
}

static int snd_hal2_record_ioctl(snd_pcm1_t *pcm1, unsigned int cmd,
				 unsigned long *arg)
{
	snd_hal2_card_t *hal2 = (snd_hal2_card_t *) pcm1->private_data;

	switch (cmd) {
	case SND_PCM1_IOCTL_RATE:
		pcm1->playback.real_rate = 
			snd_hal2_compute_rate(hal2, pcm1->playback.rate);
		return 0;
	}
	return -ENXIO;
}

static int snd_hal2_playback_open(snd_pcm1_t *pcm1)
{
	int err;
	snd_hal2_card_t *hal2;
	snd_hal2_pbus_t *pbus;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	if (!hal2) {
		snd_printk("open: no private data?\n");
		return -EINVAL;
	}

	/* Maybe we shouldn't allocate the dma here, but when the driver is
	 * loaded so that we can *beep*.
	 */
	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_PLAYBACK,
				      hal2->hal2.dac.dmaptr,
				      "HAL2 PCM - playback")) < 0)
		return err;

	pbus = &hal2->hal2.dac.pbus;
	pbus->pbusnr = 1;
	pbus->pbus = &hpc3c0->pbdma[pbus->pbusnr];
	
#ifdef SND_HAL2_DEBUG
	snd_printk("playback open ok!\n");
#endif
	hal2->usecount++;

	return 0;
}

static int snd_hal2_record_open(snd_pcm1_t *pcm1)
{
	int err;
	snd_hal2_card_t *hal2;
	snd_hal2_pbus_t *pbus;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	if (!hal2) {
		snd_printk("open: no private data?\n");
		return -EINVAL;
	}

	/* Maybe we shouldn't allocate the dma here, but when the driver is
	 * loaded so that we can *beep*.
	 */
	if ((err = snd_pcm1_dma_alloc(pcm1, SND_PCM1_RECORD, hal2->hal2.adc.dmaptr,
				      "HAL2 PCM - playback")) < 0)
		return err;

	pbus = &hal2->hal2.adc.pbus;
	pbus->pbusnr = 2;
	pbus->pbus = &hpc3c0->pbdma[pbus->pbusnr];

#ifdef SND_HAL2_DEBUG
	snd_printk("record open ok!\n");
#endif
	hal2->usecount++;

	return 0;
}

static void snd_hal2_playback_close(snd_pcm1_t *pcm1)
{
	snd_hal2_card_t *hal2;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	if (!hal2) {
		snd_printk("close: no private data?\n");
		return;
	}

	if (hal2->hal2.dac.ringbuf) {
		snd_free_pages((char *) hal2->hal2.dac.ringbuf,
			       hal2->hal2.dac.blocks * sizeof(snd_hal2_ring_t));
		hal2->hal2.dac.ringbuf = NULL;
	}

	snd_pcm1_dma_free(pcm1, SND_PCM1_RECORD, hal2->hal2.dac.dmaptr);

#ifdef SND_HAL2_DEBUG
	snd_printk("playback close ok!\n");
#endif
	hal2->usecount--;
}

static void snd_hal2_record_close(snd_pcm1_t *pcm1)
{
	snd_hal2_card_t *hal2;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	if (!hal2) {
		snd_printk("close: no private data?\n");
		return;
	}

	if (hal2->hal2.adc.ringbuf) {
		snd_free_pages((char *) hal2->hal2.adc.ringbuf,
			       hal2->hal2.adc.blocks * sizeof(snd_hal2_ring_t));
		hal2->hal2.adc.ringbuf = NULL;
	}

	snd_pcm1_dma_free(pcm1, SND_PCM1_RECORD, hal2->hal2.adc.dmaptr);

#ifdef SND_HAL2_DEBUG
	snd_printk("record close ok!\n");
#endif
	hal2->usecount--;
}

#if 0
static void snd_hal2_pio_memset(void *s, int c, size_t n)
{
	snd_hal2_reg_t *reg;

	reg = (snd_hal2_reg_t *) (s + n);

	while ((void *) reg-- > s)
		*reg = c;
}
#endif

static void snd_hal2_playback_prepare(snd_pcm1_t *pcm1, unsigned char *buffer,
				      unsigned int size, unsigned int offset,
				      unsigned int count)
{
	int blocks, block_size;
	int sample_size;
	int i;
	int pg;
	snd_hal2_card_t *hal2;
	snd_hal2_ring_t *ring;
	snd_hal2_pbus_t *pbus;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	if (!hal2) {
		snd_printk("prepare: no private data?\n");
		return;
	}

#ifdef SND_HAL2_DEBUG
	snd_printk("playback prepare..\n");
#endif

	/* Recalculate unsupported values of block and block_size..
	 */
	block_size = pcm1->playback.block_size;

	if (block_size > PAGE_SIZE)
		block_size = pcm1->playback.block_size = PAGE_SIZE;

	blocks = pcm1->playback.blocks = pcm1->playback.used_size / block_size;

	if (blocks > PAGE_SIZE / sizeof(snd_hal2_ring_t)) {
		snd_printk("too many blocks: %d\n", blocks);
		return;
	}

#ifdef SND_HAL2_DEBUG
	snd_printk("DAC: blocks: %d block_size: %d size: %u\n",
		   blocks, block_size, size);
#endif

	if (hal2->hal2.dac.ringbuf) {
		snd_free_pages((char *) hal2->hal2.dac.ringbuf,
			       hal2->hal2.dac.blocks * sizeof(snd_hal2_ring_t));
	}

	hal2->hal2.dac.ringbuf = (snd_hal2_ring_t *)
		snd_malloc_pages(blocks * sizeof(snd_hal2_ring_t), &pg, 0);
	if (!hal2->hal2.dac.ringbuf) {
		snd_printd("Oops, no memory for DMA descriptors");
		return;
	}
	hal2->hal2.dac.blocks = blocks;


	/* Setup the ring of DMA descriptors to point to their parts of the DMA
	 * buffer and to generate interrrupts.
	 */
	i = 0;
	ring = hal2->hal2.dac.ringbuf;
	ring->desc.pbuf = PHYSADDR(buffer);
	ring->desc.pnext = PHYSADDR(ring + 1);
	ring->desc.cntinfo = block_size | HPCDMA_XIE;
	while (++i < blocks) {
		ring++;
		ring->desc.pbuf = PHYSADDR(buffer + i * block_size);
		ring->desc.pnext = PHYSADDR(ring + 1);
		ring->desc.cntinfo = block_size | HPCDMA_XIE;
	}
	ring->desc.pnext = PHYSADDR(hal2->hal2.dac.ringbuf);
	
	/* The PBUS can prolly not read thes stuff when it's in the cache so we
	 * have to flush it back to main memory
	 */
	dma_cache_wback_inv((unsigned long) hal2->hal2.dac.ringbuf,
			    blocks * sizeof(snd_hal2_ring_t));


	/* Now we set up some PBUS information. The PBUS needs information about
	 * what portion of the fifo it will use. If it's receiving or
	 * transmitting, and finally whether the stream is little endian or big
	 * endian. The information is written later, on the trigger call.
	 */
	
	sample_size = pcm1->playback.voices * 2;

	pbus = &hal2->hal2.dac.pbus;
	pbus->highwater = (sample_size * 2) >> 1;	/* halfwords */
	pbus->fifobeg = 0;				/* playback is first */
	pbus->fifoend = (sample_size * 4) >> 3;		/* doublewords */
	pbus->ctrl = HPC3_PDMACTRL_RT |
		((pcm1->playback.mode & SND_PCM1_MODE_BIG) ? 0 : HPC3_PDMACTRL_SEL);

#ifdef SND_HAL2_DEBUG
	snd_printk("DAC: mode: %x voices: %d\n", pcm1->playback.mode, pcm1->playback.voices);
#endif

	/* We disable everything before we do anything at all
	 */
	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
	snd_hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
	snd_hal2_i_clearbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));

#if 0
	snd_hal2_pio_memset(hal2->hal2.aes_regs, 0, sizeof(snd_hal2_aes_regs_t));
	snd_hal2_pio_memset(hal2->hal2.syn_regs, 0, sizeof(snd_hal2_syn_regs_t));
#endif

	/* Setup the HAL2 for playback
	 */
	pcm1->playback.real_rate = snd_hal2_compute_rate(hal2, pcm1->playback.rate);
	snd_hal2_set_rate(hal2);
	snd_hal2_i_write16(hal2, H2I_DAC_C1,
			   (pbus->pbusnr << H2I_DAC_C1_DMA_SHIFT) |
			   (1 << H2I_DAC_C1_CLKID_SHIFT) |
			   (pcm1->playback.voices << H2I_DAC_C1_DATAT_SHIFT));
	snd_hal2_i_write32(hal2, H2I_DAC_C2, 0); /* disable muting */

	/* The spec says that we should write 0x08248844 but that's WRONG. HAL2
	 * does 8 bit DMA, not 16 bit even if it generates 16 bit audio.
	 */
	hpc3c0->pbus_dmacfgs[pbus->pbusnr][0] = 0x08208844;	/* Magic :-) */
}

static void snd_hal2_record_prepare(snd_pcm1_t *pcm1, unsigned char *buffer,
				      unsigned int size, unsigned int offset,
				      unsigned int count)
{
	int blocks, block_size;
	int sample_size;
	int i;
	int pg;
	snd_hal2_card_t *hal2;
	snd_hal2_ring_t *ring;
	snd_hal2_pbus_t *pbus;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;

	if (!hal2) {
		snd_printk("prepare: no private data?\n");
		return;
	}

	block_size = pcm1->record.block_size;

	if (block_size > PAGE_SIZE)
		block_size = pcm1->record.block_size = PAGE_SIZE;

	blocks = pcm1->record.blocks = pcm1->record.used_size / block_size;

	if (blocks > PAGE_SIZE / sizeof(snd_hal2_ring_t)) {
		snd_printk("too many blocks: %d\n", blocks);
		return;
	}

#ifdef SND_HAL2_DEBUG
	snd_printk("ADC: blocks: %d block_size: %d size: %u\n",
		   blocks, block_size, size);
#endif

	if (hal2->hal2.adc.ringbuf) {
		snd_free_pages((char *) hal2->hal2.adc.ringbuf,
			       hal2->hal2.adc.blocks * sizeof(snd_hal2_ring_t));
	}

	hal2->hal2.adc.ringbuf = (snd_hal2_ring_t *)
		snd_malloc_pages(blocks * sizeof(snd_hal2_ring_t), &pg, 0);
	if (!hal2->hal2.adc.ringbuf) {
		snd_printd("Oops, no memory for DMA descriptors");
		return;
	}
	hal2->hal2.adc.blocks = blocks;

	/* Setup the ring of DMA descriptors to point to their parts of the DMA
	 * buffer and to generate interrrupts.
	 */
	i = 0;
	ring = hal2->hal2.adc.ringbuf;
	ring->desc.pbuf = PHYSADDR(buffer);
	ring->desc.pnext = PHYSADDR(ring + 1);
	ring->desc.cntinfo = block_size | HPCDMA_XIE;
	while (++i < blocks) {
		ring++;
		ring->desc.pbuf = PHYSADDR(buffer + i * block_size);
		ring->desc.pnext = PHYSADDR(ring + 1);
		ring->desc.cntinfo = block_size | HPCDMA_XIE;
	}
	ring->desc.pnext = PHYSADDR(hal2->hal2.adc.ringbuf);
	
	/* The PBUS can prolly not read thes stuff when it's in the cache so we
	 * have to flush it back to main memory
	 */
	dma_cache_wback_inv((unsigned long) hal2->hal2.adc.ringbuf,
			    blocks * sizeof(snd_hal2_ring_t));

	/* Now we set up some PBUS information. The PBUS needs information about
	 * what portion of the fifo it will use. If it's receiving or
	 * transmitting, and finally whether the stream is little endian or big
	 * endian. The information is written later, on the trigger call.
	 */
	
	sample_size = pcm1->record.voices * 2;

	pbus = &hal2->hal2.adc.pbus;
	pbus->highwater = (sample_size * 2) >> 1;	/* halfwords */
	pbus->fifobeg = (4 * 4) >> 3;			/* record is second */
	pbus->fifoend = (sample_size * 4) >> 3;		/* doublewords */
	pbus->ctrl = HPC3_PDMACTRL_RT | HPC3_PDMACTRL_RCV |
		((pcm1->record.mode & SND_PCM1_MODE_BIG) ? 0 : HPC3_PDMACTRL_SEL);

#ifdef SND_HAL2_DEBUG
	snd_printk("ADC: mode: %x voices: %d\n", pcm1->playback.mode, pcm1->playback.voices);
#endif

	/* We disable everything before we do anything at all
	 */
	pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
	snd_hal2_i_clearbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
	snd_hal2_i_clearbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));

	/* Setup the HAL2 for record
	 */
	pcm1->record.real_rate = snd_hal2_compute_rate(hal2, pcm1->record.rate);
	snd_hal2_set_rate(hal2);
	snd_hal2_i_write16(hal2, H2I_ADC_C1,
			   (pbus->pbusnr << H2I_ADC_C1_DMA_SHIFT) |
			   (1 << H2I_ADC_C1_CLKID_SHIFT));
	snd_hal2_i_write16(hal2, H2I_DAC_C1,
			   (1 << H2I_DAC_C1_CLKID_SHIFT) |
			   (pcm1->record.voices << H2I_DAC_C1_DATAT_SHIFT));
	snd_hal2_i_write32(hal2, H2I_ADC_C2, 0);	/* disable muting */

	/* The spec says that we should write 0x08248844 but that's WRONG. HAL2
	 * does 8 bit DMA, not 16 bit even if it generates 16 bit audio.
	 */
	hpc3c0->pbus_dmacfgs[pbus->pbusnr][0] = 0x08208844;	/* Magic :-) */
}

static void snd_hal2_playback_trigger(snd_pcm1_t *pcm1, int up)
{
	snd_hal2_card_t *hal2;
	snd_hal2_pbus_t *pbus;

#ifdef SND_HAL2_DEBUG
	snd_printk("Trigger %s\n", up ? "up" : "down");
#endif

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	pbus = &hal2->hal2.dac.pbus;

	if (up) {
		unsigned long fifobeg, fifoend, highwater;

		if (!hal2->hal2.dac.ringbuf) {
			snd_printd("Oops, there was no ringbuf to trigger!\n");
			return;
		}

		fifobeg = pbus->fifobeg;
		fifoend = pbus->fifoend;
		highwater = pbus->highwater;

		pbus->pbus->pbdma_dptr = PHYSADDR(hal2->hal2.dac.ringbuf);

		/* We can not activate the PBUS at the same time as we configure
		 * it. We have to write the configuration bits first and then
		 * write both the configuration bits and the activation bits.
		 * If we do not do so, we might get noise in the DMA stream.
		 */
		pbus->ctrl |= ((highwater << 8) | (fifobeg << 16) | (fifoend << 24) | HPC3_PDMACTRL_LD);
		pbus->pbus->pbdma_ctrl = pbus->ctrl;
		pbus->ctrl |= (HPC3_PDMACTRL_ACT);
		pbus->pbus->pbdma_ctrl = pbus->ctrl;

#if 0
		/* Shouldn't this make difference? It works fine without it..
		 */
		if (pcm1->playback.mode & SND_PCM1_MODE_BIG)
			snd_hal2_i_clearbit16(hal2, H2I_DMA_END,
					      H2I_DMA_END_CODECTX);
		else
			snd_hal2_i_setbit16(hal2, H2I_DMA_END,
					    H2I_DMA_END_CODECTX);
#endif

		snd_hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
		snd_hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECTX);
#ifdef SND_HAL2_DEBUG
		snd_hal2_dump_regs(hal2);
#endif
	} else {
		pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
		/* The HAL2 itself may remain enabled safely */
	}
}

static void snd_hal2_record_trigger(snd_pcm1_t *pcm1, int up)
{
	snd_hal2_card_t *hal2;
	snd_hal2_pbus_t *pbus;

#ifdef SND_HAL2_DEBUG
	snd_printk("Trigger %s\n", up ? "up" : "down");
#endif

	hal2 = (snd_hal2_card_t *) pcm1->private_data;
	pbus = &hal2->hal2.adc.pbus;

	if (up) {
		unsigned long fifobeg, fifoend, highwater;

		if (!hal2->hal2.adc.ringbuf) {
			snd_printd("Oops, there was no ringbuf to trigger!\n");
			return;
		}

		fifobeg = pbus->fifobeg;
		fifoend = pbus->fifoend;
		highwater = pbus->highwater;

		pbus->pbus->pbdma_dptr = PHYSADDR(hal2->hal2.adc.ringbuf);

		pbus->ctrl |= ((highwater << 8) | (fifobeg << 16) | (fifoend << 24) | HPC3_PDMACTRL_LD);
		pbus->pbus->pbdma_ctrl = pbus->ctrl;
		pbus->ctrl |= (HPC3_PDMACTRL_ACT);
		pbus->pbus->pbdma_ctrl = pbus->ctrl;

#if 0
		if (pcm1->record.mode & SND_PCM1_MODE_BIG)
			snd_hal2_i_clearbit16(hal2, H2I_DMA_END,
					      H2I_DMA_END_CODECR);
		else
			snd_hal2_i_setbit16(hal2, H2I_DMA_END,
					    H2I_DMA_END_CODECR);
#endif
		snd_hal2_i_setbit16(hal2, H2I_DMA_DRV, (1 << pbus->pbusnr));
		snd_hal2_i_setbit16(hal2, H2I_DMA_PORT_EN, H2I_DMA_PORT_EN_CODECR);
	} else {
		pbus->pbus->pbdma_ctrl = HPC3_PDMACTRL_LD;
		/* The HAL2 itself may safely remain enabled */
	}
}

static unsigned int snd_hal2_playback_pointer(snd_pcm1_t *pcm1,
					      unsigned int used_size)
{
	snd_hal2_card_t *hal2;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;

	return hal2->hal2.dac.pbus.pbus->pbdma_bptr;
}

static unsigned int snd_hal2_record_pointer(snd_pcm1_t *pcm1,
					    unsigned int used_size)
{
	snd_hal2_card_t *hal2;

	hal2 = (snd_hal2_card_t *) pcm1->private_data;

	return hal2->hal2.adc.pbus.pbus->pbdma_bptr;
}

static void snd_hal2_playback_dma(snd_pcm1_t *pcm1, unsigned char *buffer,
				  unsigned int offset, unsigned char *user,
				  unsigned int count)
{
	snd_pcm1_playback_dma(pcm1, buffer, offset, user, count);
	dma_cache_wback_inv((unsigned long) &buffer[offset], count);
}

static void snd_hal2_record_dma(snd_pcm1_t *pcm1, unsigned char *buffer,
				unsigned int offset, unsigned char *user,
				unsigned int count)
{
	dma_cache_inv((unsigned long) &buffer[offset], count);
	snd_pcm1_record_dma(pcm1, buffer, offset, user, count);
}

static void snd_hal2_dma_move(snd_pcm1_t *pcm1,
			      unsigned char *dbuffer,
			      unsigned int dest_offset,
			      unsigned char *sbuffer,
			      unsigned int src_offset,
			      unsigned int count)
{
	snd_pcm1_dma_move(pcm1, dbuffer, dest_offset, sbuffer, src_offset, count);
	dma_cache_wback_inv((unsigned long) &dbuffer[dest_offset], count);
}

static void snd_hal2_playback_dma_neutral(snd_pcm1_t *pcm1,
					  unsigned char *buffer,
					  unsigned int offset,
					  unsigned int count,
					  unsigned char neutral_byte)
{
	snd_pcm1_playback_dma_neutral(pcm1, buffer, offset, count,
				      neutral_byte);
	dma_cache_wback_inv((unsigned long) &buffer[offset], count);
}

struct snd_stru_pcm1_hardware snd_hal2_playback =
{
	NULL,			/* private data */
	NULL,			/* private free */
	SND_PCM1_HW_16BITONLY | SND_PCM1_HW_AUTODMA,	/* flags */
	SND_PCM_FMT_MU_LAW | SND_PCM_FMT_S16_LE |
		SND_PCM_FMT_S16_BE,			/* formats */
	SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE,	/* hardware formats */
	0,
	2,	/* XXX */	/* minimal fragment */
	4000,			/* min. rate */
	48000,			/* max. rate */
	2,			/* max. voices */
	snd_hal2_playback_open,
	snd_hal2_playback_close,
	snd_hal2_playback_ioctl,
	snd_hal2_playback_prepare,
	snd_hal2_playback_trigger,
	snd_hal2_playback_pointer,
	snd_hal2_playback_dma,
	snd_hal2_dma_move,
	snd_hal2_playback_dma_neutral
};

struct snd_stru_pcm1_hardware snd_hal2_record =
{
	NULL,			/* private data */
	NULL,			/* private free */
	SND_PCM1_HW_16BITONLY | SND_PCM1_HW_AUTODMA,	/* flags */
	SND_PCM_FMT_MU_LAW | SND_PCM_FMT_S16_LE |
		SND_PCM_FMT_S16_BE,			/* formats */
	SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE,	/* hardware formats */
	0,
	2,	/* XXX */	/* minimal fragment */
	4000,			/* min. rate */
	48000,			/* max. rate */
	2,			/* max. voices */
	snd_hal2_record_open,
	snd_hal2_record_close,
	snd_hal2_record_ioctl,
	snd_hal2_record_prepare,
	snd_hal2_record_trigger,
	snd_hal2_record_pointer,
	snd_hal2_record_dma,
	snd_hal2_dma_move,
	NULL
};

void snd_hal2_pcm_free(void *private_data)
{
	snd_hal2_card_t *hal2 = (snd_hal2_card_t *) private_data;
	hal2->pcm = NULL;
}

snd_pcm_t *snd_hal2_pcm(snd_hal2_card_t *hal2)
{
	snd_pcm_t *pcm;
	snd_pcm1_t *pcm1;

	pcm = snd_pcm1_new_device(hal2->card, "HAL2");
	if (!pcm)
		return NULL;
	pcm1 = (snd_pcm1_t *) pcm->private_data;
	memcpy(&pcm1->playback.hw,
	       &snd_hal2_playback,
	       sizeof(snd_hal2_playback));
	memcpy(&pcm1->record.hw,
	       &snd_hal2_record,
	       sizeof(snd_hal2_record));
	pcm1->private_data = hal2;
	pcm1->private_free = snd_hal2_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;
	strcpy(pcm->name, "HAL2");
	return hal2->pcm = pcm;
}


/* XXX proc interface? */

#ifndef LINUX_2_1
extern struct symbol_table snd_symbol_table_hal2_export;
#endif

int init_module(void)
{
#ifndef LINUX_2_1
	if (register_symtab(&snd_symbol_table_hal2_export) < 0)
		return -ENOMEM;
#endif
	return 0;
}

void cleanup_module(void)
{
}
