⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 c6x1x_edma_mcbsp.c

📁 DSP/BIOS Driver Developer Kit 1.11 The DSP/BIOS Driver Developer Kit (DDK) provides a selection of
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 *  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 + -