EdmaMgr : Added support for transfers with large pitch.
[mfp/fcdev.git] / packages / ti / sdo / fc / edmamgr / edmamgr.c
1 /*
2  * Copyright (c) 2013-2014, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 #include <xdc/std.h>
33 #include <ti/xdais/ires.h>
35 #include <ti/sdo/fc/rman/rman.h>
37 #include "edmamgr.h"
38 #include "edmamgr_heap.h"
40 #ifndef ECPY_INLINE_ALL
41 #include <xdc/runtime/Diags.h>
42 #include <xdc/runtime/Log.h>
43 #include <ti/sdo/fc/ecpy/ecpy_util.h>
44 #endif
46 static IRES_ProtocolRevision _iresEDMA3ChanRevision =
47         IRES_EDMA3CHAN_PROTOCOLREVISION_2_0_0;
49 #define EDMA_MGR_MAX_NUM_CHANNELS     32
51 EdmaMgr_Channel EdmaMgr_channels[EDMA_MGR_MAX_NUM_CHANNELS];
53 extern __FAR__ int32_t *ti_sdo_fc_edmamgr_region2Instance;
54 extern __FAR__ EDMA3_GblConfigParams *ti_sdo_fc_edmamgr_edma3GblConfigParams;
55 extern __FAR__ EDMA3_InstanceInitConfig *ti_sdo_fc_edmamgr_edma3RegionConfig;
57 /*********************************************************************************
58  * FUNCTION PURPOSE: Init EdmaMgr resources
59  *********************************************************************************
60   DESCRIPTION:      This function initializes RMAN resouces
62   Parameters :      Inputs: proc_id          : core ID
63                             edma_config      : pointer to EDMA3 instance init
64                                                configuration
65                     Output: EdmaMgr_SUCCESS if initialization successful;
66                             Error code otherwise
67  *********************************************************************************/
68 int32_t EdmaMgr_init(int32_t proc_id, void *edma_config)
69 {
70     IRES_Status ires_status;
71     uint32_t edmaInstanceId;
72     int32_t i;
74     if (proc_id < 0 || proc_id >= EDMA3_MAX_REGIONS) {
75         return EdmaMgr_ERROR_INVARG;
76     }
78     if (ti_sdo_fc_edmamgr_region2Instance != NULL) {
79         edmaInstanceId = ti_sdo_fc_edmamgr_region2Instance[proc_id];
81         if (ti_sdo_fc_edmamgr_edma3GblConfigParams != NULL) {
82             EDMA3_PARAMS.globalConfig =
83                     (EDMA3_GblConfigParams *) &
84                     ti_sdo_fc_edmamgr_edma3GblConfigParams[edmaInstanceId];
85         }
86         else if (EDMA3_PARAMS.globalConfig == NULL) {
87             return EdmaMgr_ERROR_INVCFG;
88         }
90         if (edma_config != NULL) {
91             EDMA3_InstanceInitConfig *custom_config =
92                     (EDMA3_InstanceInitConfig *) edma_config;
94             EDMA3_PARAMS.regionConfig =
95                     (EDMA3_InstanceInitConfig *) &
96                     custom_config[EDMA3_MAX_REGIONS * edmaInstanceId + proc_id];
97         }
98         else if (ti_sdo_fc_edmamgr_edma3RegionConfig != NULL) {
99             EDMA3_PARAMS.regionConfig =
100                     (EDMA3_InstanceInitConfig *) &
101                     ti_sdo_fc_edmamgr_edma3RegionConfig[EDMA3_MAX_REGIONS *
102                     edmaInstanceId + proc_id];
103         }
104         else if (EDMA3_PARAMS.regionConfig == NULL) {
105             return EdmaMgr_ERROR_INVCFG;
106         }
107     }
108     else if (EDMA3_PARAMS.globalConfig || EDMA3_PARAMS.regionConfig == NULL) {
109         return EdmaMgr_ERROR_INVCFG;
110     }
112     EdmaMgr_heap_create();
114     RMAN_PARAMS.allocFxn = &EdmaMgr_heap_alloc;
115     RMAN_PARAMS.freeFxn = &EdmaMgr_heap_free;
116     EDMA3_PARAMS.allocFxn = &EdmaMgr_heap_alloc;
117     EDMA3_PARAMS.freeFxn = &EdmaMgr_heap_free;
118     ECPY_CFG_PARAMS.allocFxn = &EdmaMgr_heap_alloc;
119     ECPY_CFG_PARAMS.freeFxn = &EdmaMgr_heap_free;
121     /* specify EDMA instance ID */
122     ti_sdo_fc_edma3_EDMA3_physicalId = edmaInstanceId;
124     /* RMAN init */
125     ires_status = RMAN_init();
127     if (IRES_OK != ires_status) {
128         return EdmaMgr_ERROR_RMANINIT;
129     }
131     memset(EdmaMgr_channels, 0,
132             EDMA_MGR_MAX_NUM_CHANNELS * sizeof(EdmaMgr_Channel));
133     for (i = 0; i < EDMA_MGR_MAX_NUM_CHANNELS; i++) {
134         EdmaMgr_channels[i].edmaArgs.size = sizeof(IRES_EDMA3CHAN_ProtocolArgs);
135         EdmaMgr_channels[i].edmaArgs.mode = IRES_PERSISTENT;
136         EdmaMgr_channels[i].edmaArgs.numTccs = 1;
137         EdmaMgr_channels[i].edmaArgs.paRamIndex = IRES_EDMA3CHAN_PARAM_ANY;
138         EdmaMgr_channels[i].edmaArgs.tccIndex = IRES_EDMA3CHAN_TCC_ANY;
139         EdmaMgr_channels[i].edmaArgs.qdmaChan = IRES_EDMA3CHAN_CHAN_NONE;
140         EdmaMgr_channels[i].edmaArgs.edmaChan = IRES_EDMA3CHAN_EDMACHAN_ANY;
141         EdmaMgr_channels[i].edmaArgs.contiguousAllocation = TRUE;
142         EdmaMgr_channels[i].edmaArgs.shadowPaRamsAllocation = FALSE;
143         EdmaMgr_channels[i].edmaArgs.numPaRams = 0;
144     }
145     return EdmaMgr_SUCCESS;
148 /*********************************************************************************
149  * FUNCTION PURPOSE: Allocate EdmaMgr handle
150  *********************************************************************************
151   DESCRIPTION:      This function allocates an EDMA channel
153   Parameters :      Inputs: max_linked_transfer  : maximum number of linked
154                                                    transfers required for the
155                                                    channel.
157                     Output: Valid EdmaMgr handle on success;
158                             NULL handle if there is an error.
159  *********************************************************************************/
160 EdmaMgr_Handle EdmaMgr_alloc(int32_t max_linked_transfers)
162     IRES_ResourceDescriptor resDesc;
163     IRES_Status ires_status;
164     int32_t scratchId = -1;
165     int32_t i;
167     /* Find free channel */
168     for (i = 0; i < EDMA_MGR_MAX_NUM_CHANNELS; i++)
169         if (EdmaMgr_channels[i].edmaArgs.numPaRams == 0)
170             break;
172     if (i >= EDMA_MGR_MAX_NUM_CHANNELS)
173         return ((EdmaMgr_Handle)NULL);
175     EdmaMgr_channels[i].edmaArgs.numPaRams = max_linked_transfers;
177     /* Prepare IRES resource descriptor */
178     resDesc.resourceName = IRES_EDMA3CHAN_PROTOCOLNAME;
179     resDesc.revision = &_iresEDMA3ChanRevision;
180     resDesc.protocolArgs = (IRES_ProtocolArgs *)&(EdmaMgr_channels[i].edmaArgs);
181     resDesc.handle = (IRES_Handle)EdmaMgr_channels[i].edmaHandle;
183     /* Allocate EDMA Resources */
184     ires_status =
185             RMAN_allocateResources((Int)EdmaMgr_channels, &resDesc, 1,
186             scratchId);
187     if (ires_status != IRES_OK) {
188         EdmaMgr_channels[i].edmaArgs.numPaRams = 0;
189         return ((EdmaMgr_Handle)NULL);
190     }
191     EdmaMgr_channels[i].edmaHandle = (IRES_EDMA3CHAN_Handle) resDesc.handle;
193     /* Create ECPY handle */
194     EdmaMgr_channels[i].ecpyHandle =
195             ECPY_createHandle((IRES_EDMA3CHAN2_Handle)resDesc.handle,
196             (IALG_Handle)&EdmaMgr_channels[i]);
197     if (EdmaMgr_channels[i].ecpyHandle == NULL) {
198         RMAN_freeAllocatedResources((Int)EdmaMgr_channels, &resDesc, 1,
199                 scratchId);
200         EdmaMgr_channels[i].edmaHandle = NULL;
201         EdmaMgr_channels[i].edmaArgs.numPaRams = 0;
202         return ((EdmaMgr_Handle)NULL);
203     }
204     EdmaMgr_channels[i].xferPending = FALSE;
206     /* HW assign */
207     ECPY_activate(EdmaMgr_channels[i].ecpyHandle);
208     ECPY_setEarlyCompletionMode(EdmaMgr_channels[i].ecpyHandle, FALSE);
210     return ((EdmaMgr_Handle)&EdmaMgr_channels[i]);
213 /*********************************************************************************
214  * FUNCTION PURPOSE: Allocate EdmaMgr handle for Large strides
215  *********************************************************************************
216   DESCRIPTION:      This function allocates an EDMA channel for large transfers
217                     where the pitch is larger than the HW limit of 16 bits.
219                     Since these transfers excede the HW limits of a single PaRAM,
220                     two channels must be chained, and "src" or "dst" addresses
221                     will be stored in a look-up table.
223                     The first EdmaMgr channel will read these addresses from the
224                     table, and transfer them to the PaRAM of the second channel.
225                     Once this transfer is complete, chaining will trigger the
226                     transfer of the actual data in the second channel. Once the
227                     second channel completes the transfer, chaining will trigger
228                     the first channel to read the next address in the table.
229                     This will continue until the entire data is transfered.
231   Parameters :      Inputs: max_num_lines  : maximum number of lines in large
232                                              1D2D or 2D1D transfers required for
233                                              the channel.
235                     Output: Valid EdmaMgr handle on success;
236                             NULL handle if there is an error.
237  *********************************************************************************/
238 EdmaMgr_Handle EdmaMgr_allocLarge(int32_t max_num_lines)
240     EdmaMgr_Handle   h1, h2;
241     EdmaMgr_Channel  *chan1, *chan2;
242     uint32_t         tcc1, tcc2;
244     /*
245      *  Allocate a basic EdmaMgr channel.
246      *
247      *  This channel will be responsible for transferring addresses from the
248      *  lookup table to the PaRAM of the second channel.
249      */
250     if ( (h1 = EdmaMgr_alloc(1)) == NULL )
251         return ((EdmaMgr_Handle)NULL);
253     /*
254      *  Allocate a basic EdmaMgr channel.
255      *
256      *  This channel will be responsible for transferring the data of interest,
257      *  line by line.
258      */
259     if ( (h2 = EdmaMgr_alloc(1)) == NULL ) {
260         EdmaMgr_free(h1);
261         return ((EdmaMgr_Handle)NULL);
262     }
264     chan1 = (EdmaMgr_Channel *)h1;
265     chan2 = (EdmaMgr_Channel *)h2;
267     /* Allocate memory for the address lookup table. */
268     chan1->addrTable.size      = max_num_lines*sizeof(uint32_t);
269     chan1->addrTable.alignment = sizeof(uint32_t);
271     if( !EdmaMgr_heap_alloc( &chan1->addrTable, 1) ) {
272         EdmaMgr_free(chan1);
273         EdmaMgr_free(chan2);
274         return ((EdmaMgr_Handle)NULL);
275     }
277     /* Obtain each channel's TCC so that chaining can be configured, */
278     tcc1 = chan1->edmaHandle->assignedTccIndices[0];
279     tcc2 = chan2->edmaHandle->assignedTccIndices[0];
281     /* PaRAM OPT settings for first in chain. */
282     /* This TCC will load the second in the chain with the SRC address from a table in memory */
283     chan1->optChained = ECPY_PARAMOPTS_TCCBITS(tcc2) |   /* Chain to the second channel's TCC */
284                         ECPY_PARAMOPTS_ITCCHEN       |   /* Chain on intermediate transfers (after each address is loaded to the second channel). */
285                         ECPY_PARAMOPTS_TCCHEN        |   /* Chain on final transfer (after last address is loaded). */
286                         ECPY_PARAMOPTS_TCC_INTEN     |   /* Enable interrupt on final transfer (Sets IPR bit after final transfer). */
287                         ECPY_PARAMOPTS_ABSYNC;           /* Sync transfer chaining on A and B CNTs. */
289     /* PaRAM OPT settings for second in chain. */
290     /* This TCC will transfer the actual data of interest. */
291     chan2->optChained = ECPY_PARAMOPTS_TCCBITS(tcc1) |   /* Chain to the first channel. */
292                         ECPY_PARAMOPTS_ITCCHEN       |   /* Chain in intermediate transfers (to obtain next address from table). */
293                         ECPY_PARAMOPTS_TCC_INTEN     |   /* Enable interrupt on final transfer (Sets IPR bit after final transfer). */
294                         ECPY_PARAMOPTS_ABSYNC;           /* Sync transfer chaining on A and B CNTs. */
296     chan1->chainedChannel = chan2;
298     return ((EdmaMgr_Handle)chan1);
301 /*********************************************************************************
302  * FUNCTION PURPOSE: Free EdmaMgr handle
303  *********************************************************************************
304   DESCRIPTION:      This function frees an EDMA channel
306   Parameters :      Inputs: h   : EdmaMgr handle
308                     Output: EdmaMgr_SUCCESS if free is successful;
309                             Error code otherwise.
310  *********************************************************************************/
311 int32_t EdmaMgr_free(EdmaMgr_Handle h)
313     EdmaMgr_Channel *chan = (EdmaMgr_Channel *)h;
314     IRES_ResourceDescriptor resDesc;
315     IRES_Status ires_status;
316     int32_t scratchId = -1;
317     int32_t i, ret_val = EdmaMgr_SUCCESS;
319     /* Make sure all transfers have completed */
320     EdmaMgr_wait(h);
322     /* While loop for freeing chained channels. */
323     while ( chan != NULL ) {
324       EdmaMgr_Channel *chainedChan = chan->chainedChannel;
326       /* Verify that this is a valid handle? */
327       for (i = 0; i < EDMA_MGR_MAX_NUM_CHANNELS; i++)
328           if (chan == &EdmaMgr_channels[i])
329               break;
331       if (i >= EDMA_MGR_MAX_NUM_CHANNELS)
332           return EdmaMgr_ERROR_INVHANDLE;
334       /* Prepare IRES resource descriptor */
335       resDesc.resourceName = IRES_EDMA3CHAN_PROTOCOLNAME;
336       resDesc.revision = &_iresEDMA3ChanRevision;
337       resDesc.protocolArgs = (IRES_ProtocolArgs *)&(chan->edmaArgs);
338       resDesc.handle = (IRES_Handle)chan->edmaHandle;
340       ECPY_deleteHandle(chan->ecpyHandle);
342       ires_status =
343               RMAN_freeAllocatedResources((Int)EdmaMgr_channels, &resDesc, 1,
344               scratchId);
345       if (ires_status != IRES_OK) {
346           ret_val = EdmaMgr_ERROR_FREE;
347       }
349       chan->ecpyHandle = NULL;
350       chan->edmaHandle = NULL;
351       chan->xferPending = FALSE;
353       chan->edmaArgs.numPaRams = 0;
354       chan->chainedChannel = NULL;
356       if ( chan->addrTable.base ) {
357           EdmaMgr_heap_free(&chan->addrTable, 1);
358           chan->addrTable.base = NULL;
359           chan->addrTable.size = 0;
360       }
362       chan = chainedChan;
363     }
365     return ret_val;