📄 c6x1x_edma_mcbsp.c
字号:
port->enableMask |= MCBSP_SRGR_FRAMESYNC;
}
/* True if buffers are in external memory */
port->cacheCalls = params->cacheCalls;
/* Store the devid */
port->devid = devid;
/* Open and reset the McBSP */
port->hMcbsp = MCBSP_open(devid, MCBSP_OPEN_RESET);
if (port->hMcbsp == INV) {
return (IOM_EALLOC);
}
/* Configure the McBSP with the supplied configuration */
MCBSP_config(port->hMcbsp, params->mcbspCfgPtr);
/* Return the device handle and a status code for success */
*devp = port;
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_MCBSP_ChanParams *params =
(C6X1X_EDMA_MCBSP_ChanParams *) chanParams;
ChanHandle chan;
Uns mcbspAddr;
Uns old;
Uns esize;
Int i;
/*
* Configuration structure for the loop EDMA job. If the McBSP
* is left to free running, this loop job is running when there is
* no data to transfer. This is useful if the McBSP 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, DEFAULT) |
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, NULL) |
EDMA_FMK (CNT, ELECNT, NULL),
EDMA_FMKS(IDX, FRMIDX, DEFAULT) |
EDMA_FMKS(IDX, ELEIDX, DEFAULT),
EDMA_FMK (DST, DST, NULL),
EDMA_FMK (RLD, ELERLD, NULL) |
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;
/* Initialize the packet queue */
QUE_new(&chan->packetQueue);
/*
* Set the number of elements (corresponding to number of McBSP TDM
* channels) used in the Loop job to preserve the frame sync.
*/
loopEdmaCfg.cnt = EDMA_FMK(CNT, ELECNT, params->tdmChans);
/* Use the same sample size in the Loop job as in normal jobs */
esize = EDMA_FGETA(params->edmaCfgPtr, OPT, ESIZE);
EDMA_FSETA(&loopEdmaCfg, OPT, ESIZE, esize);
/* Allocate a TCC for the EDMA */
chan->tcc = EDMA_intAlloc(-1);
/* If tcc > 15 (possible on c64x) we abort. */
if (chan->tcc == -1 || chan->tcc > 15) {
chanCleanUp(chan, SETFALSE);
return (IOM_EALLOC);
}
/* 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);
}
/*
* Process the big endian problem for McBSP
* See EDMA App Note (spra636) for details setting in big endian
*/
if (chan->mode == INPUT) {
mcbspAddr = MCBSP_getRcvAddr(port->hMcbsp);
}
else {
mcbspAddr = MCBSP_getXmtAddr(port->hMcbsp);
}
#ifdef _BIG_ENDIAN
if (esize == EDMA_OPT_ESIZE_8BIT) {
mcbspAddr += 3;
}
else if (esize == EDMA_OPT_ESIZE_16BIT) {
mcbspAddr += 2;
}
#endif
if (chan->mode == INPUT) {
/* Put input specific parameters in the Loop EDMA config */
loopEdmaCfg.src = mcbspAddr;
loopEdmaCfg.dst = (Uint32) &loopDstBuf;
params->edmaCfgPtr->src = mcbspAddr;
/* Register our isrInput with the EDMA dispatcher */
EDMA_intHook(chan->tcc, &isrInput);
}
else {
/* Put output specific parameters in the Loop EDMA config */
loopEdmaCfg.src = (Uint32) &loopSrcBuf;
loopEdmaCfg.dst = mcbspAddr;
params->edmaCfgPtr->dst = mcbspAddr;
/* Register our isrOutput with the EDMA dispatcher */
EDMA_intHook(chan->tcc, &isrOutput);
}
/* Open the EDMA channel */
chan->xferPram =
EDMA_open(eventIds[port->devid][chan->mode], EDMA_OPEN_RESET);
if (chan->xferPram == EDMA_HINV) {
chanCleanUp(chan, FREETABLEEX);
return (IOM_EALLOC);
}
/* Program the data EDMA job with the TCC */
EDMA_FSETA(params->edmaCfgPtr, OPT, TCC, chan->tcc);
/* 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);
/* Transfer complete is edge triggered, so clear before enabling */
EDMA_intClear(chan->tcc);
EDMA_intEnable(chan->tcc);
/* Enable the EDMA interrupt */
IRQ_enable(IRQ_EVT_EDMAINT);
/* Start the McBSP */
if (chan->mode == INPUT) {
MCBSP_start(port->hMcbsp, port->enableMask | MCBSP_RCV_START,
MCBSP_SRGR_DEFAULT_DELAY);
}
else {
MCBSP_start(port->hMcbsp, port->enableMask | MCBSP_XMIT_START,
MCBSP_SRGR_DEFAULT_DELAY);
}
*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 McBSP 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) {
MCBSP_FSETSH(port->hMcbsp, SPCR, RRST, YES);
}
else {
MCBSP_FSETSH(port->hMcbsp, SPCR, XRST, YES);
}
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 McBSP */
MCBSP_close(port->hMcbsp);
return (IOM_COMPLETED);
}
/*
* ======== C6X1X_EDMA_MCBSP_init ========
* This function initializes the driver's data structures.
*/
#pragma CODE_SECTION(C6X1X_EDMA_MCBSP_init, ".text:init");
Void C6X1X_EDMA_MCBSP_init()
{
PortHandle port;
ChanHandle chan;
Int i, j;
/* Make sure initialization only happens once for this driver */
static Bool curInit = FALSE;
if (curInit) {
return;
}
curInit = TRUE;
for (i=0; i<NUMPORTS; i++) {
port = &ports[i];
port->inUse = FALSE;
for (j=0; j<NUMCHANS; j++) {
chan = &port->chans[j];
chan->inUse = FALSE;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -