www.pudn.com > scaling.rar > c6x1x_edma_mcasp.c


/* 
 *  Copyright 2003 by Texas Instruments Incorporated. 
 *  All rights reserved. Property of Texas Instruments Incorporated. 
 *  Restricted rights to use, duplicate or disclose this code are 
 *  granted through contract. 
 *   
 */ 
/* "@(#) DDK 1.10.00.23 07-02-03 (ddk-b12)" */ 
/*  
 *  ======== c6x1x_edma_mcasp.c ======== 
 *  
 *  Generic McASP driver for the TMS320C6x1x series. Uses the EDMA. 
 */ 
 
#include  
#include  
#include  
#include  
 
#include  
 
#include  
#include  
#include  
#include  
#include  
#include  
 
#include  
 
/* 
 *  Macro to start serializer & state machine 
 *  if turned on, driver will start both transmit and receive serializer  
 *  and state machine synchronously   
 */ 
#define STARTSERIALIZERSYNC     0 
 
/* 
 *  Macro to enable edma loop job interrupt 
 *  if turned on, each time edma loop job gets running, an ineterrupt will 
 *  be generated. This is only used for debug purpose. 
 */ 
#define ENABLELOOPINTR          0  
 
#if ENABLELOOPINTR 
#include  
extern far LOG_Obj trace; 
#endif 
 
#define IRQEVTINPUT             5 
#define IRQEVTOUTPUT            6 
#define IRQEDMA                 8 
 
 
/* Maximum number of EDMA jobs linked at a time (Must be 2). */ 
#define MAXLINKCNT 2 
 
/* Used as index since IOM mode is a bit mask and not an index */ 
#define INPUT   0 
#define OUTPUT  1 
 
/* States for chanCleanUp() */ 
#define SETFALSE 1 
#define FREETCC 2 
#define FREETABLE 3 
#define FREETABLEEX 4 
#define DELCHAN 5 
 
/* Macro to increment and return the indice that ranges over MAXLINKCNT */ 
#define nextIndex(index) ((index) ^ 1) 
 
/* Number of ports available */ 
#define NUMPORTS _MCASP_PORT_CNT  
 
/* Number of channels per port (one input and one output channel) */ 
#define NUMCHANS 2 
 
/* Structure containing channel specific variables */ 
typedef struct ChanObj { 
    Uns inUse;                /* True if the channel is in use */ 
    Int mode;                 /* Input or output channel */ 
    struct PortObj *port;     /* Pointer to the port which owns this chan */ 
    EDMA_Handle xferPram;     /* Handle to transfer PaRAM */ 
    EDMA_Handle pramTbl[MAXLINKCNT]; /* Handles to link PaRAMs */ 
    EDMA_Handle prevPramPtr;  /* Points to the PaRAM last used */ 
    EDMA_Handle loophEdma;    /* Handle to the Loop job PaRAM */ 
    IOM_Packet *flushPacket;  /* Holds the flushpacket (if any) */ 
    IOM_Packet *abortPacket;  /* Holds the abortpacket (if any) */ 
    IOM_Packet *packetList[MAXLINKCNT]; /* Holds linked  packets */ 
    QUE_Obj packetQueue;      /* Holds submitted but not linked packets */ 
    Int submitCount;          /* Number of submit calls pending */ 
    Int writeIndex;           /* Index of next PaRAM to write to */ 
    Int readIndex;            /* Index of next PaRAM to read from */ 
    Int tcc;                  /* Channel transfer complete code */ 
    IOM_TiomCallback cbFxn;   /* Called when I/O complete */ 
    Ptr cbArg;                /* Argument to callback function */ 
    Bool pause;    
    Fxn avSyncCbFxn; 
} ChanObj, *ChanHandle; 
 
/* Structure containing port specific variables */ 
typedef struct PortObj { 
    Uns inUse;                /* True if the port is in use */ 
    Int devId;                /* The device id passed to mdBindDev() */ 
    Bool cacheCalls;          /* Submitted buffers are in cacheable memory */ 
    Uint32 enableHclkg;       /* Holds enable Hclk variable */ 
    Uint32 enableClkg;        /* Holds enable Clk variable */ 
    Uint32 enableFsyncg;      /* Holds enable Fsync variable */ 
    MCASP_Handle hMcasp;      /* CSL Device handle */ 
    ChanObj chans[NUMCHANS];  /* The channels associated with the port */ 
    Uns chanCreated;          /* One channel in this port has been Created */ 
    C6X1X_EDMA_MCASP_TevtCallback evtCallback; /* event callback */ 
    Uns evtMask;              /* registered events */ 
} PortObj, *PortHandle; 
 
/* Declare the port and channel structures */ 
static PortObj ports[NUMPORTS]; 
 
/* Define EDMA Event Id's Array */ 
static Uns eventIds[NUMPORTS][2] = { 
    { EDMA_CHA_AREVT0, EDMA_CHA_AXEVT0 }, 
#if NUMPORTS >= 2 
    { EDMA_CHA_AREVT1, EDMA_CHA_AXEVT1 }, 
#endif 
#if NUMPORTS == 3 
    { EDMA_CHA_AREVT2, EDMA_CHA_AXEVT2 } 
#endif 
}; 
 
/* 
 * Forward declaration of the IOM interface functions. They are only 
 * exposed via the IOM function table to avoid namespace pollution. 
 */ 
static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams); 
static Int mdCreateChan(Ptr *chanp, Ptr devp, String name, Int mode, 
                        Ptr chanParams, IOM_TiomCallback cbFxn, Ptr cbArg); 
                         
static Int mdControlChan(Ptr *chanp, Int cmd, Arg arg);                         
static Int mdDeleteChan(Ptr chanp); 
static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet); 
static Int mdUnBindDev(Ptr devp); 
 
#if ENABLELOOPINTR 
static Void isrLoop(Int tcc); 
#endif 
 
/* Public IOM interface table */ 
IOM_Fxns C6X1X_EDMA_MCASP_FXNS = { 
    &mdBindDev, 
    &mdUnBindDev, 
    &mdControlChan, 
    &mdCreateChan, 
    &mdDeleteChan, 
    &mdSubmitChan 
}; 
 
/* Local function prototypes */ 
static Void chanCleanUp(ChanHandle chan, Uns state); 
static Void isrCommon(ChanHandle chan); 
static Void isrInput(Int tcc); 
static Void isrOutput(Int tcc); 
static Void isrEvent(Int mode); 
static Void linkPacket(ChanHandle chan, IOM_Packet *packet); 
 
/* Local driver variables. */ 
static Uint32 loopDstBuf; 
static Uint32 loopSrcBuf; 
 
#if ENABLELOOPINTR 
static Void isrLoop(Int tcc) 
{ 
    LOG_printf(&trace, "LOOP=%d", tcc); 
} 
#endif 
 
/* 
 * ======== chanCleanUp ======== 
 * Cleans up the channel resources. 
 */ 
static Void chanCleanUp(ChanHandle chan, Uns state) 
{ 
    switch(state) { 
    case DELCHAN: 
        /* Close the EDMA channel */ 
        EDMA_close(chan->xferPram); 
 
        /* Disable transfer interrupts from the EDMA */ 
        EDMA_intDisable(chan->tcc); 
        /* will fall through the next case */ 
    case FREETABLEEX: 
        /* Free the EDMA link PaRAM tables */ 
        EDMA_freeTableEx(MAXLINKCNT, chan->pramTbl); 
        /* will fall through the next case */ 
    case FREETABLE: 
        /* Free the loop EDMA PaRAM table */ 
        EDMA_freeTable(chan->loophEdma); 
        /* will fall through the next case */ 
    case FREETCC: 
        /* Free the transfer complete code */ 
        EDMA_intFree(chan->tcc); 
        /* will fall through the next case */ 
    case SETFALSE: 
        /* Mark the channel as closed */ 
        chan->inUse = FALSE; 
        break; 
    } 
} 
/* 
 * ======== isrCommon ======== 
 * Shared ISR code between input and output. Processes a normal EDMA job. 
 */ 
static Void isrCommon(ChanHandle chan) 
{ 
    IOM_Packet *packet; 
    Int cnt; 
 
    /* Check to see if this is a completed async abort call */ 
    if (chan->abortPacket) { 
        /* Discard all packets in transmission or queued up */ 
        if (chan->submitCount > MAXLINKCNT) { 
            cnt = MAXLINKCNT; 
        } 
        else { 
            cnt = chan->submitCount; 
        } 
 
        while (cnt > 0) { 
            packet = chan->packetList[chan->readIndex]; 
            packet->status = IOM_ABORTED; 
            (*chan->cbFxn)(chan->cbArg, packet); 
 
            chan->readIndex = nextIndex(chan->readIndex); 
            cnt--; 
        } 
 
        while (!QUE_empty(&chan->packetQueue)) { 
            packet = QUE_dequeue(&chan->packetQueue); 
            packet->status = IOM_ABORTED; 
            (*chan->cbFxn)(chan->cbArg, packet); 
        } 
 
        /* Reset the driver channel state */ 
        chan->writeIndex = 0; 
        chan->readIndex = 0; 
        chan->submitCount = 0; 
 
        chan->abortPacket->status = IOM_COMPLETED; 
        (*chan->cbFxn)(chan->cbArg, chan->abortPacket); 
        chan->abortPacket = NULL; 
        return; 
    } 
 
    /* Fetch the completed packet */ 
    packet = chan->packetList[chan->readIndex]; 
 
    chan->readIndex = nextIndex(chan->readIndex); 
 
    /* Mark the packet as completed */ 
    packet->status = IOM_COMPLETED; 
 
    /* Call the callback function */ 
    (*chan->cbFxn)(chan->cbArg, packet); 
 
    chan->submitCount--; 
 
    /* 
     * See if there are any unlinked packets in the packetQueue 
     * and if so link them. 
     */ 
    if (chan->submitCount >= MAXLINKCNT) { 
        packet = QUE_dequeue(&chan->packetQueue); 
        linkPacket(chan, packet); 
    } 
} 
 
/* 
 * ======== isrInput ======== 
 * The input isr called from the EDMA dispatcher every time an input 
 * EDMA job completes. 
 */ 
static Void isrInput(Int tcc) 
{ 
    ChanHandle chan; 
    Int portNbr; 
 
    /* Check which port was responsible for the interrupt */ 
    for (portNbr = 0; portNbr < NUMPORTS; portNbr++) { 
        chan = &ports[portNbr].chans[INPUT]; 
 
        if (chan->tcc == tcc && chan->inUse) { 
 
            if (EDMA_RGETH(chan->xferPram, DST) == (Uint32) &loopDstBuf && 
                chan->submitCount > 1 && !chan->abortPacket) { 
                /* 
                 * An emulation halt has occured with more than 1 job 
                 * submitted. Link the currently executing job (the Loop job) 
                 * to the first of the linked jobs which hadn't been called 
                 * back. This way we still have the same number of submitted 
                 * jobs after the execution continues as we had before the 
                 * emulation halt (breakpoint) occured (this preserves double 
                 * buffering if used). 
                 */ 
                //EDMA_disableChannel(chan->xferPram); 
                EDMA_link(chan->xferPram, chan->pramTbl[chan->readIndex]); 
                //EDMA_enableChannel(chan->xferPram); 
            } 
            else { 
                /* Call the common ISR code for a finished normal EDMA job */ 
                isrCommon(chan); 
            } 
        } 
    } 
} 
 
/* 
 * ======== isrOutput ======== 
 * The output isr called from the EDMA dispatcher every time an output 
 * EDMA job completes. 
 */ 
static Void isrOutput(Int tcc) 
{ 
    ChanHandle chan; 
    Int portNbr;      
     
    /* Check which port was responsible for the interrupt */ 
    for (portNbr = 0; portNbr < NUMPORTS; portNbr++)  
    { 
        chan = &ports[portNbr].chans[OUTPUT]; 
        if (chan->tcc == tcc && chan->inUse )  
        {                              
			if(chan->avSyncCbFxn != NULL) { 
			    chan->avSyncCbFxn(); /* add hook for the a/v sync call-back function */ 
			} 
            if(!chan->pause) 
            { 
            	if (EDMA_RGETH(chan->xferPram, SRC) == (Uint32)&loopSrcBuf && 
	                chan->submitCount > 1 && !chan->abortPacket) { 
	                /* 
	                 * An emulation halt has occured with more than 1 job 
	                 * submitted. Link the currently executing job (the Loop job) 
	                 * to the first of the linked jobs which hadn't been called 
	                 * back. This way we still have the same number of submitted 
	                 * jobs after the execution continues as we had before the 
	                 * emulation halt (breakpoint) occured (this preserves double 
	                 * buffering if used). 
	                 */ 
	                //EDMA_disableChannel(chan->xferPram); 
	                EDMA_link(chan->xferPram, chan->pramTbl[chan->readIndex]); 
	                //EDMA_enableChannel(chan->xferPram); 
	            	} 
	            	else { 
	                /* Call the common ISR code for a finished normal EDMA job */ 
	                isrCommon(chan); 
	 
	                /* Check to see if an async flush has completed */ 
	                if (chan->submitCount == 0 && chan->flushPacket) { 
	                    chan->flushPacket->status = IOM_COMPLETED; 
	                    (*chan->cbFxn)(chan->cbArg,chan->flushPacket); 
	                    chan->flushPacket = NULL; 
	                } 
	            } 
            } 
        } 
    } 
} 
 
/* 
 * ======== isrEvent ======== 
 * The event isr called when an McASP input/output event happens 
 * This is used for exception event handling asserted by McASP. Normal data 
 * hanlding is done by EDMA.  
 */ 
static Void isrEvent(Int mode) 
{ 
    MCASP_Handle hMcasp; 
    Uns portNbr; 
    Uns events; 
    Uns eventReturn; 
     
    /* Check which port was responsible for the interrupt */ 
    for (portNbr = 0; portNbr < NUMPORTS; portNbr++) { 
        if (ports[portNbr].inUse) { 
            hMcasp = ports[portNbr].hMcasp; 
            events = 0; 
            if (mode == INPUT) {           
                if (MCASP_FGETH(hMcasp, RSTAT, ROVRN)) { 
                    MCASP_FSETSH(hMcasp, RSTAT, ROVRN, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_ROVRN; 
                }  
                if (MCASP_FGETH(hMcasp, RSTAT, RCKFAIL)) { 
                    MCASP_FSETSH(hMcasp, RSTAT, RCKFAIL, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_RCKFAIL; 
                } 
                if (MCASP_FGETH(hMcasp, RSTAT, RSYNCERR)) { 
                    MCASP_FSETSH(hMcasp, RSTAT, RSYNCERR, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_RSYNCERR; 
                }    
                if (MCASP_FGETH(hMcasp, RSTAT, RDMAERR)) { 
                    MCASP_FSETH(hMcasp, RSTAT, RDMAERR, 1); 
                    events |= C6X1X_EDMA_MCASP_EVT_RDMAERR; 
                }    
                /* Clear not registered events here */ 
                MCASP_RSETH(hMcasp, RSTAT, 0xFFFFFFFF); 
            }  
            else { 
                if (MCASP_FGETH(hMcasp, XSTAT, XUNDRN)) { 
                    MCASP_FSETSH(hMcasp, XSTAT, XUNDRN, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_XUNDRN; 
                }  
                if (MCASP_FGETH(hMcasp, XSTAT, XCKFAIL)) { 
                    MCASP_FSETSH(hMcasp, XSTAT, XCKFAIL, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_XCKFAIL; 
                } 
                if (MCASP_FGETH(hMcasp, XSTAT, XSYNCERR)) { 
                    MCASP_FSETSH(hMcasp, XSTAT, XSYNCERR, YES); 
                    events |= C6X1X_EDMA_MCASP_EVT_XSYNCERR; 
                }    
                if (MCASP_FGETH(hMcasp, XSTAT, XDMAERR)) { 
                    MCASP_FSETH(hMcasp, XSTAT, XDMAERR, 1); 
                    events |= C6X1X_EDMA_MCASP_EVT_XDMAERR; 
                }    
                /* Clear not registered events here */ 
                MCASP_RSETH(hMcasp, XSTAT, 0xFFFFFFFF); 
            } 
            eventReturn = ports[portNbr].evtMask & events; 
  
            if (eventReturn) { 
                (*ports[portNbr].evtCallback)(eventReturn, portNbr); 
            }  
        }    
    }     
} 
 
/* 
 * ======== linkPacket ======== 
 * Links a packet with the EDMA. When called by mdSubmitChan() it is called 
 * with all interrupts disabled, but when called by an ISR only the EDMA IRQ 
 * is disabled. 
 */ 
static Void linkPacket(ChanHandle chan, IOM_Packet *packet) 
{ 
    EDMA_Handle pramPtr; 
    Uns edmaCnt; 
 
    /* Store the packet in the packetList */ 
    chan->packetList[chan->writeIndex] = packet; 
 
    /* Set up pointer to link PaRAM to write submit job info to */ 
    pramPtr = chan->pramTbl[chan->writeIndex]; 
    chan->writeIndex = nextIndex(chan->writeIndex); 
 
    /* Load the buffer pointer into the EDMA */ 
    if (chan->mode == INPUT) { 
        EDMA_RSETH(pramPtr, DST, (Uint32) packet->addr); 
    } 
    else { 
        EDMA_RSETH(pramPtr, SRC, (Uint32) packet->addr); 
    } 
 
    /* 
     * Load the transfer count (in samples) into the EDMA. Use the ESIZE 
     * field of the EDMA job to calculate number of samples. 
     */ 
    edmaCnt = (Uint32)packet->size >> (2 - EDMA_FGETH(pramPtr, OPT, ESIZE)); 
    EDMA_FSETH(pramPtr, CNT, FRMCNT, (edmaCnt /  
            EDMA_FGETH(pramPtr, CNT, ELECNT) - 1));  
 
    /* 
     * Link to loop EDMA job upon termination. This way we won't 
     * loose the frame sync if the channel is starved. 
     */ 
    EDMA_link(pramPtr, chan->loophEdma); 
 
    /* Disable the EDMA channel to make sure current job doesn't complete */ 
    //EDMA_disableChannel(chan->xferPram); 
 
    /* 
     * Link the currently executing job to the new job. This can be 
     * either the loop EDMA job or a real data EDMA job. 
     */ 
    EDMA_link(chan->xferPram, pramPtr); 
 
    if (chan->submitCount > 0) { 
        /* 
         * We need to link the parameter space corresponding to the running 
         * job so that if a breakpoint occurs, we know how to recover. 
         */ 
        EDMA_link(chan->prevPramPtr, pramPtr); 
    } 
 
    /* Reenable the EDMA channel */ 
    //EDMA_enableChannel(chan->xferPram); 
 
    /* Save the new job for the loop above for next time */ 
    chan->prevPramPtr = pramPtr; 
} 
 
/* 
 * ======== mdBindDev ======== 
 * This function allocates and configures the McASP port specified by devId. 
 */ 
static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams) 
{ 
    Uns old; 
    Int inIrqId; 
    int outIrqId; 
    int irqId; 
    PortHandle port; 
    HWI_Attrs hwiAttrs; 
    C6X1X_EDMA_MCASP_DevParams *params = 
        (C6X1X_EDMA_MCASP_DevParams *) devParams; 
 
    /* This driver must receive a valid devparams */ 
    if (params == NULL) { 
        return (IOM_EBADARGS); 
    } 
 
    /* Check the version number */ 
    if (params->versionId != C6X1X_EDMA_MCASP_VERSION_1){ 
        /* Unsupported version */ 
        return(IOM_EBADARGS); 
    } 
 
    /* Get the device parameters of the specified port */ 
    port = &ports[devid]; 
 
    /* Mark the port as in use */ 
    old = ATM_setu(&(port->inUse), TRUE); 
 
    /* Check if the port was already bound */ 
    if (old) { 
        return (IOM_EALLOC); 
    } 
 
    /* Map the supplied IRQ to the EDMA event */ 
    if (params->irqId > 0 ) { 
        irqId = params->irqId; 
    } 
    else { 
        irqId = IRQEDMA; 
    } 
    IRQ_map(IRQ_EVT_EDMAINT, irqId); 
 
    hwiAttrs.intrMask = params->edmaIntrMask; 
    hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ 
    hwiAttrs.arg = NULL; 
 
    /* Plug the EDMA dispatcher into the HWI dispatcher */ 
    HWI_dispatchPlug(irqId, (Fxn)EDMA_intDispatcher, -1, &hwiAttrs); 
    
    /* Set the McASP high frequency sample rate generator */ 
    /* Set the McASP sample rate generator */ 
    /* Set the McASP frame sync generator */ 
    port->enableHclkg = params->enableHclkg; 
    port->enableClkg = params->enableClkg; 
    port->enableFsyncg = params->enableFsyncg; 
 
    /* True if buffers are in external memory */ 
    port->cacheCalls = params->cacheCalls; 
 
    /* Store the devid */ 
    port->devId = devid; 
 
    /* No channel create yet */ 
    port->chanCreated = 0; 
 
    /* Open and reset the McASP */ 
    port->hMcasp = MCASP_open(devid, MCASP_OPEN_RESET); 
 
    if (port->hMcasp == INV) { 
        return (IOM_EALLOC); 
    } 
 
    /* Configure the McASP with the supplied configuration */ 
    MCASP_config(port->hMcasp, params->mcaspCfgPtr); 
 
    if (params->evtCallback != NULL) { 
        /* register the events */ 
        port->evtCallback = params->evtCallback->evtFxn; 
        port->evtMask = params->evtCallback->evtMask; 
  
        if (params->inEvtIrqId > 0) { 
            inIrqId = params->inEvtIrqId; 
        } 
        else { 
            inIrqId = IRQEVTINPUT; 
        }  
 
        if (params->outEvtIrqId > 0) { 
            outIrqId = params->outEvtIrqId; 
        } 
        else { 
            outIrqId = IRQEVTOUTPUT; 
        }  
 
        IRQ_map(MCASP_getXmtEventId(port->hMcasp), outIrqId); 
        IRQ_map(MCASP_getRcvEventId(port->hMcasp), inIrqId);  
 
        hwiAttrs.intrMask = params->inEvtIntrMask; 
        hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ 
        hwiAttrs.arg = INPUT; 
        HWI_dispatchPlug(inIrqId, (Fxn)isrEvent, -1, &hwiAttrs); 
 
        hwiAttrs.intrMask = params->outEvtIntrMask; 
        hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */ 
        hwiAttrs.arg = OUTPUT; 
        HWI_dispatchPlug(outIrqId, (Fxn)isrEvent, -1, &hwiAttrs); 
    }  
    else { 
        port->evtCallback = NULL; 
    } 
 
    /* set 0xe for debug, real value should be 0 */ 
    loopSrcBuf = 0; 
    loopDstBuf = 0;  
 
    /* Return the device handle and a status code for success */ 
    *devp = port; 
    return (IOM_COMPLETED); 
} 
 
 
static Int mdControlChan(Ptr *chanp, Int cmd, Arg arg) 
{     
    ChanHandle chan = (ChanHandle) chanp; 
    Int imask; 
    if(cmd == C6X1X_EDMA_MCASP_CMD_PAUSE) { 
        chan->pause = TRUE;  
        /* Disable interrupts to protect submitCount */ 
        imask = HWI_disable(); 
 
        if (chan->submitCount > 0) { 
 
            /* 
             * Disable the EDMA channel while linking the currently 
             * executing job while linking it with the Loop job to make 
             * sure the currently executing job doesn't complete before 
             * the link is complete. 
             */ 
            //EDMA_disableChannel(chan->xferPram); 
            EDMA_link(chan->xferPram, chan->loophEdma); 
            //EDMA_enableChannel(chan->xferPram); 
        } 
 
        /* Reenable interrupts */ 
        HWI_restore(imask); 
         
     
    } else if(cmd == C6X1X_EDMA_MCASP_CMD_RESUME && chan->pause == TRUE) { 
        chan->pause = FALSE; 
        if(chan->submitCount > 0 && !chan->abortPacket) { 
            /* 
             * An emulation halt has occured with more than 1 job 
             * submitted. Link the currently executing job (the Loop job) 
             * to the first of the linked jobs which hadn't been called 
             * back. This way we still have the same number of submitted 
             * jobs after the execution continues as we had before the 
             * emulation halt (breakpoint) occured (this preserves double 
             * buffering if used). 
             */ 
            //EDMA_disableChannel(chan->xferPram); 
            EDMA_link(chan->xferPram, chan->pramTbl[nextIndex(chan->writeIndex)]); 
            //EDMA_enableChannel(chan->xferPram); 
        } 
    } else if(cmd == C6X1X_EDMA_MCASP_CMD_SET_AVSYNC_CALLBACK) { 
         chan->avSyncCbFxn = (Fxn)arg; 
    } 
    return IOM_COMPLETED; 
} 
 
 
/* 
 * ======== mdCreateChan ======== 
 * This function creates and configures a device channel. 
 */ 
static Int mdCreateChan(Ptr *chanp, Ptr devp, String name, Int mode, 
                        Ptr chanParams, IOM_TiomCallback cbFxn, Ptr cbArg) 
{ 
    PortHandle port = (PortHandle) devp; 
    C6X1X_EDMA_MCASP_ChanParams *params = 
        (C6X1X_EDMA_MCASP_ChanParams *) chanParams; 
    ChanHandle chan; 
    Uns old; 
    Uns esize; 
    Uns cnt; 
    Uns fs; 
    Uns elerld; 
    Uns mcaspAddr; 
    Int edmaChanEvent; 
    Int i; 
#if ENABLELOOPINTR 
    Int loopTcc; 
#endif 
 
    /* 
     * Configuration structure for the loop EDMA job. If the McASP 
     * is left to free running, this loop job is running when there is 
     * no data to transfer. This is useful if the McASP is externally 
     * clocked.  If the driver is starved or if an emulation halt 
     * (breakpoint) occurs, the frame sync will still be correct when 
     * the driver continues transmitting data. 
     */ 
    EDMA_Config loopEdmaCfg = { 
        EDMA_FMKS(OPT, PRI, HIGH)           | 
        EDMA_FMKS(OPT, ESIZE, 32BIT)        | 
        EDMA_FMKS(OPT, 2DS, NO)             | 
        EDMA_FMKS(OPT, SUM, NONE)           | 
        EDMA_FMKS(OPT, 2DD, NO)             | 
        EDMA_FMKS(OPT, DUM, NONE)           | 
        EDMA_FMKS(OPT, TCINT, NO)           | 
        EDMA_FMKS(OPT, TCC, DEFAULT)        | 
        EDMA_FMKS(OPT, LINK, YES)           | 
        EDMA_FMKS(OPT, FS, NO), 
 
        EDMA_FMK (SRC, SRC, NULL), 
 
        EDMA_FMK (CNT, FRMCNT, 0)           | 
        EDMA_FMK (CNT, ELECNT, 0), 
 
        EDMA_FMKS(IDX, FRMIDX, OF(0))       | 
        EDMA_FMKS(IDX, ELEIDX, OF(0)), 
 
        EDMA_FMK (DST, DST, NULL), 
 
        EDMA_FMK (RLD, ELERLD, 0)           | 
        EDMA_FMK (RLD, LINK, NULL) 
    }; 
 
 
    /* This driver needs a valid channel parameter structure passed */ 
    if (params == NULL) { 
        return (IOM_EBADARGS); 
    } 
 
    /* Use own indexes since IOM mode is a bit mask and not an index */ 
    if (mode == IOM_INPUT) { 
        chan = &port->chans[INPUT]; 
        chan->mode = INPUT; 
    } 
    else { 
        chan = &port->chans[OUTPUT]; 
        chan->mode = OUTPUT; 
    } 
 
    /* Mark the channel as used */ 
    old = ATM_setu(&(chan->inUse), TRUE); 
 
    /* Make sure the channel wasn't already created */ 
    if (old) { 
        return (IOM_EALLOC); 
    } 
 
    /* Initialise the channel structure */ 
    chan->cbFxn = cbFxn; 
    chan->cbArg = cbArg; 
    chan->port = port; 
    chan->writeIndex = 0; 
    chan->readIndex = 0; 
    chan->submitCount = 0; 
    chan->flushPacket = NULL; 
    chan->abortPacket = NULL; 
    chan->pause = FALSE;   
    chan->avSyncCbFxn = NULL; 
 
    /* Initialize the packet queue */ 
    QUE_new(&chan->packetQueue); 
 
    /* 
     * Set the number of elements (corresponding to number of McASP TDM 
     * channels) used in the Loop job to preserve the frame sync. 
     * FRMCNT should be (num of TDM slots - 1); 
     */ 
    cnt = params->tdmChans - 1; 
    EDMA_FSETA(&loopEdmaCfg.cnt, CNT, FRMCNT, cnt); 
    EDMA_FSETA(&loopEdmaCfg.cnt, CNT, ELECNT, params->edmaCfgPtr->cnt); 
 
    /* Use the same sample size in the Loop job as in normal jobs */ 
    esize = EDMA_FGETA(¶ms->edmaCfgPtr->opt, OPT, ESIZE); 
    EDMA_FSETA(&loopEdmaCfg.opt, OPT, ESIZE, esize); 
 
    /* Use  the same FS info as in the normal job */ 
    fs = EDMA_FGETA(¶ms->edmaCfgPtr->opt, OPT, FS);  
    EDMA_FSETA(&loopEdmaCfg.opt, OPT, FS, fs); 
 
    /* Use  the same ELERLD info as in the normal job */ 
    elerld = EDMA_FGETA(¶ms->edmaCfgPtr->rld, RLD, ELERLD);  
    EDMA_FSETA(&loopEdmaCfg.rld, RLD, ELERLD, elerld);  
 
    /* Allocate a TCC for the EDMA */ 
    chan->tcc = EDMA_intAlloc(-1); 
    /* If tcc > 15 we abort. */ 
    if (chan->tcc == -1 || chan->tcc > 15) { 
        chanCleanUp(chan, SETFALSE); 
        return (IOM_EALLOC); 
    } 
 
#if ENABLELOOPINTR 
    loopTcc = EDMA_intAlloc(-1); 
    /* If tcc > 15 we abort. */ 
    if (loopTcc == -1 || loopTcc > 15) { 
        EDMA_intFree(loopTcc); 
        chanCleanUp(chan, FREETCC); 
        return (IOM_EALLOC); 
    } 
#endif 
 
    /* Allocate an EDMA PaRAM for the Loop EDMA job. */ 
    chan->loophEdma = EDMA_allocTable(-1); 
 
    if (chan->loophEdma == EDMA_HINV) { 
        chanCleanUp(chan, FREETCC); 
        return (IOM_EALLOC); 
    } 
 
    /* 
     * Allocate EDMA PaRAM link area based on max number of 
     * submits possible. 
     */ 
    if (EDMA_allocTableEx(MAXLINKCNT, chan->pramTbl) != MAXLINKCNT) { 
        chanCleanUp(chan, FREETABLE); 
        return (IOM_EALLOC); 
    } 
 
    /* workaround for big endian problem in McASP */ 
    if (chan->mode == INPUT) { 
        mcaspAddr = MCASP_getRbufAddr(port->hMcasp); 
    } 
    else { 
        mcaspAddr = MCASP_getXbufAddr(port->hMcasp); 
    } 
 
#ifdef _BIG_ENDIAN 
    if (esize == EDMA_OPT_ESIZE_8BIT) { 
        mcaspAddr += 3; 
    } 
    else if (esize == EDMA_OPT_ESIZE_16BIT) { 
        mcaspAddr += 2; 
    } 
#endif 
 
    if (chan->mode == INPUT) { 
         
        /* Put input specific parameters in the Loop EDMA config */ 
        EDMA_RSETA(&loopEdmaCfg.src, SRC, mcaspAddr); 
        EDMA_RSETA(&loopEdmaCfg.dst, DST, (Uint32) &loopDstBuf); 
 
        EDMA_RSETA(¶ms->edmaCfgPtr->src, SRC, mcaspAddr); 
        /* Register our isrInput with the EDMA dispatcher */ 
        EDMA_intHook(chan->tcc, &isrInput); 
    } 
    else {      
         
        /* Put output specific parameters in the Loop EDMA config */ 
        EDMA_RSETA(&loopEdmaCfg.src, SRC, (Uint32) &loopSrcBuf); 
        EDMA_RSETA(&loopEdmaCfg.dst, DST, mcaspAddr); 
 
        EDMA_RSETA(¶ms->edmaCfgPtr->dst, DST, mcaspAddr); 
        /* Register our isrOutput with the EDMA dispatcher */ 
        EDMA_intHook(chan->tcc, &isrOutput); 
    } 
 
    /* Open theEDMA Channel */ 
#if (CHIP_DM642) 
    edmaChanEvent = eventIds[port->devId][chan->mode];  
#else 
    edmaChanEvent = EDMA_map(eventIds[port->devId][chan->mode],  
            params->edmaChan);  
#endif 
    chan->xferPram = EDMA_open(edmaChanEvent, EDMA_OPEN_RESET); 
 
    if (chan->xferPram == EDMA_HINV) { 
        chanCleanUp(chan, FREETABLEEX); 
        return (IOM_EALLOC); 
    } 
 
    /* Program the data EDMA job with the TCC */ 
    EDMA_FSETA(¶ms->edmaCfgPtr->opt, OPT, TCC, chan->tcc); 
 
#if ENABLELOOPINTR 
    EDMA_intHook(loopTcc, &isrLoop); 
    EDMA_FSETA(&loopEdmaCfg.opt, OPT, TCINT, EDMA_OPT_TCINT_YES); 
    EDMA_FSETA(&loopEdmaCfg.opt, OPT, TCC, loopTcc); 
#endif 
 
    /* Program the link PaRAMs with the config struct */ 
    for (i=0; i < MAXLINKCNT; i++) { 
        EDMA_config(chan->pramTbl[i], params->edmaCfgPtr); 
    } 
 
    /* Program the Loop EDMA job with its config struct */ 
    EDMA_config(chan->loophEdma, &loopEdmaCfg); 
 
    /* 
     * Link the Loop EDMA job to itself to make it run 
     * continuously when there is no data to transmit. 
     */ 
    EDMA_link(chan->loophEdma, chan->loophEdma); 
 
    /* Configure the EDMA channel to start with the Loop EDMA job */ 
    EDMA_config(chan->xferPram, &loopEdmaCfg); 
    EDMA_link(chan->xferPram, chan->loophEdma); 
 
    /* Transfer complete is edge triggered, so clear before enabling */ 
    EDMA_intClear(chan->tcc); 
    EDMA_intEnable(chan->tcc); 
 
#if ENABLELOOPINTR 
    EDMA_intClear(loopTcc); 
    EDMA_intEnable(loopTcc); 
#endif 
 
    /* Enable the EDMA interrupt */ 
    IRQ_enable(IRQ_EVT_EDMAINT); 
 
    EDMA_enableChannel(chan->xferPram); 
 
    /* Start the McASP */ 
    if (chan->mode == INPUT) { 
        old = ATM_setu(&(port->chanCreated), TRUE); 
         
        /*  
         * clock generator is divided from high frequency clock, so start high 
         * freq clock generator first  
         */ 
        if (port->enableHclkg == C6X1X_EDMA_MCASP_RCV) { 
            MCASP_enableHclk(port->hMcasp, MCASP_RCV);  
            while(!MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST)); 
        } 
        else if ((port->enableHclkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableHclk(port->hMcasp, MCASP_RCVXMT);  
            while(!(MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST) &  
                    MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST))); 
        } 
 
        if (port->enableClkg == C6X1X_EDMA_MCASP_RCV) { 
            MCASP_enableClk(port->hMcasp, MCASP_RCV);  
            while(!MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST));  
        }  
        else if ((port->enableClkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableClk(port->hMcasp, MCASP_RCVXMT);  
            while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST) &  
                MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST))); 
        }  
 
#if STARTSERIALIZERSYNC 
        if (old) { 
            MCASP_enableSers(port->hMcasp, MCASP_RCVXMT); 
            while (!(MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR) & 
                    MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR))); 
 
            while (MCASP_xdata(port->hMcasp)); 
 
            MCASP_enableSm(port->hMcasp, MCASP_RCVXMT); 
            while (!(MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST) & 
                    MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST)));      
        } 
#else 
        MCASP_enableSers(port->hMcasp, MCASP_RCV); 
        while (!MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR)); 
 
        MCASP_enableSm(port->hMcasp, MCASP_RCV); 
        while (!MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST));      
#endif 
 
        if (port->enableFsyncg == C6X1X_EDMA_MCASP_RCV) { 
              MCASP_enableFsync(port->hMcasp, MCASP_RCV); 
              while(!MCASP_FGETH(port->hMcasp, GBLCTL, RFRST));     
        } 
        else if ((port->enableFsyncg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableFsync(port->hMcasp, MCASP_RCVXMT); 
            while(!(MCASP_FGETH(port->hMcasp, GBLCTL, RFRST) &  
                    MCASP_FGETH(port->hMcasp, GBLCTL, XFRST)) );        
        }  
    } 
    else { 
        old = ATM_setu(&(port->chanCreated), TRUE); 
 
        /*  
         * clock generator is divided from high frequency clock, so start high 
         * freq clock generator first  
         */ 
        if (port->enableHclkg == C6X1X_EDMA_MCASP_XMT) { 
            MCASP_enableHclk(port->hMcasp, MCASP_XMT);  
            while(!MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST)); 
 
        } 
        else if ((port->enableHclkg  == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableHclk(port->hMcasp, MCASP_RCVXMT);  
            while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RHCLKRST) &  
                    MCASP_FGETH(port->hMcasp, GBLCTL, XHCLKRST))); 
        } 
 
        if (port->enableClkg == C6X1X_EDMA_MCASP_XMT) { 
            MCASP_enableClk(port->hMcasp, MCASP_XMT);  
            while(!MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST)); 
        } 
        else if ((port->enableClkg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableClk(port->hMcasp, MCASP_RCVXMT);  
            while(!( MCASP_FGETH(port->hMcasp, GBLCTL, RCLKRST) &  
                    MCASP_FGETH(port->hMcasp, GBLCTL, XCLKRST))); 
        } 
 
 
#if STARTSERIALIZERSYNC 
        if (old) { 
            MCASP_enableSers(port->hMcasp, MCASP_RCVXMT); 
            while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR) & 
                    MCASP_FGETH(port->hMcasp, GBLCTL, RSRCLR))); 
 
            while (MCASP_xdata(port->hMcasp)); 
 
            MCASP_enableSm(port->hMcasp, MCASP_RCVXMT); 
            while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST) & 
                    MCASP_FGETH(port->hMcasp, GBLCTL, RSMRST)));      
        } 
#else 
        MCASP_enableSers(port->hMcasp, MCASP_XMT); 
        while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSRCLR))); 
 
        while (MCASP_xdata(port->hMcasp)); 
 
        MCASP_enableSm(port->hMcasp, MCASP_XMT); 
        while (!(MCASP_FGETH(port->hMcasp, GBLCTL, XSMRST)));      
#endif 
 
        if (port->enableFsyncg == C6X1X_EDMA_MCASP_XMT) { 
            MCASP_enableFsync(port->hMcasp, MCASP_XMT); 
            while(!MCASP_FGETH(port->hMcasp,GBLCTL, XFRST));        
        } 
        else if ((port->enableFsyncg == C6X1X_EDMA_MCASP_RCVXMT) && (old)) { 
            MCASP_enableFsync(port->hMcasp, MCASP_RCVXMT); 
            while(!(MCASP_FGETH(port->hMcasp,GBLCTL, RFRST) &  
                    MCASP_FGETH(port->hMcasp,GBLCTL, XFRST)) );        
        }  
    }  
 
    /* enable McASP event interrupt if registered */ 
    /* Clear CKFAIL error before enable interrupt */ 
    if (chan->mode == INPUT) {  
        if (port->evtCallback != NULL) { 
            if (port->evtMask & C6X1X_EDMA_MCASP_EVT_RCKFAIL) {  
                MCASP_FSETSH(port->hMcasp, RINTCTL, RCKFAIL, DISABLE); 
                while (MCASP_FGETH(port->hMcasp, RSTAT, RCKFAIL)) { 
                    MCASP_FSETSH(port->hMcasp, RSTAT, RCKFAIL, YES); 
                } 
                MCASP_FSETSH(port->hMcasp, RINTCTL, RCKFAIL, ENABLE); 
            } 
 
            IRQ_enable(MCASP_getRcvEventId(port->hMcasp)); 
        } 
    }  
    else {  
        if (port->evtCallback != NULL) {  
            if (port->evtMask & C6X1X_EDMA_MCASP_EVT_XCKFAIL) {  
                MCASP_FSETSH(port->hMcasp, XINTCTL, XCKFAIL, DISABLE); 
                while (MCASP_FGETH(port->hMcasp, XSTAT, XCKFAIL)) { 
                    MCASP_FSETSH(port->hMcasp, XSTAT, XCKFAIL, YES); 
                } 
                MCASP_FSETSH(port->hMcasp, XINTCTL, XCKFAIL, ENABLE); 
            } 
 
            IRQ_enable(MCASP_getXmtEventId(port->hMcasp)); 
        } 
    } 
     
    *chanp = (Ptr) chan; 
    return (IOM_COMPLETED); 
} 
 
/* 
 * ======== mdDeleteChan ======== 
 * This function frees a channel and all it's associated resources. 
 */ 
static Int mdDeleteChan(Ptr chanp) 
{ 
    ChanHandle chan = (ChanHandle) chanp; 
    PortHandle port = chan->port; 
 
    /* Clean up the channel resources */ 
    chanCleanUp(chan, DELCHAN); 
 
    /* 
     * Reset the McASP transmitter or receiver. If the channel is 
     * recreated, mdCreateChan() will reenable the transmitter/receiver 
     * and by pulling it out of reset it will also restart the EDMA channel. 
     */ 
    if (chan->mode == INPUT) { 
        MCASP_resetRcv(port->hMcasp); 
    } 
    else { 
        MCASP_resetXmt(port->hMcasp); 
    } 
 
    return (IOM_COMPLETED); 
} 
 
/* 
 * ======== mdSubmitChan ======== 
 * This function transmits a buffer to or from the McASP using the EDMA. 
 */ 
static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet) 
{ 
    ChanHandle chan = (ChanHandle) chanp; 
    PortHandle port = chan->port; 
    Uns imask; 
 
    /* No packets can be submitted while abort or flush is active */ 
    if (chan->flushPacket || chan->abortPacket) { 
        return (IOM_EBADIO); 
    } 
 
    /* 
     * Check to see if an abort command has been issued. Note that 
     * flushing the input channel is handled the same as abort. 
     */ 
    if ((packet->cmd == IOM_FLUSH && chan->mode == INPUT) || 
        packet->cmd == IOM_ABORT) { 
 
        /* Disable interrupts to protect submitCount */ 
        imask = HWI_disable(); 
 
        /* Store the abort packet for the ISR to check */ 
        if (chan->submitCount > 0) { 
            chan->abortPacket = packet; 
 
            /* 
             * Disable the EDMA channel while linking the currently 
             * executing job while linking it with the Loop job to make 
             * sure the currently executing job doesn't complete before 
             * the link is complete. 
             */ 
            //EDMA_disableChannel(chan->xferPram); 
            EDMA_link(chan->xferPram, chan->loophEdma); 
            //EDMA_enableChannel(chan->xferPram); 
        } 
 
        /* Reenable interrupts */ 
        HWI_restore(imask); 
 
        if (chan->abortPacket) { 
            return(IOM_PENDING); 
        } 
 
        /* If there were no buffers in the channel, return synchronously */ 
        packet->status = IOM_COMPLETED; 
 
        return (IOM_COMPLETED); 
    } 
 
    /* Check to see if the submitted packet is an output flush packet */ 
    if (packet->cmd == IOM_FLUSH && chan->mode == OUTPUT) { 
        /* Disable interrupts to protect submitCount */ 
        imask = HWI_disable(); 
 
        /* Store the flush packet for the ISR to check */ 
        if (chan->submitCount > 0) { 
            chan->flushPacket = packet; 
        } 
 
        /* Reenable interrupts */ 
        HWI_restore(imask); 
 
        if (chan->flushPacket) { 
            return(IOM_PENDING); 
        } 
 
        /* If there were no buffers in the channel, return synchronously */ 
        packet->status = IOM_COMPLETED; 
 
        return (IOM_COMPLETED); 
    } 
 
    if (packet->cmd != IOM_READ && packet->cmd != IOM_WRITE) { 
        /* Unsupported command passed */ 
        return (IOM_ENOTIMPL); 
    } 
 
    /* maintain cache coherency */ 
    if (chan->mode == INPUT) { 
        /* CACHE uses words, and packet->size is in nmaus (bytes on c6x) */ 
        if (port->cacheCalls) { 
            CACHE_clean(CACHE_L2, packet->addr, packet->size >> 2); 
        } 
    } 
    else { 
        /* CACHE uses words, and packet->size is in nmaus (bytes on c6x) */ 
        if (port->cacheCalls) { 
            CACHE_flush(CACHE_L2, packet->addr, packet->size >> 2); 
        } 
    } 
 
    /* Disable interrupts to protect submitCount */ 
    imask = HWI_disable(); 
    /* 
     * If there is no space available for the new packet, put it on a 
     * queue to be linked in when space is available. Otherwise link it in. 
     */ 
    if (chan->submitCount >= MAXLINKCNT) { 
        QUE_enqueue(&chan->packetQueue, packet); 
    } 
    else { 
        linkPacket(chan, packet); 
    } 
 
    chan->submitCount++; 
 
    HWI_restore(imask); 
 
    return (IOM_PENDING); 
} 
 
/* 
 * ======== mdUnBindDev ======== 
 * This function frees a port and all it's associated resources. 
 */ 
static Int mdUnBindDev(Ptr devp) 
{ 
    PortHandle port = (PortHandle) devp; 
 
    port->inUse = FALSE; 
 
    /* Close the McASP */ 
    MCASP_close(port->hMcasp); 
 
    return (IOM_COMPLETED); 
} 
 
/* 
 * ======== C6X1X_EDMA_MCASP_init ======== 
 * This function initializes the driver's data structures. 
 */ 
#pragma CODE_SECTION(C6X1X_EDMA_MCASP_init, ".text:init"); 
Void C6X1X_EDMA_MCASP_init() 
{ 
    PortHandle port; 
    ChanHandle chan; 
    Int i, j; 
    /* Make sure the initialization only happens once for thid driver */ 
    static Bool curInit = FALSE; 
 
    if (curInit) { 
        return; 
    } 
    curInit = TRUE; 
 
    for (i=0; iinUse = FALSE; 
 
        for (j=0; jchans[j]; 
 
            chan->inUse = FALSE; 
        } 
    } 
}