📄 c6x1x_edma_mcbsp.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.11.00.00 11-04-03 (ddk-b13)" */
/*
* ======== c6x1x_edma_mcbsp.c ========
*
* Generic McBSP driver for the TMS320C6x1x series. Uses the EDMA.
*/
#include <std.h>
#include <atm.h>
#include <iom.h>
#include <que.h>
#include <hwi.h>
#include <csl.h>
#include <csl_mcbsp.h>
#include <csl_irq.h>
#include <csl_edma.h>
#include <csl_cache.h>
#include <c6x1x_edma_mcbsp.h>
/* 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 _MCBSP_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 */
} 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 enableMask; /* Holds enable Srgr and Fsgr if applicable */
MCBSP_Handle hMcbsp; /* CSL Device handle */
ChanObj chans[NUMCHANS]; /* The channels associated with the port */
} 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_REVT0, EDMA_CHA_XEVT0 },
{ EDMA_CHA_REVT1, EDMA_CHA_XEVT1 },
#if NUMPORTS == 3
{ EDMA_CHA_REVT2, EDMA_CHA_XEVT2 }
#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 mdDeleteChan(Ptr chanp);
static Int mdSubmitChan(Ptr chanp, IOM_Packet *packet);
static Int mdUnBindDev(Ptr devp);
/* Public IOM interface table */
IOM_Fxns C6X1X_EDMA_MCBSP_FXNS = {
&mdBindDev,
&mdUnBindDev,
IOM_CONTROLCHANNOTIMPL,
&mdCreateChan,
&mdDeleteChan,
&mdSubmitChan
};
/* Local function prototypes */
static Void isrInput(Int tcc);
static Void isrOutput(Int tcc);
static Void chanCleanUp(ChanHandle chan, Uns state);
static Void isrCommon(ChanHandle chan);
static Void linkPacket(ChanHandle chan, IOM_Packet *packet);
/* Local driver variables. */
static Uint32 loopDstBuf = 0;
static Uint32 loopSrcBuf = 0;
/*
* ======== 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);
case FREETABLEEX:
/* Free the EDMA link PaRAM tables */
EDMA_freeTableEx(MAXLINKCNT, chan->pramTbl);
case FREETABLE:
/* Free the loop EDMA PaRAM table */
EDMA_freeTable(chan->loophEdma);
case FREETCC:
/* Free the transfer complete code */
EDMA_intFree(chan->tcc);
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 (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_link(chan->xferPram, chan->pramTbl[chan->readIndex]);
}
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;
}
}
}
}
}
/*
* ======== 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;
/* 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.
*/
EDMA_RSETH(pramPtr, CNT, (Uint32) packet->size >>
(2 - EDMA_FGETH(pramPtr, OPT, ESIZE)));
/*
* 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 McBSP port specified by devid.
*/
static Int mdBindDev(Ptr *devp, Int devid, Ptr devParams)
{
Uns old;
PortHandle port;
HWI_Attrs hwiAttrs;
C6X1X_EDMA_MCBSP_DevParams *params =
(C6X1X_EDMA_MCBSP_DevParams *) devParams;
/* This driver must receive a valid devparams */
if (params == NULL) {
return (IOM_EBADARGS);
}
/* Check if the driver is supporting the version used by
* the application */
if(params->versionId != C6X1X_EDMA_MCBSP_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 */
IRQ_map(IRQ_EVT_EDMAINT, params->irqId);
/* set the interrupt mask */
hwiAttrs.intrMask = params->intrMask;
hwiAttrs.ccMask = IRQ_CCMASK_NONE; /* the default value */
hwiAttrs.arg = NULL;
/* Plug the EDMA dispatcher into the HWI dispatcher */
HWI_dispatchPlug(params->irqId, (Fxn)EDMA_intDispatcher, -1, &hwiAttrs);
/* Initialise the enableMask */
port->enableMask = 0;
/* Set the McBSP sample rate generator to be enabled */
if (params->enableSrgr) {
port->enableMask |= MCBSP_SRGR_START;
}
/* Set the McBSP frame sync generator to be enabled */
if (params->enableFsg) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -