/*
 * Includes
 */



#ifndef __KERNEL__
# include "403serial.h"
# include "eval.h"
# include "qdma.h"
# include "tivomisc.h"
# include "px_hw.h"
# include "linkq.h"
/*# include "sysdep.h"*/
#else
# include <linux/module.h>
# include <linux/sched.h>
# include <linux/kernel.h> /* printk() */
# include <linux/fs.h>     /* everything... */
# include <linux/errno.h>  /* error codes */
# include <linux/malloc.h>
# include <linux/mm.h>
# include <linux/ioport.h>
# include <linux/interrupt.h>
# include <linux/tqueue.h>
# include <linux/init.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/segment.h>
# include <asm/qdma.h>
# include <asm/px_hw.h>
# include <asm/linkq.h>
# include <asm/pgtable.h>
#endif

extern void debug(char *ptr, int );

#define DMA_CH_ASSERT 0

#define IDESG

#ifdef IDESG
#define CHANNEL_TO_PTR(ch) (&DmaChArray[(ch==tyDmaChIdeSG) ? tyDmaChIde : (ch)])
#else
#define CHANNEL_TO_PTR(ch) (&DmaChArray[ch])
#endif


/*
 * Types
 */
#ifndef IDESG
#ifndef NDEBUG
# ifdef __KERNEL__
#   define DEBUG(flags,args...) do{if(me->debug&flags)printk(args##);}while(0)
#   define WARNING(args...) printk("<6>" args##)
# else
#   define DEBUG(flags,args...) do{if(me->debug&flags)s1printf(args##);}while(0)
#   define WARNING(args...) s1printf(args##)
# endif
#else
#   define DEBUG(flags,args...) 
#   define DEBUG(flags,args...) 
#endif
#else
#define DEBUG(flags, args...) 
/* printk(args##) */
#define WARNING(args...)
/* printk(args##) */
#endif

typedef enum {
  tyNextFull,
  tyCurNextFull,  
  tyCurFull,
  tyStopping,
  tyStopped,
  tyIdle,
  tyRinging,
  tyRingingSoon
} TyDmaChStateT;

typedef struct _DmaChT {
  char name[8];
  TyDmaChStateT state;
  LinkqT reqs;
  DmaChReqT *req,*lastReq;
  int fragsLeft;
  DmaChEventT event;
  U32 curAddrReg;
  U32 curCountReg;
  U32 nextAddrReg;
  U32 nextCountReg;
  U32 nextEmptyMask;
  U32 idleMask;
  U32 debug;
  U8 nextEmptyIrq;
  U8 idleIrq;
  void (*Notify)(DmaChReqT *req, void *pNotify);
  void *pNotify;
  unsigned int onMediaSwitch2;  

  /*
   * only for ringing DMA's.
   */
  U32 ringBuf;
  int ringLen;
  unsigned long resetFifoMask;
} DmaChT;

/*
 * Globals
 */
DmaChT DmaChArray[tyDmaChEnd-tyDmaChStart-1];
char *dmaChEventStringsG[]= {
  0,
  "tyNextIrq",
  "tyIdleIrq",
  "tyNextAndIdleIrq",
  "tyStartMove",
  "tyReady",
  "tyStop",
  "tyEnqueue",
  "tyRingStart",
  "tyRingStop"
};


/*
  
State Machine for  wrapping channels:

Event RingStart:
   tyIdle -> tyRinging
   all other states -> return error

Event RingStop:
   tyRinging -> tyIdle;Notify();
   all other states -> return error;

Event NextIrq:
   tyRingingSoon -> tyRinging
   tyRinging -> tyRinging;Notify();
   all other states -> panic()
   
Event IdleIrq
   all states -> panic()
*/


char *dmaChStateStringsG[]= {
  "tyNextFull",
  "tyCurNextFull",
  "tyCurFull",
  "tyStopping",
  "tyStopped",
  "tyIdle",
  "tyRinging",
  "tyRingingSoon"
  
};

#ifdef ALLOC_ILINK_DMA_BUFFERS
/*
  This is a range of continuous physical pages managed by the i.LINK
  driver. This was the simplest way to get CONSECUTIVE PHYSICAL pages
  in order to make large DMA fragments. Alternative methods of
  __get_dma_pages() and kmalloc(), with GPF_DMA, do not return
  consecutive physical pages!

  This variable is configured only if ALLOC_ILINK_DMA_BUFFERS is
  defined.
 */
#define ILINK_DMA_BUFFERS_SIZE 1024*1024 /* 1MB */
unsigned ilink_dma_size = ILINK_DMA_BUFFERS_SIZE;
char ilink_dma_buffers[ILINK_DMA_BUFFERS_SIZE];
#endif

/*
 * Functions
 */

#ifdef __KERNEL__
/*
 * Do the proper cache flushing, so the drivers don't have to.
 * XXX We should recognize which direction the DMA is going.
 * If it's a write, then the caches only need to stored, and
 * valid cache lines can stay valid. If it's a read, we must
 * insure that all data is out of the caches and invalid.
 * This results in a slight performance improvement, since
 * lines we'll later overwrite are never written back to memory.
 */
static void
FlushStaleData(DmaChReqT *req)
{
  int i;
  for (i = 0; i < req->numFrags; i++) {
    unsigned long addr = (unsigned long)req->fragp[i].buf & 0x8fffffff;
    unsigned long len = req->fragp[i].len & 0x00ffffff;
    dma_flush_cache_range(addr, addr + len);
  }
}
#else
# define FlushStaleData(x)
#endif


TyDmaChStateT 
DmaChGetState(DmaChT *me)
{
  if (me == &DmaChArray[tyDmaChIdeSG]) {
    me = &DmaChArray[tyDmaChIde];
  }
  return me->state;
}





/*
 * We are only protecting between top and bottom code for
 * a given channel not between multiple clients for the
 * channel.
 */
void
DmaChMutex(DmaChT *me, bool grab)
{
  if (me == &DmaChArray[tyDmaChIdeSG]) {
    me = &DmaChArray[tyDmaChIde];
  }

#ifdef __KERNEL__  
  if (grab){
    //    DEBUG(DEBUG_ALL,"MLOCK %s\n",me->name);
    disable_irq(me->nextEmptyIrq);
    disable_irq(me->idleIrq);
  }else{
    //    DEBUG(DEBUG_ALL,"MUNLOCK %s\n",me->name);
    enable_irq(me->nextEmptyIrq);
    enable_irq(me->idleIrq);
  }
#endif  
}


void
DmaChDebugPrint(int channel, U32 on){
#ifdef __KERNEL__  
  DEBUG(DEBUG_ALL, "QDMA: setting debug on %s to 0x%x\n",
	 DmaChArray[channel].name,on);
#endif
  DmaChArray[channel].debug = on;
}
  
void
DmaChPrintState(DmaChT *me) {
   DEBUG(DEBUG_ALL,"last event %s  in state  %s\n",
	   dmaChEventStringsG[me->event],
	   dmaChStateStringsG[me->state]);
}

inline void
DmaChAssert(DmaChT *me,bool val)
{
  if (!val) {
    WARNING("DmaCh ASSERT on %s...looping: last event %s  in state  %s\n",
	   me->name,
	   dmaChEventStringsG[me->event],
	   dmaChStateStringsG[me->state]);
    debug("DmaChAssert\n", 0);
  }

}
/* IDESG should be passed !!! */
void
DmaChNotify(DmaChT *me)
{
  DmaChT *notify_me = me;
#ifdef IDESG
  if (me == &DmaChArray[tyDmaChIdeSG]) {
    notify_me = &DmaChArray[tyDmaChIde];
  }
#endif
  DEBUG(DEBUG_ALL,"NOTIFYING, me=0x%p\n", me);

  if (DmaChGetState(me)==tyRinging){
    if (notify_me->Notify) {
      notify_me->Notify(me->lastReq, notify_me->pNotify);
    }
  } else {
    me->lastReq->finished = 1;
    if (notify_me->Notify) {
      notify_me->Notify(me->lastReq, notify_me->pNotify);
    }
    me->lastReq = 0;
  }
}

void
DmaChException(DmaChT *me)
{

#ifdef __KERNEL__  
  printk("DMA exception on %s last event %s  in state  %s\n", me->name,
	   dmaChEventStringsG[me->event],
	   dmaChStateStringsG[me->state]);
  DEBUG(DEBUG_ALL,"DMA");
#else
  WARNING("%s: exception: last event %s  in state  %s\n",me->name,
	dmaChEventStringsG[me->event],dmaChStateStringsG[me->state]);
 loop:
  goto loop;	   
#endif

}




bool
DmaChIsThereWork(DmaChT *me){
  /* is this right?? */
  if (me->fragsLeft || !LinkqIsEmpty((LinkqT *)&me->reqs)) {
    return TRUE;
  }
  return FALSE;
}




#ifndef __KERNEL__
void strncpy(char *p1, const char *p2, int len)
{
  int i;
  for (i=0;i<len;i++){
    *p1++ = *p2++;
  }
}
#endif

  


  
void
DmaChSetState(DmaChT *me, TyDmaChStateT state)
{
  DEBUG(DEBUG_STATES,"\n%s: evt %s in state  %s -> new state %s\n",me->name,
	dmaChEventStringsG[me->event],
	dmaChStateStringsG[me->state],
	dmaChStateStringsG[state]);
  me->state = state; // FIXME: we modify two values Ide and IdeSG
  if (state==tyIdle){
    DEBUG(DEBUG_IDLE,"idle on %s\n",me->name);
  }
#ifdef IDESG
  if (me == &DmaChArray[tyDmaChIde] && ms4probe()) {
    me = &DmaChArray[tyDmaChIdeSG];
    me->state = state;
  } else if (me == &DmaChArray[tyDmaChIdeSG]) {
    me = &DmaChArray[tyDmaChIde];
    me->state = state;
  }
#endif
}



#ifdef __KERNEL__

void KernelIdleIrq(int irq, void *dev_id, struct pt_regs *regs)
{
  if (DmaChArray[(TyDmaChT)dev_id].onMediaSwitch2)
    clear_current_ms2_isr();
  else
    clear_current_ms1_isr();
  DmaChIdleIrq((TyDmaChT )dev_id);
}

void KernelNextIrq(int irq, void *dev_id, struct pt_regs *regs)
{
  if (DmaChArray[(TyDmaChT)dev_id].onMediaSwitch2)
    clear_current_ms2_isr();
  else
    clear_current_ms1_isr();
  DmaChNextIrq((TyDmaChT )dev_id);
}

static int
GetBitPosition(U32 mask)
{
  int pos = 0;
  while(mask > 1) {
    pos++;
    mask>>=1;
  }
  return pos;
}

#endif



void
DmaChInit(TyDmaChT channel, DmaChInitT *init)
{
  volatile unsigned long syscntl;
  DmaChT *me = &DmaChArray[channel];
  strncpy(me->name,init->name,sizeof(me->name)-1);
  LinkqInit((LinkqT *)&me->reqs);
  me->name[sizeof(me->name)-1]=0;
  me->nextAddrReg = init->nextAddrReg;
  me->nextCountReg = init->nextCountReg;
  me->debug=0;
  me->curAddrReg = init->curAddrReg;
  me->curCountReg = init->curCountReg;
  me->ringBuf = 0;
  me->resetFifoMask = init->resetFifoMask;
  me->onMediaSwitch2 = init->onMediaSwitch2;
  /*
   * Reset the DMA channel.
   */
  if (init->resetFifoMask) {
      /* stop current dma */
      MsWrite(me->nextCountReg, 0);  
      MsWrite(me->curCountReg, 0);

      /*
       * reset fifo and bring it out of reset
       */

      /* well, oslink2 is on ms1, but not the status register ...
       * to make things simpler ;-)
       */
      if (me->onMediaSwitch2 || (channel == tyDmaChOSLinkIn2)) {
        syscntl = MsRead(MS2_SYSTEM_REGISTER)|init->resetFifoMask;
        DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(int)syscntl);
        MsWrite(MS2_SYSTEM_REGISTER,syscntl);
        syscntl &= ~(init->resetFifoMask);
        DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(int)syscntl) ; 
        MsWrite(MS2_SYSTEM_REGISTER,syscntl);
      } else {
        syscntl = MsRead(MS_SYSTEM_REGISTER)|init->resetFifoMask;
        DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(int)syscntl);
        MsWrite(MS_SYSTEM_REGISTER,syscntl);
        syscntl &= ~(init->resetFifoMask);
        DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(int)syscntl) ; 
        MsWrite(MS_SYSTEM_REGISTER,syscntl);
      }
  }
  
  me->nextEmptyMask = init->nextEmptyMask;
  me->idleMask = init->idleMask;

#ifdef __KERNEL__
  /* put ISR reset here */
#else
  {
      U32 isr;
      if (me->onMediaSwitch2) {
        isr = MsRead(MS2_ISR_REG);
        MsWrite(MS2_ISR_REG,(isr | me->nextEmptyMask | me->idleMask));
      } else {
        isr = MsRead(MS_ISR_REG);
        MsWrite(MS_ISR_REG,(isr | me->nextEmptyMask | me->idleMask));
      }
  }
#endif
#ifdef __KERNEL__
  if (me->onMediaSwitch2) {
    me->nextEmptyIrq =   31-GetBitPosition(init->nextEmptyMask)+MEDIASWITCH2_IBASE;  
    me->idleIrq = 31-GetBitPosition(init->idleMask)+MEDIASWITCH2_IBASE;
  } else {
    me->nextEmptyIrq =   31-GetBitPosition(init->nextEmptyMask)+MEDIASWITCH_IBASE;  
    me->idleIrq = 31-GetBitPosition(init->idleMask)+MEDIASWITCH_IBASE;
  }
#endif
  
  me->req = 0;
  me->lastReq = 0;
  me->fragsLeft = 0;
  me->state = tyIdle;
  me->Notify=0;
  me->pNotify=0;

#ifdef __KERNEL__
  {
    int result;
    printk("DMA Channel %s, next irq %d, idle irq %d, channel %d\n",me->name,me->nextEmptyIrq,
	   me->idleIrq,channel);
    result = request_irq(me->nextEmptyIrq, KernelNextIrq, SA_INTERRUPT,me->name, (void *)channel);
    if (result) {
      printk(KERN_INFO "%s: can't get assigned irq \n",me->name);
    }      
    result = request_irq(me->idleIrq, KernelIdleIrq, SA_INTERRUPT,me->name, (void *)channel);
    if (result) {
      printk(KERN_INFO "%s: can't get assigned irq com\n",me->name) ;   
    }      
    enable_irq(me->nextEmptyIrq);
    enable_irq(me->idleIrq);
    if (me->onMediaSwitch2)
      printk("MS2 IMR %x\n",inl(MS2_IM_REG));
    else
      printk("MS IMR %x\n",inl(MS_IM_REG));
  }
#endif
}


int
DmaChSetParameters(TyDmaChT channel, DmaChParmsT *parms)
{
  DmaChT *me = &DmaChArray[channel];
  if (DmaChGetState(me)==tyStopped ||
      DmaChGetState(me)==tyIdle) {
    me->Notify = parms->Notify;
    me->pNotify = parms->pNotify;
    return 0;
  }
  return -1;
}

/* MediaSwitch1 Control Register */
#define AMIX_RESET (1<<11)
#define AVOUT_RESET (1<<10)
#define IDE_RESET (1<<9)
#define EVT_RESET (1<<8)
#define VBI_RESET (1<<7)
#define VIN_RESET (1<<6)
#define AIN_RESET (1<<5)
/* MediaSwitch2 Control Register */
#define ST551_RESET (1<<1)
#define ST552_RESET (1<<2)

#define OSOUT1_RESET (1<<9)
#define OSOUT2_RESET (1<<10)
/* well, on MS2 you have two reset lines for oslink. This bit is used on MS4
 * to reset the other oslink. */
#define OSIN_RESET (1<<10) 

#define EVT2_RESET (1<<8)
#if 0
#define VBI2_RESET (1<<7)
#endif
#define ILOUT_RESET (1<<7)
#define VIN2_RESET (1<<6)
#define AIN2_RESET (1<<5) /* it is reserved on MS4 */
#define DIGAIN1_RESET AIN2_RESET
#define DIGVIN1_RESET VIN2_RESET

/* MediaSwitch4 Control Register */
#define DIGAIN2_RESET (1<<4)
/* Video and Audio are grouped on the MS4 */
#define DIGVIN2_RESET DIGAIN2_RESET
#define IDESG_RESET (1<<5)


void __init
DmaChInitAll(void)
{
  DmaChInitT init;
  int syscntl;

  /* Until we get this from the PROM */
  if (!ms_base_addr)
    ms_base_addr = OLD_MS_BASE_ADDR;

  init.name="VidOut";
  init.nextAddrReg = MS_VOUT_NEXT_ADDR_REG;
  init.nextCountReg = MS_VOUT_NEXT_COUNT_REG;
  init.curAddrReg = MS_VOUT_CUR_ADDR_REG;
  init.curCountReg = MS_VOUT_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyVoutNextEmpty;
  init.idleMask = tyVoutIdle;
  init.resetFifoMask = AVOUT_RESET;  
  DmaChInit(tyDmaChVideoOut,&init);

  if (!ms3probe()) {
    init.name="VidIn";
    init.nextAddrReg = MS_VIN_NEXT_ADDR_REG;
    init.nextCountReg = MS_VIN_NEXT_COUNT_REG;
    init.curAddrReg = MS_VIN_CUR_ADDR_REG;
    init.curCountReg = MS_VIN_CUR_COUNT_REG;
    init.onMediaSwitch2 = 0;
    init.nextEmptyMask = tyVinNextEmpty;
    init.idleMask = tyVinIdle;
    init.resetFifoMask = VIN_RESET;
    DmaChInit(tyDmaChVideoIn,&init);
  }

  init.name="Events";
  init.nextAddrReg = MS_EVT_NEXT_ADDR_REG;
  init.nextCountReg = MS_EVT_NEXT_COUNT_REG;
  init.curAddrReg = MS_EVT_CUR_ADDR_REG;
  init.curCountReg = MS_EVT_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyEvtNextEmpty;
  init.idleMask = tyEvtIdle;
  init.resetFifoMask = EVT_RESET;
  DmaChInit(tyDmaChEventsIn,&init);

  init.name="OSD";
  init.nextAddrReg = MS_OSD_NEXT_ADDR_REG;
  init.nextCountReg = MS_OSD_NEXT_COUNT_REG;
  init.curAddrReg = MS_OSD_CUR_ADDR_REG;
  init.curCountReg = MS_OSD_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyOsdNextEmpty;
  init.idleMask = tyOsdIdle;
  init.resetFifoMask = 0;
  DmaChInit(tyDmaChOsd,&init);

  init.name="AudOut";
  init.nextAddrReg = MS_AOUT_NEXT_ADDR_REG;
  init.nextCountReg = MS_AOUT_NEXT_COUNT_REG;
  init.curAddrReg = MS_AOUT_CUR_ADDR_REG;
  init.curCountReg = MS_AOUT_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyAoutNextEmpty;
  init.idleMask = tyAoutIdle;
  init.resetFifoMask = 0;
  DmaChInit(tyDmaChAudioOut,&init);

  if (!ms3probe()) {
    init.name="AudIn";
    init.nextAddrReg = MS_AIN_NEXT_ADDR_REG;
    init.nextCountReg = MS_AIN_NEXT_COUNT_REG;
    init.curAddrReg = MS_AIN_CUR_ADDR_REG;
    init.curCountReg = MS_AIN_CUR_COUNT_REG;
    init.onMediaSwitch2 = 0;
    init.nextEmptyMask = tyAinNextEmpty;
    init.idleMask = tyAinIdle;
    init.resetFifoMask = AIN_RESET;    
    DmaChInit(tyDmaChAudioIn,&init);

    init.name="VBI";
    init.nextAddrReg = MS_VBI_NEXT_ADDR_REG;
    init.nextCountReg = MS_VBI_NEXT_COUNT_REG;
    init.curAddrReg = MS_VBI_CUR_ADDR_REG;
    init.curCountReg = MS_VBI_CUR_COUNT_REG;
    init.onMediaSwitch2 = 0;
    init.nextEmptyMask = tyVbiNextEmpty;
    init.idleMask = tyVbiIdle;
    init.resetFifoMask = VBI_RESET;    
    DmaChInit(tyDmaChVbi,&init);
  }

  init.name="Ide";
  init.nextAddrReg = MS_IDE_NEXT_ADDR_REG;
  init.nextCountReg = MS_IDE_NEXT_COUNT_REG;
  init.curAddrReg = MS_IDE_CUR_ADDR_REG;
  init.curCountReg = MS_IDE_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyIdeNextEmpty;
  init.idleMask = tyIdeIdle;
  init.resetFifoMask = IDE_RESET;
  DmaChInit(tyDmaChIde,&init);

  if (!ms2probe())
    return;

  printk("MS2 exists...reset 5505...\n");
  syscntl = MsRead(MS2_SYSTEM_REGISTER)|ST551_RESET;
  MsWrite(MS2_SYSTEM_REGISTER,syscntl);

  /* Media Switch 2 */
  init.name= (ms4probe() ? "OSOut1" : "OSOut") ;
  init.nextAddrReg = MS2_OSOUT_NEXT_ADDR_REG;
  init.nextCountReg = MS2_OSOUT_NEXT_COUNT_REG;
  init.curAddrReg = MS2_OSOUT_CUR_ADDR_REG;
  init.curCountReg = MS2_OSOUT_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyOsoutNextEmpty;
  init.idleMask = tyOsoutIdle;
  init.resetFifoMask = OSOUT1_RESET;
  DmaChInit(tyDmaChOSLinkOut,&init);

  init.name= (ms4probe() ? "OSIn1" : "OSIn");
  init.nextAddrReg = MS2_OSIN_NEXT_ADDR_REG;
  init.nextCountReg = MS2_OSIN_NEXT_COUNT_REG;
  init.curAddrReg = MS2_OSIN_CUR_ADDR_REG;
  init.curCountReg = MS2_OSIN_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyOsinNextEmpty;
  init.idleMask = tyOsinIdle;
  /* the meaning of the reset bit has changed between ms2 and 4 */
  init.resetFifoMask = (ms4probe() ? OSOUT1_RESET : OSIN_RESET);
  DmaChInit(tyDmaChOSLinkIn,&init);

  syscntl = MsRead(MS2_SYSTEM_REGISTER) &= ~ST551_RESET;
  MsWrite(MS2_SYSTEM_REGISTER,syscntl);

  init.name= (ms4probe() ? "DigVin1" : "VidIn2") ;
  init.nextAddrReg = MS2_VIN_NEXT_ADDR_REG;
  init.nextCountReg = MS2_VIN_NEXT_COUNT_REG;
  init.curAddrReg = MS2_VIN_CUR_ADDR_REG;
  init.curCountReg = MS2_VIN_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyVin2NextEmpty;
  init.idleMask = tyVin2Idle;
  init.resetFifoMask = VIN2_RESET;
  DmaChInit(tyDmaChVideoIn2,&init);

  if (!ms3probe()) {
    /* Then, this must be ms2 */
    init.name="Events2";
    init.nextAddrReg = MS2_EVT_NEXT_ADDR_REG;
    init.nextCountReg = MS2_EVT_NEXT_COUNT_REG;
    init.curAddrReg = MS2_EVT_CUR_ADDR_REG;
    init.curCountReg = MS2_EVT_CUR_COUNT_REG;
    init.onMediaSwitch2 = 1;
    init.nextEmptyMask = tyEvt2NextEmpty;
    init.idleMask = tyEvt2Idle;
    init.resetFifoMask = EVT2_RESET;
    DmaChInit(tyDmaChEventsIn2,&init);
  }

  init.name= (ms4probe() ? "DigAin1" : "AudIn2" );
  init.nextAddrReg = MS2_AIN_NEXT_ADDR_REG;
  init.nextCountReg = MS2_AIN_NEXT_COUNT_REG;
  init.curAddrReg = MS2_AIN_CUR_ADDR_REG;
  init.curCountReg = MS2_AIN_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyAin2NextEmpty;
  init.idleMask = tyAin2Idle;
  init.resetFifoMask = AIN2_RESET;
  DmaChInit(tyDmaChAudioIn2,&init);

  init.name="ILnkOut";
  init.nextAddrReg = MS2_ILOUT_NEXT_ADDR_REG;
  init.nextCountReg = MS2_ILOUT_NEXT_COUNT_REG;
  init.curAddrReg = MS2_ILOUT_CUR_ADDR_REG;
  init.curCountReg = MS2_ILOUT_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyILinkOutNextEmpty;
  init.idleMask = tyILinkOutIdle;
  init.resetFifoMask = ILOUT_RESET;
  DmaChInit(tyDmaChILinkOut,&init);

  /* MediaSwitch 4 */
  if (!ms4probe()) {
    return;
  }
  printk("MS4 detected... Setting up second digital input\n");
  printk("Resetting second 5505\n");
  init.name= "DigAIn2";
  init.nextAddrReg = MS4_AIN2_NEXT_ADDR_REG;
  init.nextCountReg = MS4_AIN2_NEXT_COUNT_REG;
  init.curAddrReg = MS4_AIN2_CUR_ADDR_REG;
  init.curCountReg = MS4_AIN2_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyDigAin2NextEmpty; 
  init.idleMask = tyDigAin2Idle;
  init.resetFifoMask = DIGAIN2_RESET;
  DmaChInit(tyDmaChDigAudioIn2,&init);

  init.name="DigVIn2";
  init.nextAddrReg = MS4_VIN2_NEXT_ADDR_REG;
  init.nextCountReg = MS4_VIN2_NEXT_COUNT_REG;
  init.curAddrReg = MS4_VIN2_CUR_ADDR_REG;
  init.curCountReg = MS4_VIN2_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyDigVin2NextEmpty;
  init.idleMask = tyDigVin2Idle;
  init.resetFifoMask = DIGVIN2_RESET;
  DmaChInit(tyDmaChDigVideoIn2,&init);

  /* setting up the second Os link channel */
  syscntl = MsRead(MS2_SYSTEM_REGISTER)|ST552_RESET;
  MsWrite(MS2_SYSTEM_REGISTER,syscntl);

  init.name= "OSOut2";
  init.nextAddrReg = MS4_OSOUT2_NEXT_ADDR_REG;
  init.nextCountReg = MS4_OSOUT2_NEXT_COUNT_REG;
  init.curAddrReg = MS4_OSOUT2_CUR_ADDR_REG;
  init.curCountReg = MS4_OSOUT2_CUR_COUNT_REG;
  init.onMediaSwitch2 = 1;
  init.nextEmptyMask = tyOsout2NextEmpty;
  init.idleMask = tyOsout2Idle;
  init.resetFifoMask = OSOUT2_RESET;
  DmaChInit(tyDmaChOSLinkOut2,&init);

  init.name= "OSIn2";
  init.nextAddrReg = MS4_OSIN2_NEXT_ADDR_REG;
  init.nextCountReg = MS4_OSIN2_NEXT_COUNT_REG;
  init.curAddrReg = MS4_OSIN2_CUR_ADDR_REG;
  init.curCountReg = MS4_OSIN2_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0; /* it is situated on the MS1 part of the MS4 */
  init.nextEmptyMask = tyOsin2NextEmpty;
  init.idleMask = tyOsin2Idle;
  init.resetFifoMask = OSOUT2_RESET;
  DmaChInit(tyDmaChOSLinkIn2,&init);

#ifdef IDESG
  init.name= "IDESG";
  init.nextAddrReg = MS4_IDESG_NEXT_ADDR_REG;
  init.nextCountReg = MS4_IDESG_NEXT_COUNT_REG;
  init.curAddrReg = MS4_IDESG_CUR_ADDR_REG;
  init.curCountReg = MS4_IDESG_CUR_COUNT_REG;
  init.onMediaSwitch2 = 0;
  init.nextEmptyMask = tyIdeSGNextEmpty;
  init.idleMask = tyIdeSGIdle;
  init.resetFifoMask = 0;
  DmaChInit(tyDmaChIdeSG,&init);
#endif
  syscntl = MsRead(MS2_SYSTEM_REGISTER) &= ~ST552_RESET;
  MsWrite(MS2_SYSTEM_REGISTER,syscntl);
}

#define DMA_FULL_ENABLE_MASK ((1<<24)|(1<<30)|(1<<31))
#define DMA_NEXT_ENABLE_MASK ((1<<24)|(1<<31))
#define DMA_NEXT_ENABLE_MASK_WITH_RING ((1<<24)|(1<<29)|(1<<31))
#define DMA_ENABLE_MASK (1<<24)

static void 
setup_dma_fraglist(DmaChReqT *req)
{
#define DMA_READ_WRITE_MASK 0x10000000
  int i, fops;
  DmaChFragT *fragp;
  
  fragp = (DmaChFragT *)req->fragp->buf;
  fops  = fragp[0].len & DMA_READ_WRITE_MASK;
  MsWrite(MS_IDE_NEXT_COUNT_REG, fops);

  for(i=0;i<(((req->fragp->len)&0xffff)/sizeof(DmaChFragT)-1);i++) {
    fragp[i].len |= DMA_ENABLE_MASK;
  }
  fragp[i].len |= DMA_FULL_ENABLE_MASK;
  FlushStaleData(req);
}

int StartTransfer(DmaChT *me)
{
  int whichFrag;
  U32 len;
  DmaChReqT *req;
  int sg = 0;

#ifdef IDESG  
  if ((me == &DmaChArray[tyDmaChIde] && ms4probe()) ||
      (me == &DmaChArray[tyDmaChIdeSG])) {
    me = &DmaChArray[tyDmaChIdeSG];
    sg = 1;
  }
#endif
  req = me->req;
#ifdef IDESG_D
printk("StartTransfer\n");
printk("  chan %s, sg=%d, req=0x%lx\n",  me->name, sg, req);

if (req)
  printk("  numF=%d, left=%d\n", req->numFrags, me->fragsLeft);
#endif

  if (me->fragsLeft) {

    whichFrag = req->numFrags - me->fragsLeft;

    if (!req->fragp) {
      printk("No fragp in StartTransfer\n");
    }
    DmaChAssert(me,req->fragp);
#ifdef IDESG_D
printk("1st frag, buf=0x%lx, len=%d, whichFrag=%d, fragsLeft=%d, isempty=%d\n", req->fragp[0].buf, req->fragp[0].len, whichFrag, me->fragsLeft, LinkqIsEmpty(&me->reqs));
#endif
    MsWrite(me->nextAddrReg, (U32)req->fragp[whichFrag].buf);
    if (me->fragsLeft == 1 && LinkqIsEmpty(&me->reqs)) {
      if (sg) {
        /* we do not want any interrupt when DMA is done */
        len = req->fragp[whichFrag].len | DMA_ENABLE_MASK; 
        setup_dma_fraglist(req);
      } else {
        len = req->fragp[whichFrag].len | DMA_FULL_ENABLE_MASK;
      }
      DEBUG(DEBUG_FRAGS,"st current: frag# %d, len %x, req %p\n",
	    whichFrag,
	    len,req);
      MsWrite(me->nextCountReg,len);
      
    } else {
      if (sg) {
	panic("sg StartTransfer too many frags.");
      } else {
        len = req->fragp[whichFrag].len | DMA_NEXT_ENABLE_MASK;
      }
      DEBUG(DEBUG_FRAGS,"st current: frag# %d, len %x, req %p\n",
	    whichFrag,
	    len,req);
      MsWrite(me->nextCountReg,len);
    }
  }else{
    /*  Get next req  */
    req=(DmaChReqT *)LinkqRemove(&me->reqs);
	     

    /* Are there any left? */
    if (!req) {
      DEBUG(DEBUG_FRAGS,"st: no more requests\n");
      me->req = 0;
      me->fragsLeft = 0;
      return FALSE;
    }

      
    /*  Set fragsleft  */

    me->req = req;
    /*  Send first frag of next req */
    me->fragsLeft = req->numFrags;
    DEBUG(DEBUG_FRAGS,"Dequeuing %d frags in StartTransfer\n",me->fragsLeft);        
if (me->fragsLeft<0) printk("No fragsLeft: %d\n", me->fragsLeft);
    DmaChAssert(me,me->fragsLeft>0);
    



    if (me->fragsLeft == 1 && LinkqIsEmpty(&me->reqs)) {
      if (sg) {
        /* we do not want any interrupt when DMA is done */
        len=  req->fragp[0].len | DMA_ENABLE_MASK; 
        /* Walk the IDE frag table and setup last frag to interrupt */
        setup_dma_fraglist(req);
      } else {
        len=  req->fragp[0].len | DMA_FULL_ENABLE_MASK;
      }
    }else{
      if (sg)
        panic("StartTransfer too many frags.");
      len= req->fragp[0].len | DMA_NEXT_ENABLE_MASK;
    }

    DEBUG(DEBUG_FRAGS,"st new: frag# %d, len %x, req %p\n",
	     0,
	     len,req);	           
#ifdef IDESG_D
printk("st new: buf=0x%x, len=0x%x\n", (U32)req->fragp[0].buf,len);
if (sg)
{int *p = (int *)req->fragp[0].buf;
printk("X1=0x%x, X2=0x%x\n", p[0], p[1]);
}
#endif    
    MsWrite(me->nextAddrReg, (U32)req->fragp[0].buf);
    /*    MsWrite(me->nextCountReg,(U32)req->fragp[0].len); */
    MsWrite(me->nextCountReg,len);     
  }
  return TRUE;
}



static void IdleIrqStuff(DmaChT *me)
{
#ifdef IDESG
  if (me == &DmaChArray[tyDmaChIde] && ms4probe()) {
    me = &DmaChArray[tyDmaChIdeSG];
  }
#endif

  if (me->lastReq) {
    /*
     * If we just called NextIrqStuff as in NextAndIdleIrq
     * the me->lastReq == 0 already and we
     * wont do this
     */    
    /*
     * We just put the first frag of a new request
     * in the current hardware buffer. That means
     * the old current hardware buffer corresponding
     * with the last request is done.
     */
#ifdef IDESG
//if (me == &DmaChArray[tyDmaChIdeSG])
//printk("notify SG?\n");
#endif
    DmaChNotify(me); // FIXME SG or IDE ?
  }
  if (me->fragsLeft == 0) {
    /*
     * Last fragment has gone into current dma buffer on
     * hardware
     *
     * save request  so that on next irq we
     * can notify on the request once all of it has been xfered.
     */
    me->lastReq = me->req;
    me->req = 0;
  }
}


/*
 * We've finished Dmaing a frag and are under irq
 */
static void
NextIrqStuff(DmaChT *me)
{
#ifdef IDESG
  if (me == &DmaChArray[tyDmaChIde] && ms4probe()) {
    me = &DmaChArray[tyDmaChIdeSG];
  }
#endif

  if (me->fragsLeft<=0){
    WARNING("NextIrqStuff on %s: fragsleft = %d\n", 
	    me->name, me->fragsLeft);
    WARNING("\n%s: evt %s in state  %s -> new state %s\n",me->name,
	    dmaChEventStringsG[me->event],
	    dmaChStateStringsG[me->state],
	    dmaChStateStringsG[me->state]);
    me->fragsLeft=0;

  }else{
    me->fragsLeft--;
    if (me->fragsLeft<0){
      WARNING("fragsleft going negative on %s: fragsleft = %d\n",
	      me->name, me->fragsLeft);
    }
  }
  if (me->lastReq) {
    /*
     * We just put the first frag of a new request
     * in the current hardware buffer. That means
     * the old current hardware buffer corresponding
     * with the last request is done.
     */
    DmaChNotify(me);
  }
  if (me->fragsLeft == 0) {
    /*
     * Last fragment has gone into current dma buffer on
     * hardware
     *
     * save request  so that on next irq we
     * can notify on the request once all of it has been xfered.
     */
    me->lastReq = me->req;
    me->req = 0;
  }
}


		 
void
DmaChEnqueue(TyDmaChT channel, DmaChReqT *req)
{
  int res;
  DmaChT *me = &DmaChArray[channel];
  DmaChMutex(me,TRUE);  
#ifdef IDESG
  if (ms4probe() && (channel == tyDmaChIdeSG || channel == tyDmaChIde)) {
    panic("Unexpected DmaChEnqueue on MS4 !!\n");
  }
#endif
  DEBUG(DEBUG_ALL,"\n\nDmaChEnqueue on %s\n",me->name);      
  me->event = tyEnqueue;
  DEBUG(DEBUG_ALL,"Enqueuing %d frags in DmaChEnqueue\n",req->numFrags);
  FlushStaleData(req);  


  
#if DMA_CH_ASSERT
  {
    int i;
    for (i=0;i<req->numFrags;i++){
      if (((unsigned long)req->fragp[i].buf % 4) != 0) {
	DmaChException(me);
      }
    }
  }
#endif  

  req->finished = 0;
  
  if ((res = DmaChGetState(me)) == tyIdle) {
    DmaChSetState(me, tyStopped);
  }
  /* careful with synchro */
  LinkqInsert((LinkqT *)&me->reqs, (LinkItemT *)req);
  DmaChMutex(me,FALSE);
}

void
DmaListChEnqueue(TyDmaChT listchannel, TyDmaChT channel,DmaChReqT *req)
{
  int res;
  DmaChT *me = &DmaChArray[listchannel];
  DmaChT *reqme = &DmaChArray[channel];
  static struct _DmaChReqT DmaListChReq;
  static DmaChFragT listfragbuf;

  DmaChMutex(me,TRUE);  
  DEBUG(DEBUG_ALL,"\n\nDmaListChEnqueue on %s\n",me->name);      

  me->event = tyEnqueue;
  reqme->event = tyEnqueue;
  DEBUG(DEBUG_ALL,"Enqueuing %d frags in DmaChEnqueue\n",req->numFrags);
  FlushStaleData(req);

  /* Set up listfragbuf */
  listfragbuf.buf = (U32*)req->fragp;
  listfragbuf.len = (U32)req->numFrags * sizeof(DmaChFragT);
  DmaListChReq.numFrags = 1;
  DmaListChReq.fragp = &listfragbuf;
  DmaListChReq.next = NULL;
  FlushStaleData(&DmaListChReq);  
  if (me->req != me->lastReq)
    printk("DmaListChEnqueue, req!= lastReq\n");
  //me->req = me->lastReq = &DmaListChReq;
  //reqme->req = reqme->lastReq = req;
#ifdef IDESG_D
  printk("Sizeof: listfragbuf.len = %ld\n", listfragbuf.len);
#endif

#if DMA_CH_ASSERT
  {
    int i;
    for (i=0;i<req->numFrags;i++){
      if (((unsigned long)req->fragp[i].buf % 4) != 0) {
	DmaChException(me);
      }
    }
  }
#endif  

  req->finished = 0;
  DmaListChReq.finished = 0;
  
  //  if ((res = DmaChGetState(me)) == tyIdle) {
  //    DmaChSetState(me, tyStopped);
  //  }
  if ((res = DmaChGetState(reqme)) == tyIdle) {
    DmaChSetState(reqme, tyStopped);
  }
  /* careful with synchro */
  LinkqInsert((LinkqT *)&me->reqs, (LinkItemT *)&DmaListChReq);
  DmaChMutex(me,FALSE);    
}

#if __KERNEL__
int
DmaChStop(TyDmaChT channel, bool immediate)
{
  unsigned int isr;  
  DmaChT *me;
#ifdef IDESG
  DmaChT *sg_me;
#endif

  me = CHANNEL_TO_PTR(channel);
  DmaChMutex(me,TRUE);
  me->event = tyStop;
  switch(DmaChGetState(me)) {
  case tyIdle:
    goto good_cleanup;
  case tyStopped:
#ifdef IDESG_D
printk("%s %s\n", __FUNCTION__, me->name);
#endif
    goto bad_cleanup;
  case tyNextFull: /* fallthru */
  case tyCurFull:    /* fallthru */
  case tyCurNextFull:
    if(immediate){
      unsigned long syscntl;
      disable_irq(IRQ_MEDIASWITCH);

      /*
       * Stop DMA
       */
      MsWrite(me->nextCountReg, 0);
      MsWrite(me->curCountReg, 0);
#ifdef IDESG
      sg_me = &DmaChArray[tyDmaChIdeSG];
      if (ms4probe() && (channel == tyDmaChIde || channel == tyDmaChIdeSG)) {
	MsWrite(sg_me->nextCountReg, 0);
	MsWrite(sg_me->curCountReg, 0);
      }
#endif
      /*
       * Clear ISR
       */
      if (me->onMediaSwitch2) {
        isr = inl(MEDIASWITCH2_ISR);
        isr &= ~(me->nextEmptyMask|me->idleMask);
        outl(isr, MEDIASWITCH2_ISR);
      } else {
        isr = inl(MEDIASWITCH_ISR);
        isr &= ~(me->nextEmptyMask|me->idleMask);
        outl(isr, MEDIASWITCH_ISR);
      }


      
      /*
       * reset fifo and bring it out of reset
       */
      if (me->onMediaSwitch2 || channel == tyDmaChOSLinkIn2) {
        syscntl = MsRead(MS2_SYSTEM_REGISTER)|me->resetFifoMask;
        DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(int)syscntl);
        MsWrite(MS2_SYSTEM_REGISTER,syscntl);
        syscntl &= ~(me->resetFifoMask);
        DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(int)syscntl) ; 
        MsWrite(MS2_SYSTEM_REGISTER,syscntl);
      } else {
        syscntl = MsRead(MS_SYSTEM_REGISTER)|me->resetFifoMask;
        DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(int)syscntl);
        MsWrite(MS_SYSTEM_REGISTER,syscntl);
        syscntl &= ~(me->resetFifoMask);
        DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(int)syscntl) ; 
        MsWrite(MS_SYSTEM_REGISTER,syscntl);
      }
#ifdef IDESG
      if (channel == tyDmaChIde || channel == tyDmaChIdeSG) { 
	sg_me->req=0;
	sg_me->lastReq=0;
      } else {
#endif
	me->req=0;
	me->lastReq=0;
#ifdef IDESG
      }
#endif
      DmaChSetState(me,tyIdle);
      enable_irq(IRQ_MEDIASWITCH);        
      printk("Stopping immediate on %s\n",me->name);
    }else{
      DmaChSetState(me,tyStopping);
    }
    goto good_cleanup;
  case tyStopping:
    goto bad_cleanup;
  default:
    goto bad_cleanup;
  }
 good_cleanup:
  DmaChMutex(me,FALSE);  
  return 0;
 bad_cleanup:
  DmaChMutex(me,FALSE);    
  DmaChException(me);
  return -1;
}

#endif

int
DmaChStart(TyDmaChT channel)
{
  DmaChT *me;

  me = CHANNEL_TO_PTR(channel);
//if (channel > 0)  printk("\n\nDmaChStart on %s\n",me->name);
  DEBUG(DEBUG_ALL,"\n\nDmaChStart on %s\n",me->name);

  DmaChMutex(me,TRUE);
  me->event = tyStartMove;
  switch(DmaChGetState(me)) {
  case tyIdle:
    goto bad_cleanup;
  case tyStopped:
    me->fragsLeft = 0;
    StartTransfer(me); /* return not checked cause of assert */
    DmaChSetState(me,tyNextFull);
    goto good_cleanup;
  case tyNextFull:
    {
      DmaChT *sg_me = me;
      /*
       * We shouldn't ever get here. FIXME
       * Find out why we do!!!
       */
#ifdef IDESG
      if (channel == tyDmaChIde && ms4probe()) {
	sg_me = &DmaChArray[tyDmaChIdeSG];
      }
#endif

      if (MsRead(sg_me->curCountReg)==0xC0000000){
	WARNING("FOUND %s IDLE..RESTARTING\n",me->name);
	sg_me->fragsLeft=0;
	
	StartTransfer(sg_me);
	DmaChSetState(sg_me,tyNextFull);
      }
    goto good_cleanup; /* nop */
    }
  case tyCurFull:
    StartTransfer(me); /* return not checked cause of assert */
    DmaChSetState(me,  tyCurNextFull);
    goto good_cleanup;
  case tyCurNextFull:
    goto good_cleanup;
  default:
    goto bad_cleanup;
  }
  
 bad_cleanup:
  DmaChMutex(me,FALSE);  
  DmaChException(me);
  return -1;
 good_cleanup:
  DmaChMutex(me,FALSE);    
  return 0;
}




int
DmaChNextIrq(TyDmaChT channel)
{
  DmaChT *me;

  me = CHANNEL_TO_PTR(channel);

  me->event = tyNextIrq;
  DEBUG(DEBUG_STATES,"\n\n%s: DmaChNextIrq\n",me->name);
  switch(DmaChGetState(me)){
  case tyRingingSoon:
    DmaChSetState(me,tyRinging);
    goto good_cleanup;
  case tyRinging:
    /*    dma_flush_cache_range(me->ringBuf,me->ringBuf+me->ringLen); */
    DmaChNotify(me);
    goto good_cleanup;
  case tyCurFull:
    DEBUG(DEBUG_ALL,"spurious\n");
    goto good_cleanup;
  case tyNextFull:
    NextIrqStuff(me);
    if (StartTransfer(me)) {
      DmaChSetState(me,tyCurNextFull);
    }else{
      DmaChSetState(me,tyCurFull);
    }
    goto good_cleanup;
    
  case tyCurNextFull:
    NextIrqStuff(me);
    if (StartTransfer(me)) {
      DmaChSetState(me,tyCurNextFull);
    }else{
      DmaChSetState(me,tyCurFull);
    }
    goto good_cleanup;
  case tyIdle:      /* fallthru */
  case tyStopping:
    goto good_cleanup; /* nop */
  case tyStopped:
#ifdef IDESG_D
printk("%s %s\n", __FUNCTION__, me->name);
#endif
    goto bad_cleanup;
  default:
    goto bad_cleanup;
  }
  
 good_cleanup:
  return 0;
 bad_cleanup:
  DmaChException(me);
  return -1;
}



bool
DmaChIdle(TyDmaChT channel)
{
  DmaChT *me;
  me = CHANNEL_TO_PTR(channel);

  me->event = tyReady;
  switch(DmaChGetState(me)){
  case tyNextFull:
  case tyCurNextFull:
  case tyCurFull:
  case tyStopped:
    return FALSE;
  case tyIdle:
    return TRUE;
  default:
    DmaChException(me);
    return FALSE;
  }
}

int
DmaChIdleIrq(TyDmaChT channel)
{
  DmaChT *me;

  me = CHANNEL_TO_PTR(channel);

  DEBUG(DEBUG_STATES,"\n\n%s: DmaChIdleIrq\n",me->name);    
  me->event=tyIdleIrq;
  switch(DmaChGetState(me)){
  case tyIdle:
    WARNING("%s: warning: DmaChIdleIrq in state tyIdle\n",me->name);
    goto good_cleanup;
  case tyStopped:    /* fallthru */
  case tyNextFull:
#ifdef IDESG_D
printk("%s %s\n", __FUNCTION__, me->name);
#endif
    goto bad_cleanup;
  case tyCurFull:
    IdleIrqStuff(me);
    if (StartTransfer(me)) {
      DmaChSetState(me,tyNextFull);
    }else{
      DmaChSetState(me,tyIdle);
    }
    goto good_cleanup;    
  case tyCurNextFull:
    /*
     * We are getting this because we really go a next and idle irq
     * but don't have one in the kernel. Backtrack the state.
     */
    DmaChSetState(me,tyNextFull);
    DEBUG(DEBUG_ALL,"NEXT AND IDLE IRQ\n");
    goto good_cleanup;
#ifdef NEXT_AND_IDLE_IRQ    
    goto bad_cleanup;
#endif    
  case tyStopping:
     IdleIrqStuff(me);    
    DmaChSetState(me,tyStopped);
    goto good_cleanup;
  default:
    goto bad_cleanup;
  }
 good_cleanup:
  return 0;
 bad_cleanup:
  DmaChException(me);
  return -1;  
}




/*
  
State Machine for  wrapping channels:

Event RingStart:
   tyIdle -> tyRingingSoon
   all other states -> return error

Event RingStop:
   tyRinging -> tyIdle;Notify();
   all other states -> return error;

Event NextIrq:
   tyRinging -> tyRinging;Notify();
   tyRingingSoon ->tyRinging
   all other states -> panic()
   
Event IdleIrq
   all states -> panic()
   
*/

int
DmaChGetAddr(TyDmaChT channel, U32 *addr){
  DmaChT *me = &DmaChArray[channel];
  int res = 0;
  *addr = 0;
  //  DEBUG(DEBUG_ALL,"\n\nRingGetAddr on %s\n",me->name);        
  DmaChMutex(me,TRUE);
  switch(DmaChGetState(me)) {
  case tyRingingSoon:
//	    *addr = MsRead(me->nextAddrReg);
    //    *addr =  MsRead(me->curAddrReg);
    *addr = me->ringBuf;
    goto cleanup;
  case tyRinging:
    *addr =  MsRead(me->curAddrReg);
    goto cleanup;
  case tyIdle:
    //    *addr =  0;
    *addr = me->ringBuf;
    //    printk("idle on me->name\n");
    goto cleanup;
  default:
    res = -1;
    //printk("def\n");
    goto cleanup;
  }
 cleanup:
  DmaChMutex(me,FALSE);
  return res;
}


int DmaChRingStart(TyDmaChT channel, U32 buf, U32 length){
  int res = 0;
  volatile unsigned long syscntl;
  DmaChT *me = &DmaChArray[channel];
  DEBUG(DEBUG_ALL,"\n\nRingStart on %s\n",me->name);
#ifdef IDESG
  if (ms4probe() && channel == tyDmaChIde) {
    panic("Should not do a DmaChRingStart w/ Ide !! \n");
  }
#endif

#ifdef IDESG_D
printk("\n\nRingStart on %s\n",me->name);
#endif
  DmaChMutex(me,TRUE);
  me->event = tyRingStart;
  DEBUG(DEBUG_ALL,"tyRingStart = %d\n",(int)tyRingStart);
#ifdef IDESG_D
printk("tyRingStart = %d\n",(int)tyRingStart);
#endif
  me->ringBuf = buf;
  me->ringLen = length;

  /* stop current dma */
  MsWrite(me->nextCountReg, 0);
  MsWrite(me->curCountReg, 0);

  /*
   * reset fifo and bring it out of reset
   */
  if (me->onMediaSwitch2 || channel == tyDmaChOSLinkIn2) {
    syscntl = MsRead(MS2_SYSTEM_REGISTER)|me->resetFifoMask;
    DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(unsigned int)syscntl);
    MsWrite(MS2_SYSTEM_REGISTER,syscntl);
    syscntl &= ~(me->resetFifoMask);
    DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(unsigned int)syscntl) ; 
    MsWrite(MS2_SYSTEM_REGISTER,syscntl);
  } else {
    syscntl = MsRead(MS_SYSTEM_REGISTER)|me->resetFifoMask;
    DEBUG(DEBUG_ALL,"resetting fifo to 1 %x\n",(unsigned int)syscntl);
    MsWrite(MS_SYSTEM_REGISTER,syscntl);
    syscntl &= ~(me->resetFifoMask);
    DEBUG(DEBUG_ALL,"resetting fifo to 0 %x\n",(unsigned int)syscntl) ; 
    MsWrite(MS_SYSTEM_REGISTER,syscntl);
  }

  
  switch(DmaChGetState(me)) {
    volatile unsigned long res;

  case tyIdle:
    DmaChSetState(me,tyRingingSoon);
    DEBUG(DEBUG_ALL,"Writing next addr %x with %x\n",me->nextAddrReg,buf);
    MsWrite(me->curAddrReg, buf);
    res = MsRead(me->curAddrReg);
    if ((buf & ~(1<<31)) != (res & ~(1<<31))) {
      printk("buf %x, read %lx\n",buf,res);
      debug("dma prob", 0);
    }
    MsWrite(me->nextAddrReg, buf);

    DEBUG(DEBUG_ALL,"Writing next count %x with %x\n",me->nextCountReg,
	  length | DMA_NEXT_ENABLE_MASK_WITH_RING);
    MsWrite(me->nextCountReg,length | DMA_NEXT_ENABLE_MASK_WITH_RING);
    goto cleanup;
  case tyRingingSoon:
    //    DmaChSetState(me,tyRinging);
    printk("bizarre\n");
    goto cleanup;
  default:
    res = -1;
    goto cleanup;
  }
cleanup:
  DmaChMutex(me,FALSE);
  return res;  
}

int DmaChRingStop(TyDmaChT channel){
  int res = 0;
  DmaChT *me;
  me = CHANNEL_TO_PTR(channel);
  
  DEBUG(DEBUG_ALL,"\n\nRingStop on %s\n",me->name);        
  DmaChMutex(me,TRUE);
  me->event = tyRingStop;
  switch(DmaChGetState(me)) {
  case tyRinging:
  case tyRingingSoon:
    MsWrite(me->nextCountReg,0);
    MsWrite(me->curCountReg, 0);
    DmaChSetState(me,tyIdle);
    goto cleanup;
  default:
    res = -1;
    goto cleanup;
  }
 cleanup:
  if (DmaChGetState(me)!=tyIdle) {
    DmaChMutex(me,FALSE);
  }
  return res;
}

void
DmaChDumpState(int channel) {
  DmaChT *me = &DmaChArray[channel];
  WARNING("\nName: %s\nState: %s\n",me->name,dmaChStateStringsG[me->state]);
}

