/***************************************************************************
*	NAME:  DMA.C
**	COPYRIGHT:
**	"Copyright (c) 1992, by FORTE
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
*  CREATION DATE: 11/18/92
*--------------------------------------------------------------------------*
*     VERSION	DATE	   NAME		DESCRIPTION
*>	1.0	11/18/92		Original
***************************************************************************/

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <dos.h>

#include "forte.h"
#include "gf1hware.h"
#include "extern.h"
#include "gf1os.h"
#include "dma.h"	/* Hardware defs for PC's dma controllers */
#include "gf1proto.h"
#include "osproto.h"
#include "ultraerr.h"

extern DMA_ENTRY _gf1_dma[]; /* Structure that holds data on PC's dma chan */
extern ULTRA_DATA _gf1_data; /* OS data structure */

/***************************************************************
 * This function will start up a DMA transfer to the UltraSound
 ***************************************************************/

void
UltraStartDramDma(unsigned char control)/* bits for card (see gf1hware.h) */
{
/* make sure only right bits are on. Ignore others. */
control = control & (DMA_READ|DMA_16|DMA_CVT_2);

/* Set xfer speed. */
control = control | DMA_R0;		/* FAST ..... */

control = control | (DMA_ENABLE|DMA_IRQ_ENABLE);	/* Enable & turn IRQ on */

if (_gf1_data.dram_dma_chan >= 4)
	control |= DMA_WIDTH_16;				/* 16 bit dma channel */

/* DMA xfer. begins here. */
ENTER_CRITICAL;

outp(_gf1_data.reg_select,DMA_CONTROL);
outp(_gf1_data.data_hi,control);

LEAVE_CRITICAL;

}

void
UltraStartRecordDma(unsigned char control)
{
/* make sure only right bits are on. Ignore others. */
control = control & (ADC_MODE|ADC_TWOS_COMP);

control = control | (ENABLE_ADC|ADC_IRQ_ENABLE);	/* Enable & turn IRQ on */

if (_gf1_data.adc_dma_chan >= 4)
	control |= ADC_DMA_WIDTH;				/* 16 bit dma channel */

/* DMA xfer. begins here. */
ENTER_CRITICAL;

outp(_gf1_data.reg_select,SAMPLE_CONTROL);
outp(_gf1_data.data_hi,control);

LEAVE_CRITICAL;
}

void
UltraDmaNext(DMA_ENTRY *tdma,int record)
{
unsigned int tcount;

	tdma->flags &= ~TWO_FLAG;				/* shut off rollover */
	tdma->amnt_sent = tdma->cur_size;		/* save the amount xferred */
	tdma->cur_size = tdma->nxt_size;		/* set up for next buffer */

	tcount = tdma->nxt_size;
	outp(tdma->single,tdma->dma_disable);		/* disable chan */
	outp(tdma->clear_ff,0);						/* clear f/f */
	outp(tdma->addr,tdma->nxt_addr&0xff);		/* LSB */
	outp(tdma->addr,(tdma->nxt_addr>>8)&0xff);	/* MSB */
	outp(tdma->page,tdma->nxt_page);			/* page # */
	outp(tdma->mode,tdma->cur_mode); 			/* set mode */
	outp(tdma->clear_ff,0);						/* clear f/f */
	outp(tdma->count,tcount&0x0ff);				/* LSB count */
	outp(tdma->count,(tcount>>8)&0x0ff);		/* MSB count */
	outp(tdma->single,tdma->dma_enable);		/* enable */

	/* Now start up xfer ... */
	if (record)
		UltraStartRecordDma(tdma->cur_control);
	else
		UltraStartDramDma(tdma->cur_control);
}

int
PrimeDma(void far *pc_ptr,int type,unsigned int size,unsigned int channel)
{
DMA_ENTRY *tdma;
unsigned long s_20bit,e_20bit;
unsigned int spage,saddr,tcount;
unsigned int epage,eaddr;
#ifndef FLAT_MODEL
unsigned int sseg,soff;
#endif
int i;
unsigned char *tptr;

tdma  = &_gf1_dma[channel-1];		/* point to this dma data */

if (tdma->flags & DMA_PENDING)
	return(DMA_BUSY);

tdma->flags |= DMA_PENDING;
tdma->flags |= CALIB_COUNT;

/* Convert the pc address to a 20 bit physical address that the DMA */
/* controller needs */
#ifdef FLAT_MODEL
s_20bit = (unsigned long)pc_ptr;
#else /* !FLAT_MODEL */
sseg = FP_SEG((void far*)pc_ptr);
soff = FP_OFF((void far*)pc_ptr);

s_20bit = (unsigned long)((((unsigned long)sseg)<< 4) + (unsigned long)soff);
#endif /* FLAT_MODEL */

e_20bit = s_20bit + size - 1;
spage = (unsigned int)((s_20bit & 0xffff0000L)>>16);
epage = (unsigned int)((e_20bit & 0xffff0000L)>>16);

if (channel >= 4)
	{
	/* if 16-bit xfer, then addr,count & size are divided by 2 */
	s_20bit = s_20bit >> 1;
	e_20bit = e_20bit >> 1;
	size = size >> 1;
	}

saddr = (unsigned int)(s_20bit & 0x0000ffffL);
eaddr = (unsigned int)(e_20bit & 0x0000ffffL);

#ifdef NEVER
printf("saddr = %x eaddr = %x\n",saddr,eaddr);
printf("s_20bit = %lx e_20bit = %lx\n",s_20bit,e_20bit);
printf("spage = %d epage = %d\n",spage,epage);

tptr = (unsigned char *)pc_ptr;
for (i=0;i<10;i++)
	printf("%02x ",tptr[i]);
printf("\n");
#endif

/* In case the buffer goes over a page, save the data for the irq */
/* handler to use to finish sending the data */

if (spage != epage)
	{
	tdma->flags |= TWO_FLAG;
	tdma->nxt_page = epage;
	if (channel >= 4)
		{
		if (tdma->nxt_page & 0x01)
			tdma->nxt_addr = 0x8000;
		else
			tdma->nxt_addr = 0;
		eaddr &= 0x7fff;
		}
	else
		tdma->nxt_addr = 0;
	tdma->nxt_size = eaddr;
	size = size - eaddr - 1;	/* only supposed to send this much */
	}
else
	{
	tdma->flags &= ~TWO_FLAG;
	}


if ((type == INDEF_READ) && (tdma->flags & TWO_FLAG))
	{
	return(BAD_DMA_ADDR);
	}

tdma->cur_page = spage;
tdma->cur_addr = saddr;
tdma->amnt_sent = 0;			/* init the amount xferred so far */
tdma->cur_size = size;			/* show how big THIS part of buffer is */

tcount = size-1;

switch (type)
	{
	case READ_DMA:
		tdma->cur_mode = tdma->read;
		break;
	case WRITE_DMA:
		tdma->cur_mode = tdma->write;
		break;
	case INDEF_READ:
		/* with this mode, all we have to do is kick off the gf1 */
		/* sample control to restart the dma recording .... */
		tdma->cur_mode = tdma->read | 0x10; 	/* turn on auto init */
		break;
	case INDEF_WRITE:
		tdma->cur_mode = tdma->write | 0x10; 	/* turn on auto init */
		break;
	}

outp(tdma->single,tdma->dma_disable);		/* disable channel */
outp(tdma->mode,tdma->cur_mode);	 		/* set mode */
outp(tdma->clear_ff,0);						/* clear f/f */
outp(tdma->addr,tdma->cur_addr&0xff);		/* LSB */
outp(tdma->addr,(tdma->cur_addr>>8)&0xff);	/* MSB */
outp(tdma->page,tdma->cur_page);			/* page # */
outp(tdma->clear_ff,0);						/* clear f/f */
outp(tdma->count,tcount&0x0ff);				/* LSB count */
outp(tdma->count,(tcount>>8)&0x0ff);		/* MSB count */
outp(tdma->single,tdma->dma_enable);		/* enable */

return(ULTRA_OK);
}

void
UltraStopRecordDma(void)
{
DMA_ENTRY *tdma;
unsigned char val;

ENTER_CRITICAL;

outp(_gf1_data.reg_select,SAMPLE_CONTROL);
val = inp(_gf1_data.data_hi);
val &= ~ENABLE_ADC;
outp(_gf1_data.data_hi,val);

tdma  = &_gf1_dma[_gf1_data.adc_dma_chan-1];	/* point to this dma data */
outp(tdma->single,tdma->dma_disable);		/* disable chan */
tdma->flags &= ~DMA_PENDING;

/* Clear flag that irq handler clears when the xfer is complete */
_gf1_data.flags &= ~ADC_DMA_BUSY;

LEAVE_CRITICAL;
}

void
UltraStopPlayDma(void)
{
DMA_ENTRY *tdma;
unsigned char val;

ENTER_CRITICAL;

outp(_gf1_data.reg_select,DMA_CONTROL);
val = inp(_gf1_data.data_hi);
val &= ~DMA_ENABLE;
outp(_gf1_data.data_hi,val);

tdma  = &_gf1_dma[_gf1_data.dram_dma_chan-1];	/* point to this dma data */
outp(tdma->single,tdma->dma_disable);		/* disable chan */
tdma->flags &= ~DMA_PENDING;

/* Clear flag that irq handler clears when the xfer is complete */
_gf1_data.flags &= ~DRAM_DMA_BUSY;

LEAVE_CRITICAL;
}

