/*
 *  Copyright (c) by Martin Pahl <pahl@tnt.uni-hannover.de>
 *  Private functions for Zefiro ZA2 
 *
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "driver.h"
#include "za2.h"
#include <float.h>
/* ***************************************************************** */
/*          PRIVATE FUNCTIONS                                        */
/* ***************************************************************** */

/* ================================================================= */
/*          local prototype                                          */
/* ================================================================= */
static short  snd_za2_wait_one_cycle(za2_t *codec);
static void   snd_za2_dsp_open(za2_t *codec);
static void   snd_za2_dsp_send(za2_t *codec, 
				unsigned short cmd, 
				unsigned long data);
static short  snd_za2_dsp_close(za2_t *codec);
static void   snd_za2_numode(za2_t *codec, short numode);
static long   snd_za2_calc_samplerate(long nsrate);
static void   snd_za2_add_cmd(za2_t *codec, 
			     unsigned short aCmd, unsigned long aData);
static short snd_za2_terminate_setza(za2_t *codec);

#if 0

STATIC void doit(za2_t *codec, void);
#endif

static void snd_za2_delay(int val)
{
   unsigned long last_jiffies = jiffies;
   val /= 10;
   while(jiffies - val < last_jiffies)
      /* do nothing */ ;
}

/* ------------------------------------------------------------------
** wait_one_cycle
** ------------------------------------------------------------------
*/
static short snd_za2_wait_one_cycle(za2_t *codec)
{
	short erc;
	
	erc = 0;
	while((inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC)&&(erc++<40))
		;
	while((!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC))&&(erc++<80))
		;
	if(erc==80) {
#ifdef SNDCFG_DEBUG
#endif
		printk("wait_one_cycle failed!\n");
		return(1); /* 1 for failed */
	}
	else
		return(0);
}



/* ------------------------------------------------------------------
** dsp_open
** ------------------------------------------------------------------
*/
static void snd_za2_dsp_open(za2_t *codec)
{
	/* raise CS */
	outw(ZA2_BIT_RESET 
	     +ZA2_BIT_CS
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +codec->digin, 
	     ZA2P(codec,COMMAND));  
	snd_za2_wait_one_cycle(codec);

	/* clock it.. 8 times actually */
	outw(0x0, ZA2P(codec,HOSTINPUT));    

	snd_za2_wait_one_cycle(codec);

	/* drop CS again, keep reset hi */
	outw(ZA2_BIT_RESET 
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +codec->digin, 
	     ZA2P(codec,COMMAND));

	snd_za2_wait_one_cycle(codec);

	/* send out read bit and dummy address */
	outw(0x0, ZA2P(codec,HOSTINPUT));    

	snd_za2_wait_one_cycle(codec);

}   /* end OpenDSP */

/* ------------------------------------------------------------------
** SendDSP
** ------------------------------------------------------------------
*/
static void snd_za2_dsp_send(za2_t *codec, 
				 unsigned short cmd, 
				 unsigned long data)
{
#if 1	   
	   	printk("snd_za2_dsp_send: cmd=%d, data=0x%06lx(%ld)\n",
		       cmd,data,data);
#endif	   

	snd_za2_wait_one_cycle(codec);
	outw(0, ZA2P(codec,HOSTINPUT));
	snd_za2_wait_one_cycle(codec);
	outw(0, ZA2P(codec,HOSTINPUT));
	snd_za2_wait_one_cycle(codec);
	outw(0, ZA2P(codec,HOSTINPUT));
	snd_za2_wait_one_cycle(codec);
	outw(cmd, ZA2P(codec,HOSTINPUT));

#if 0
#ifdef SNDCFG_DEBUG
	printk("dsp_sendCmd%d(%lx) -> ",cmd,data);
#endif
#endif
	cmd = ((data&0xff0000)>>16);
	snd_za2_wait_one_cycle(codec);
	outw(cmd, ZA2P(codec,HOSTINPUT));
#if 0
#ifdef SNDCFG_DEBUG
	printk("%02x",cmd);
#endif
#endif
	cmd = ((data&0xff00)>>8);
	snd_za2_wait_one_cycle(codec);
	outw(cmd, ZA2P(codec,HOSTINPUT));
#if 0
#ifdef SNDCFG_DEBUG
	printk("%02x",cmd);
#endif
#endif
	cmd = (data&0xff);
	snd_za2_wait_one_cycle(codec);
	outw(cmd, ZA2P(codec,HOSTINPUT));

#if 0
#ifdef SNDCFG_DEBUG
	printk("%02x\n",cmd);
#endif
#endif

}   /* end SendDSP */

/* ------------------------------------------------------------------
** dsp_close
** ------------------------------------------------------------------
*/
static short snd_za2_dsp_close(za2_t *codec)
{
	unsigned long flags;
	short wait;
	short last;
	short samp;
	short ercflag;
	short r;
	
	ercflag = 0;
       	
	/* be sure we have a clock */
	outw(ZA2_BIT_RESET 
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +codec->digin, 
	     ZA2P(codec,COMMAND));  
	ercflag += snd_za2_wait_one_cycle(codec);

	/* signal end of block...? */
	outw(0x00FF, ZA2P(codec,HOSTINPUT)); 
	ercflag += snd_za2_wait_one_cycle(codec);

	/* signal end of block...? */
	outw(0x00FF, ZA2P(codec,HOSTINPUT));
	ercflag += snd_za2_wait_one_cycle(codec);

	/* raise CS */
	outw(ZA2_BIT_RESET 
	     +ZA2_BIT_CS
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +codec->digin, 
	     ZA2P(codec,COMMAND));  
	ercflag += snd_za2_wait_one_cycle(codec);

	/* clock it.. 8 times actually */
	outw(0x0, ZA2P(codec,HOSTINPUT));    
	ercflag += snd_za2_wait_one_cycle(codec);

	/* drop CS again, keep reset hi */
	outw(ZA2_BIT_RESET 
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +codec->digin, 
	     ZA2P(codec,COMMAND));
	ercflag += snd_za2_wait_one_cycle(codec);

	/* send out read bit and dummy address */
	outw(0x1, ZA2P(codec,HOSTINPUT));    
	ercflag += snd_za2_wait_one_cycle(codec);

	/*  let the PC read info */
	outw(ZA2_BIT_RESET
	     +(codec->za2mode & ZA2_BIT_MODE1) 
	     +ZA2_BIT_MODE0
	     +codec->digin,
	     ZA2P(codec,COMMAND));
	if (ercflag) return(7);  /* 7 means Clock is dead.. */

	wait = 0;
	/* make sure REQ is inactive */
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)) {
		if(wait++>1000)
			return(8);   /* 8 is 'REQ never went inactive' */
		/* wait for host port */
		while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY)  
			;
		/* clock it 8 times */
		outw(0, ZA2P(codec,HOSTINPUT));            
	}

	/* back to normal */
	outw(ZA2_BIT_RESET
	     +codec->za2mode
	     +codec->digin,
	     ZA2P(codec,COMMAND));  

	wait = 0;
	/* wait for REQ to be active */
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ) {
		if(wait++>2000)  /* could be a long wait.. not sure why */
			return(9);     /* 9 is 'REQ never came back!' */
		snd_za2_wait_one_cycle(codec);
	}

	snd_cli(&flags);

	/* wait for the 'za' sync for mode 1 and 3 */
  	if (codec->za2mode&ZA2_BIT_MODE0) {
		wait = 0;
		samp = 0;
		last=(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);

	    	while(samp!=ZA2_ZA_ID) {
			while((inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC)==last)
				;
		    last = (inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
		    samp = (short) inw(ZA2P(codec,SAMPLES));

		    if(wait++>100) {
			    snd_sti(&flags);

			    if (inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)
				    return(3);
			    else
				    return(1);  /* failed! */
		    }
	    }

	    for(r=0;r<16;r++) {
		    while((inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC)==last)
			    ;
		    last = (inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
		    if( ((unsigned short) inw(ZA2P(codec,SAMPLES))) 
			!= ZA2_ZA_ID) {
			    snd_sti(&flags);
			    
			    if (inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)
				    return(4);
			    else
				    return(2);  /* failed! */
		    }
	    }
	}

	snd_sti(&flags);
#if 0   
	printk("za2_dsp_close: id=0x%x\n",samp);
#endif   
	return(0);
}   /* end dsp_close */

static void snd_za2_setenv(za2_t *codec)
{
	codec->command=ZA2_BIT_BOOT 
		+ ZA2_BIT_RESET 
		+ codec->za2mode
		+ codec->digin
		+ (codec->irq-9)*ZA2_BIT_IRQSEL0
		+ (codec->dma_in16-4)*ZA2_BIT_IDMASEL0
		+ (codec->dma_out16-4)*ZA2_BIT_ODMASEL0;
   printk("za2_setenv: irq=%d dma_in=%d dma_out=%d command=%x\n",
	  codec->irq,codec->dma_in16,codec->dma_out16,codec->command);
}

/* ------------------------------------------------------------------
** snd_za2_numode
** ------------------------------------------------------------------
*/
static void snd_za2_numode(za2_t *codec, short numode)
{
	long stillLoop = 0;
	long MAX_LOOP  = 10000;
	
	if ((codec->za2mode&ZA2_BIT_MODE1)!=(numode&ZA2_BIT_MODE1)) {
		outw(ZA2_BIT_RESET
		     +(numode&ZA2_BIT_MODE1)
		     +ZA2_BIT_MODE0
		     +codec->digin,
		     ZA2P(codec,COMMAND));
		snd_za2_wait_one_cycle(codec);
		snd_za2_wait_one_cycle(codec);

		/* FIX_ME */
		/* wait for REQ to come back.. if gone */
		while((inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)
		      &&(stillLoop++ < MAX_LOOP))
			;
	}
	outw(ZA2_BIT_RESET+numode+codec->digin,ZA2P(codec,COMMAND));

	codec->za2mode = numode;
	codec->command &= ZA2_NBITS_MODE;
	codec->command += codec->za2mode;
}  /* end NuZA2Mode */


/* ------------------------------------------------------------------
** calc_samplerate
**
** Description: Here come a little math ! we are searching for
**              the ratio of "sample rate desired / 96000". But
**              we want to put this ratio in a rationnal format,
**              i.e. "ratio = unsigned A / unsigned B"
** ------------------------------------------------------------------
*/

static long snd_za2_calc_samplerate(long nsrate)
{
	unsigned short r,z;
	long cm1;
	long div,mlt;
	float nuy, besty;
	float nux;
	short bestz = 0;
	short bestr = 0;


	besty = 9.9;
	for(r = 1; r < 512; r++) {
		nux = ((float) nsrate / 1000.0) / (96.0/(float)r);
		nuy = nux-(float)((short)nux);

		z = (short)nux;
		if(nuy > 0.5)
		{
			nuy = 1.0 - nuy;
			z = (short)(nux+1.0);
		}

		if((z>1)&&(r>1))
		{
			nuy = ((float) nsrate / 1000.0) 
				- (96.0/(float)r*(float)z);
			if (nuy<0) nuy *=-1.0;
			if (nuy<besty)
			{
				besty = nuy;
				bestr = (long)r;
				bestz = (long)z;
			}
		}
	}

	div = bestr;
	mlt = bestz;

	cm1 = ((div-1)<<10)+mlt-1;
#if 1
   printk("snd_za2_calc_samplerate: srate=%ld, div=%ld, mlt=%ld besty=%f\n",
	  nsrate,div,mlt,besty);
#endif   
	return(cm1);

}   /* end calc_samplerate */


/* ==================================================================== */
/* add_cmd                                                               */
/* ==================================================================== */
static void snd_za2_add_cmd(za2_t *codec,
			   unsigned short aCmd, unsigned long aData)
{
	if (codec->cmdcnt<ZA2_MAX_CMD_BUFFER) {
		codec->cmd[codec->cmdcnt] = aCmd;
		codec->data[codec->cmdcnt] = aData;
		codec->cmdcnt++;
#if 1	   
	   	printk("snd_za2_add_cmd: cmd=%d, data=0x%06lx(%ld), count=%d\n",
		       aCmd,aData,aData,codec->cmdcnt);
#endif	   
	}
	else
	{
        printk("snd_za2_add_cmd: Command-Buffer overflow cmd=%d, data=%ld\n",
               aCmd, aData);
	}
}

/* ***************************************************************** */
/*          PUBLIC FUNCTIONS                                         */
/* ***************************************************************** */

void snd_za2_SetDebugFlag(za2_t *codec)
{
	codec->debug_flag = 1;
}

void snd_za2_UnsetDebugFlag(za2_t *codec)
{
	codec->debug_flag = 0;
}

/* ------------------------------------------------------------------
** doit
** ------------------------------------------------------------------
*/

static void snd_za2_doit(za2_t *codec, 
		 const unsigned char *fpga_bit_stream,
		 short *read_pos, 
		 short *bit_count)
{
	/* lookup table */
	static const unsigned char lookup[8] = {128, 64, 32, 16, 8, 4, 2, 1};
                                            
	/* put current bit of current value */
	if(fpga_bit_stream[*read_pos]&lookup[*bit_count]) {
		if (codec->debug_flag)
			printk("1");
		outb(0x02, ZA2P(codec,XILINX));
	}
	else
	{
		if (codec->debug_flag)
			printk("0");
		outb(0x0, ZA2P(codec,XILINX));
	}
	
	/* next value */
	if((*bit_count)++ > 6)
	{
		*bit_count = 0;
		(*read_pos)++;
	}
}

/* -------------------------------------------------------------------------
** upbit
** -------------------------------------------------------------------------
*/
short snd_za2_upbit(za2_t *codec, 
		    const unsigned char *fpga_bit_stream)
{
	short i, j;
	short err;
	short read_pos;
	short bit_count;

	snd_za2_setenv(codec);
	
	outb(2, ZA2P(codec,XILINX));   /* turn off the reset */
	snd_za2_delay(10);
	     
	/* =========================================== */
	/*  Verify the validity of bit file            */
	/* =========================================== */
	if ( (fpga_bit_stream[6]!=0xf0) ||
	     (fpga_bit_stream[7]!=0x0f) ||
	     (fpga_bit_stream[8]!=0x00) ||
	     (fpga_bit_stream[9]!=0x33) ||
	     (fpga_bit_stream[10]!=0x30) ) {
		printk("snd_za2_upbit: fpga bit stream is not a valid!\n");
		return(ZA2_ERR_BAD_BIT_FILE); /* error */
	}

	if (fpga_bit_stream[11]!=0x33)
		return(ZA2_ERR_CANT_CONFIG_ZA2_WITH_ZA1); /* error */

	/* ============================================ */
	/*  Printing hardware setup                     */
	/* ============================================ */

	if( ((short) inw(ZA2P(codec,STATUS))) != -1)
		return(ZA2_ERR_ALREADY_INITIALIZED); /* error */

	/* ============================================ */
	/*  Printing actual setting                     */
	/* ============================================ */

	if(codec->debug_flag)
		switch(codec->command&(ZA2_BIT_INPSEL0+ZA2_BIT_INPSEL1))
		{
		case 0: printk("Using fiber optic input\n"); break;
		case 1: printk("Using coaxial input\n"); break;
		case 2: printk("Using AES/EBU input\n"); break;
		case 3: printk("Using AES/EBU input\n"); break;
		}

	/* ============================================ */
	/*  Loading the FPGA matrice                    */
	/* ============================================ */

	read_pos = 118;
	bit_count = 0;

	for (i=0;i<8;i++)
		snd_za2_doit(codec,fpga_bit_stream,&read_pos,&bit_count);
	if (codec->debug_flag)
		printk("\n");

	for (i=0;i<4;i++)
		snd_za2_doit(codec,fpga_bit_stream,&read_pos,&bit_count);
	if (codec->debug_flag)
		printk("\n");

	for(i=0;i<24;i++)
		snd_za2_doit(codec,fpga_bit_stream,&read_pos,&bit_count);
	if(codec->debug_flag)
		printk("\n");

	for(i=0;i<4;i++)
		snd_za2_doit(codec,fpga_bit_stream,&read_pos,&bit_count);
	if(codec->debug_flag)
		printk("\n");

	for(j=0;j<241;j++) {  /* was 197 for 3020 */
		for(i=0;i<46;i++)   /* was 75 for 3020 */
			snd_za2_doit(codec,fpga_bit_stream,
				     &read_pos,&bit_count);
		if (codec->debug_flag)
			printk("\n                 ");
		
		for(i=0;i<46;i++)   /* was 75 for 3020 */
			snd_za2_doit(codec,fpga_bit_stream,
				     &read_pos,&bit_count);
		if(codec->debug_flag)
			printk("\n");
	}

	for(i=0;i<16;i++)
		snd_za2_doit(codec,fpga_bit_stream,&read_pos,&bit_count);
	if (codec->debug_flag)
		printk("\n");

	snd_za2_delay(100);

	if( ((short) inw(ZA2P(codec,STATUS))) == -1)
		err = ZA2_ERR_DID_NOT_CONFIG_CORRECTLY;
	else
	{
		err = ZA2_ERR_SUCCES;
	}

	outw(codec->command&ZA2_BITS_INIT,ZA2P(codec,COMMAND));

	return err; /* succes */
}


#ifdef NOT_USED
/* ================================================================== */
/*  Function: za2_bit_check                                           */
/*                                                                    */
/*  Description: sanity check for the za2 "hardware" config           */
/* ================================================================== */
static short snd_za2_bit_check(void)
{
	if( ((short) inw(ZA2P(codec,STATUS))) == -1)
		return ZA2_ERR_NO_HARDWARE_CONFIG;
	return 0;
}
#endif

#ifdef USELESS
/* ================================================================== */
/*  Function: open_sim                                                */
/*                                                                    */
/*  Description: open a channel for uploading the micro-code into DSP */
/* ================================================================== */
STATIC short open_sim(void)
{
   psize          = 0;
   bit_count      = 0;
   currentReadPos = 0;
   strcpy(programName,"unknow");
   return 0; /* succes */
}

#endif

#ifdef USELESS
/* ================================================================== */
/*  Function: write_sim                                               */
/*                                                                    */
/*  Description: put "len" bytes into the DSP micro-code buffer.      */
/* ================================================================== */
STATIC short write_sim(const unsigned char * data, size_t len)
{
   if( (psize + len) < MAX_DSP_BUFFER)
   {
      memcpy( &(program[psize]), data, len);
      psize += len;
      return len;
   }
   return ZA2_SIM_OUT_OF_SPACE; /* out of space */
}
#endif

/* ================================================================= */
/*  Function: upsim                                                  */
/*                                                                   */
/*  Description: Function used to upload the DSP code into the card  */
/* ================================================================= */

static short snd_za2_upsim(za2_t *codec)
{
	unsigned long flags;
	unsigned short r      = 0;
	unsigned short adrs   = 0;
	unsigned short length = 0;

	long tcheck   = 0;
	long checksum = 0;
	short  val      = 0;
	short  pp       = 0;

	if (!codec->dspos)
		return ZA2_ERR_SIM_OUT_OF_SPACE;
	printk("ZA2: snd_za2_upsim: name=%s, psize=%d\n",
	       codec->dspos->name,codec->dspos->psize);
	if (codec->dspos->psize>ZA2_MAX_DSP_PRG_SIZE)
		return ZA2_ERR_SIM_OUT_OF_SPACE; /* out of space */
	/* calculate "command" variable */
	snd_za2_setenv(codec);

	/* verify integrity of programmable hardware */
	if( ((short) inw(ZA2P(codec,STATUS))) == -1)
		return ZA2_ERR_NOT_INITIALIZED;

	/* ------------------------------------------------------- */
	/* reset the DSP                                           */
	/* ------------------------------------------------------- */

	if(codec->debug_flag)
		printk("reset\n");

	/* raise BOOT */
	outw(ZA2_BIT_RESET+ZA2_BIT_CS+ZA2_BIT_BOOT, ZA2P(codec,COMMAND));
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* select SPI mode, drop CS */
	outw(ZA2_BIT_BOOT+ZA2_BIT_RESET, ZA2P(codec,COMMAND));
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* drop RESET */
	outw(ZA2_BIT_BOOT, ZA2P(codec,COMMAND) );
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* raise RESET, initiate download */
	outw(ZA2_BIT_BOOT+ZA2_BIT_RESET, ZA2P(codec,COMMAND));
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* drop BOOT */
	outw(ZA2_BIT_RESET, ZA2P(codec,COMMAND));
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* pause */

	snd_za2_delay(100);
	
	
	/* ------------------------------------------------------- */
	/* begin of download process                               */
	/* ------------------------------------------------------- */
	
	if(codec->debug_flag)
		printk("start\n");

	pp = 0;  /* reset counter */
	
	outw(ZA2_BIT_CS+ZA2_BIT_RESET, ZA2P(codec,COMMAND));  /* raise CS */

	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));
	outw(0, ZA2P(codec,HOSTINPUT));    /* clock it.. 8 times actually */
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* drop CS again, keep reset hi */
	outw(ZA2_BIT_RESET, ZA2P(codec,COMMAND));  

	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	/* send out write bit and dummy address */
	outw(0, ZA2P(codec,HOSTINPUT));       
	while(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC);
	while(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_FSYNC));

	checksum = 0;

	snd_cli(&flags);

	while(pp<codec->dspos->psize) {
		val = codec->dspos->program[pp++];
		while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
		outw(val, ZA2P(codec,HOSTINPUT));
		adrs=val*256;

		val = codec->dspos->program[pp++];
		while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
		outw(val, ZA2P(codec,HOSTINPUT));
		adrs+=val;

		if(adrs==0xffff) {
			if(codec->debug_flag)
				printk("Rep  Checksum: ");

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			if(codec->debug_flag)
				printk("%02x",val);

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			if(codec->debug_flag)
				printk("%02x",val);

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			
			if(codec->debug_flag)
				printk("%02x\n",val);
			
			if(codec->debug_flag)
				printk("Calc Checksum: %0lx\n",
				       checksum&0xffffff);
			snd_za2_wait_one_cycle(codec);
			if(!(inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)) {
				outw(codec->command&ZA2_BITS_INIT, 
				     ZA2P(codec,COMMAND));
				outw(0, ZA2P(codec,SAMPLES));
				
				snd_sti(&flags);

				return ZA2_ERR_DOWNLOAD_FAILED; /* error */
			}

			if (strstr(codec->dspos->name,"48244")) {
				printk("48Khz ");
				snd_za2_numode(codec,
						 ZA2_BIT_MODE0|ZA2_BIT_MODE1);
				codec->filter = 1;
				printk("to 44.1Khz resampler loaded\n");
			} /* default, DSP OS */
			else if((strstr(codec->dspos->name,"DSPOS"))
				||(strstr(codec->dspos->name,"dspos"))) {
				if(codec->debug_flag)
					printk("DSP ");
				snd_za2_numode(codec,ZA2_BIT_MODE0);
				codec->filter = 0;
				if (codec->debug_flag)
					printk("Operating System loaded\n");
			}
			else {
				if(codec->debug_flag)
					printk("User program: ");
				snd_za2_numode(codec,ZA2_BIT_MODE0);
				codec->filter = 0;
				if(codec->debug_flag)
					printk("%s loaded (Mode 1)\n",
					       codec->dspos->name);
			}

			codec->za2volume = 16384;
			codec->srate = 48000;
			/*! snd_za2_ZaPutEnv(codec); !*/
			
			outw(codec->command&ZA2_BITS_INIT,
			     ZA2P(codec,COMMAND));
			outw(0, ZA2P(codec,SAMPLES));

			snd_sti(&flags);

			return 0; /* succes */
		} /* if adrs==0xffff */

		if(codec->debug_flag)
			printk("Adrs %x\n",adrs);
		checksum+=(long)adrs;


		val = codec->dspos->program[pp++];
		while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
		outw(val, ZA2P(codec,HOSTINPUT));
		length=val*256;


		val = codec->dspos->program[pp++];
		while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
		outw(val, ZA2P(codec,HOSTINPUT));
		length+=val;
		checksum+=(long)length;
		length= length/3;

		if(codec->debug_flag)
			printk("Length %x\n",length);

		for (r=0;r<length;r++) {
			if(codec->debug_flag)
				printk("%04x: ",adrs+r);

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			if (codec->debug_flag)
				printk("%02x",val);
			tcheck=(long)val*65536;

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			if(codec->debug_flag)
				printk("%02x",val);
			tcheck+=(long)val*256;

			val = codec->dspos->program[pp++];
			while(inw(ZA2P(codec,STATUS))&ZA2_BIT_PCHOSTRDY);
			outw(val, ZA2P(codec,HOSTINPUT));
			if(codec->debug_flag)
				printk("%02x\n",val);
			tcheck+=(long)val;
			checksum += tcheck;
		} /* for */
	} /* while */

	snd_sti(&flags);

	return ZA2_ERR_UPSIM_UNKNOW_ERROR;
}


static int snd_za2_initialized(za2_t *codec)
{
  return (!(inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ));
}

/* ******************************************************************* */
/*               SETTING FUNCTIONS (SETZA)                             */
/* ******************************************************************* */

#ifdef NOT_NEEDED
/* ==================================================================== */
/* zaSetDmaIn                                                           */
/* ==================================================================== */
static short snd_za2_zaSetDmaIn(za2_t *codec)
{
      codec->command&=za2_NBITS_IDMA;
      codec->command+=(codec->dma_in16-4)*ZA2_BIT_IDMASEL0;
      return 0;
}

/* ==================================================================== */
/* zaSetDmaOut                                                          */
/* ==================================================================== */
static void snd_za2_zaSetDmaOut(za2_t *codec)
{
      codec->command&=ZA2_NBITS_ODMA;
      codec->command+=(codec->dma_out16-4)*ZA2_BIT_ODMASEL0;
}

/* ==================================================================== */
/* zaSetIrq                                                             */
/* ==================================================================== */
static void snd_za2_zaSetIrq(za2_t *codec)
{
      codec->command&=ZA2_NBITS_IRQ;
      codec->command+=(codec->irq-9)*ZA2_BIT_IRQSEL0;
}
#endif /* NOT_NEEDED */
#ifdef USELESS
/* ==================================================================== */
/* zaSetMode                                                            */
/* ==================================================================== */
STATIC short snd_za2_set_mode(za2_t *codec, int mode)
{
	switch(mode)
	{
	case ZA2_MODE_FREE_PLAY:
		snd_za2_numode(codec,mode);
		snd_za2_add_cmd(codec,ZA2_SYNC_OUTPUT, 1);
		codec->isok=1;
		break;

	case ZA2_MODE_SYNC_PLAY:
		snd_za2_numode(codec->za2mode);
		codec->srate = 48000;
		snd_za2_add_cmd(codec,ZA2_REG_SAMPLE_RATE, 0x1402);
		snd_za2_add_cmd(codec,ZA2_REG_SYNC_OUTPUT, 1);
		break;

	case ZA2_MODE_PASS_THRU:
		snd_za2_numode(codec->za2mode);
		if(codec->srate!=48000) {
			codec->srate = 48000;
			snd_za2_add_cmd(codec,ZA2_REG_SAMPLE_RATE, 0x1402);
			/*! resetflag = 1; ?!*/
		}
		snd_za2_add_cmd(codec,ZA2_REG_SYNC_OUTPUT, 1);
		break;

	default:
		return ZA2_ERR_UNKNOW_MODE; /* error: unknow mode */
	}
	return 0; /* succes */
}
#endif

/* ==================================================================== */
/* set_samplerate                                                    */
/* ==================================================================== */
static short snd_za2_set_samplerate(za2_t *codec)
{
	if( (codec->srate < 20000) || (codec->srate > 60000) )
		return ZA2_ERR_VALUE_OUT_OF_BOUND;

	snd_za2_add_cmd(codec, ZA2_REG_SAMPLE_RATE, 
		       snd_za2_calc_samplerate(codec->srate) );
    
	codec->resetflag = 1;
	codec->cbitflag = 1;
	return 0;
}

/* ==================================================================== */
/* zaSetInput                                                           */
/*                                                                      */
/* Note: seems to be independant of other settings                      */
/*       but cannot be call while recording                             */
/* ==================================================================== */
#if 0
static short snd_za2_set_input(za2_t *codec)
{
	long stillLoop = 0;
	long MAX_LOOP  = 10000;
	unsigned short digin;

	digin=codec->command & ZA2_BITS_INPUT;
	codec->command&=ZA2_NBITS_INPUT;
	codec->command|=codec->digin;

	if(digin!=codec->digin)	{
		outw(codec->command&ZA2_BITS_INIT, ZA2P(codec,COMMAND));
		snd_za2_delay(100);   /* wait for sync */
		while((inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)
		      &&(stillLoop++ < MAX_LOOP))
			;
	}
	codec->isok  = 1;

	return 0; /* succes */
}
#endif

#ifdef USELESS
/* ==================================================================== */
/* zaSetOutputEnable                                                    */
/*                                                                      */
/* Note: seems to be independant of other settings                      */
/*       but cannot be call while recording                             */
/* ==================================================================== */
STATIC short zaSetOutputEnable(short status)
{
   enableOut = status;
   return 0;
}
#endif

/* ==================================================================== */
/* zaSetVolume                                                          */
/* ==================================================================== */
#if 0
static short snd_za2_set_volume(za2_t *codec)
{
   if (codec->volume == 100)
      codec->za2volume=16384;
   else
      codec->za2volume = codec->volume * 164;
   snd_za2_add_cmd(codec,ZA2_REG_VOLUME, ((long) codec->za2volume << 8));
   return 0;
}
#endif

/* ==================================================================== */
/* zaSendStartId                                                        */
/* ==================================================================== */
#if 0
static short snd_za2_send_startid(za2_t *codec)
{
   snd_za2_add_cmd(codec,za2_REG_START_ID, 300);

   return 0;
}
#endif

#ifdef USELESS
/* ==================================================================== */
/* zaSetProtocol                                                        */
/* ==================================================================== */
static short zaSetProtocol(short protocol)
{
	return 0;
}


/* ==================================================================== */
/* zaSetEmph                                                            */
/* ==================================================================== */
STATIC short zaSetEmph(short newEmph)
{
   if(newEmph)
      emph = 1;
   else
      emph = 0;

   dirty = 1;
   cbitflag = 1; /* update cbits stream */
   return 0;
}

/* ==================================================================== */
/* zaSetSCMS                                                            */
/* ==================================================================== */
STATIC short zaSetSCMS(short newScms)
{
   if(newScms)
      scms = 1;
   else
      scms = 0;

   dirty = 1;
   cbitflag = 1; /* update cbits stream */

   return 0;
}
#endif

#ifdef USELESS
/* ==================================================================== */
/* init_setza                                                           */
/* ==================================================================== */
static void snd_za2_init_setza(void)
{
   isok      = 0;
#if 0
   dirty     = 0;
#endif
   resetflag = 0;
   cmdcnt    = 0;
   cbitflag  = 0;
}
#endif

#ifdef NEW
#endif
/* ==================================================================== */
/* terminate_setza                                                      */
/* ==================================================================== */
static short snd_za2_terminate_setza(za2_t *codec)
{
	short  ix;          /* for loop */
	short  err = 0;     /* respond from send command (sync or not) */
	long tmpData = 0; /* temporary variable */
	
	/* ------------------------------------------------------------- */
	/* if going to playback mode, or switching inputs we'll          */
	/* give the benefit of the doubt and not complain about no clock */
	/* ------------------------------------------------------------- */
	
	if ((inw(ZA2P(codec,STATUS))&ZA2_BIT_REQ)&&(!codec->isok))
		return ZA2_ERR_HAS_NO_DSP_OS;
	
	/* -------------------------------------------- */
	/* if we need to change the "cbits" data stream */
	/* -------------------------------------------- */
	if(codec->cbitflag)
	{
		/* ------------------------------------------ */
		/* if the input is enable (mode 2 or 3), then */
		/* check sampling rate and update state var.  */
		/* ------------------------------------------ */
		if (codec->za2mode&ZA2_BIT_MODE1) {
			switch(inw(ZA2P(codec,STATUS)) & ZA2_BITS_FREQ) {
			case 0x000:
				codec->srate = 48000;
				break;
			case 0x010:
				codec->srate = 48000;
				break;
			case 0x020:
				codec->srate = 44100;
				break;
			case 0x030:
				codec->srate = 32000;
				break;
			case 0x800:
				codec->srate = 48000;
				break;
			case 0x810:
				codec->srate = 44100;
				break;
			case 0x820:
				codec->srate = 44056;
				break;
			case 0x830:
				codec->srate = 32000;
				break;
			}
		}
		
		/* if we are with AES/EBU protocol */
		if(codec->aeso) {
			snd_za2_add_cmd(codec,ZA2_REG_USER_BIT, 0); /* no user bits in AES */
			
			tmpData = 0x000300;
			if((codec->srate>=44000)&&(codec->srate<44200))
				tmpData += 0x300000;    /* label as 44.1khz */
			else if(codec->srate<44000)
				tmpData += 0xf00000;    /* label as 32khz */
			else
				tmpData += 0xc00000;    /* label as 48khz */
			if(codec->emph)
				tmpData += 0x00f000;   /* with emphasis */
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_0, tmpData);     /* pro mode */
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_1, 0);
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_2, 0);
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_3, 0);
		} else { /* it's SPDIF protocol */
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_0, 
					0x003000*(1-codec->scms) 
					+ 0x00c000*codec->emph);
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_1, 0xc00f00);   /* original DAT source */
			snd_za2_add_cmd(codec,ZA2_REG_CBITS_2, 0x000000);   /* source/channel num */
			if((codec->srate>=44000)&&(codec->srate<44200)) {
				snd_za2_add_cmd(codec,ZA2_REG_USER_BIT, 1323);
				snd_za2_add_cmd(codec,ZA2_REG_CBITS_3, 0x000000);    /* label as 44.1khz */
			}
			else if(codec->srate<44000) {
				snd_za2_add_cmd(codec,ZA2_REG_USER_BIT, 1920);
				snd_za2_add_cmd(codec,ZA2_REG_CBITS_3, 0x000f00);   /* label as 32khz */
			}
			else {
				snd_za2_add_cmd(codec,ZA2_REG_USER_BIT, 1440);
				snd_za2_add_cmd(codec,ZA2_REG_CBITS_3, 0x000c00);   /* label as 48khz */
			}
		} /* else of aeso */
	} /* if cbitflag */
	/* ------------------------------------- */
	/* if there a command to send to the DSP */
	/* ------------------------------------- */
	if(codec->cmdcnt) {
		/* if a reset is required */
		if(codec->resetflag) {
			snd_za2_dsp_open(codec);
			snd_za2_dsp_send(codec, ZA2_REG_DAC_STATE, 0x9001);  /* disable DAC */
			for(ix = 0; ix < codec->cmdcnt; ix++)
				snd_za2_dsp_send(codec, 
						  codec->cmd[ix], 
						  codec->data[ix]);
			snd_za2_dsp_send(codec, ZA2_REG_SOFT_RESET, 1); /* soft reset? */
			snd_za2_dsp_send(codec, ZA2_REG_DO_IT, 0xffffff);  /* do it.. */
			snd_za2_delay(10);
			for(ix = 0; ix < 8000; ix++)
				snd_za2_wait_one_cycle(codec);
			
#if 0
		   	if(!(codec->state&(ZA2_STATE_RECORD_RUNNING
					   |ZA2_STATE_PLAYBACK_RUNNING))) {
				snd_za2_dsp_open(codec);
				snd_za2_dsp_send(codec, ZA2_REG_DAC_STATE, 0xb001);  /* enable DAC */
				err = snd_za2_dsp_close(codec);
			}
#endif
		}
		else {
			snd_za2_dsp_open(codec);
			for(ix = 0; ix < codec->cmdcnt; ix++)
				snd_za2_dsp_send(codec, 
						  codec->cmd[ix], 
						  codec->data[ix]);
			if(!(codec->state&(ZA2_STATE_RECORD_RUNNING
					   |ZA2_STATE_PLAYBACK_RUNNING)))
				err = snd_za2_dsp_close(codec);
		}
	        codec->cmdcnt=0;
	} /* if cmdcnt */

	if(!(codec->state&(ZA2_STATE_RECORD_RUNNING
					   |ZA2_STATE_PLAYBACK_RUNNING)))
		outw(codec->command&ZA2_BITS_INIT,ZA2P(codec,COMMAND));

  return err;
} /* setza */

#ifdef USELESS
/* ******************************************************************* */
/*                    STATE VARIABLE QUERIES                           */
/* ******************************************************************* */

/* ------------------------------------ */
/*  zaGetBaseAddr                       */
/* ------------------------------------ */
STATIC short zaGetBaseAddr(void)
{
   return base;
}

/* ------------------------------------ */
/*  zaGetZaNum                          */
/* ------------------------------------ */
STATIC short zaGetZaNum(void)
{
   return 2;
}

/* ------------------------------------ */
/*  zaGetDmaIn                          */
/* ------------------------------------ */
STATIC short zaGetDmaIn(void)
{
   return dmi;
}

/* ------------------------------------ */
/*  zaGetDmaOut                         */
/* ------------------------------------ */
STATIC short zaGetDmaOut(void)
{
   return dmo;
}

/* ------------------------------------ */
/*  zaGetIrq                            */
/* ------------------------------------ */
STATIC short zaGetIrq(void)
{
   return irq;
}

/* ------------------------------------ */
/*  zaGetMode                           */
/* ------------------------------------ */
STATIC short zaGetMode(void)
{
   return za2mode;
}

/* ------------------------------------ */
/*  zaGetSamplingRate                   */
/* ------------------------------------ */
STATIC long zaGetSamplingRate(void)
{
   if(za2mode > ZA2_FREE_PLAY_MODE)
   {
      sratecode = (unsigned short) inw(ZA2P(codec,STATUS));
      sratecode &= 0x830 + filter;
      switch(sratecode)
      {
      case 0x000:
         if(codec->debug_flag)
            printk("Out of range.. ");
         srate = 48000;
         break;
      case 0x010:
         if(codec->debug_flag)
            printk("48Khz (rough) ");
         srate = 48000;
         break;
      case 0x020:
         if(codec->debug_flag)
            printk("44.1Khz (rough) ");
         srate = 44100;
         break;
      case 0x030:
         if(codec->debug_flag)
            printk("32Khz (rough) ");
         srate = 32000;
         break;
      case 0x800:
         if(codec->debug_flag)
            printk("48Khz ");
         srate = 48000;
         break;
      case 0x810:
         if(codec->debug_flag)
            printk("44.1Khz ");
         srate = 44100;
         break;
      case 0x820:
         if(codec->debug_flag)
            printk("44.056Khz ");
         srate = 44056;
         break;
      case 0x830:
         if(codec->debug_flag)
            printk("32Khz ");
         srate = 32000;
         break;
      default:
         if(codec->debug_flag)
            printk("44.1khz resampled from 48 (we assume) ");
         srate = 44100;
         break;
      }
   }
   return srate;
}

/* ------------------------------------ */
/*  zaGetSamplingRate                   */
/* ------------------------------------ */
STATIC short zaGetSRateCode(void)
{
   zaGetSamplingRate();
   return sratecode;
}


/* ------------------------------------ */
/*  zaGetInputType                      */
/* ------------------------------------ */
STATIC short zaGetInputType(void)
{
   return digin;
}

/* ------------------------------------ */
/*  zaGetOutputEnable                   */
/* ------------------------------------ */
STATIC short zaGetOutputEnable(void)
{
   return enableOut;
}

/* ------------------------------------ */
/*  zaGetVolume                         */
/* ------------------------------------ */
STATIC short zaGetVolume(void)
{
   return volume;
}

/* ------------------------------------ */
/*  zaGetProtocol                       */
/* ------------------------------------ */
STATIC short zaGetProtocol(void)
{
   return aeso;
}

/* ------------------------------------ */
/*  zaGetEmph                           */
/* ------------------------------------ */
STATIC short zaGetEmph(void)
{
   return emph;
}

/* ------------------------------------ */
/*  zaGetSCMS                           */
/* ------------------------------------ */
STATIC short zaGetSCMS(void)
{
   return scms;
}

/* ------------------------------------ */
/*  zaGetFilter                         */
/* ------------------------------------ */
STATIC short zaGetFilter(void)
{
   return filter;
}

/* ------------------------------------ */
/*  zaGetCommand                        */
/* ------------------------------------ */
STATIC short zaGetCommand(void)
{
   return command;
}
#endif /* USELESS */

