|
Please note there are wholly different API's available for the LDDE 2.2 and the STLinux2.0 distributions. The information on this page relates solely to API available in LDDE2.2. Try here for the legacy API documentation relating to STLinux2.0. IndexIntroductionThe STMicroelectronics DMA API provides a kernel-space API to access the functionality of the FlexibleDMA engine, and also provides a generic mode capable of standard memory-memory moves that is compatible with the original SHDMA-API. In the STMicroelectronics specific mode, support is given to paced transfers to and from peripherals. Additionally Free-running transfers from mem-mem and scatter-gather to and from memory is possible. No explicit support is provided for pacing a scatter-gather transfer either to of from a peripheral. Additionally no support is provided for PES scanning or SPDIF compressed audio formatting which has native support in the Firmware of the STB710x series CPU's. Though generic SPDIF transfers (PCM) are supported. Channels are still reserved by drivers in a similar method to the legacy system:- a request/free pair will ensure that a given channel is reserved for use by the requestee until a free calls returns it to the available pool. However in addition to reserving channels specifically by number, or by the next available channel, it is now possible to reserve by channel capability. Some channels in the FDMA FW have been optimized for particular application requirements, such as memory bandwidth and buffer space. For those familiar with the previous API, the architecture of configuring and executing a transfer has changed considerably, so here follows a brief explanation: Configuring a transfer is now an independent operation from any particular channel, transfer descriptors can be configured asynchronously from execution of a transfer. So transfers can be configured / reconfigured whilst the dma channel is active. Only when the transfer is initiated is it tied to a particular channel, this association persists until the entire transfer ( either single node or linked list)is completed. This transfer can then be re-associated with the same, or any other channel for the next execution. This allows a device driver to pre-allocate a number of transfers of different configurations and utilize them as required. Memory for the fdma descriptor is allocated as the node is compiled, and is associated with the transfer descriptor at that time, and persists until the user explicitly destroys the descriptor they have allocated. This allows compiled transfers to be re-compiled with new parameters. Within certain limitations - parameters such as size and address can be modified, but not those which change the mode of the transfer. Thus the burden of managing the descriptor memory is shifted into the user domain to provide greater flexibility. Nodes of heterogenous types can now be linked in an arbitrary way providing a more flexible model. As an example you would be able to configure a transfer that would scatter-gather memory from user-space into a kernel buffer, then link that transfer to a paced one which fed a particular peripheral. Both open ended and circular linked lists are available, although circular lists can only be linked back to the first node in a chain, and only the last node in the chain is capable of linking back. Configuring the DMA system for your driverTo enable the STM_DMA API and driver two options must be selected, both of these are enabled in the default configuration. Bring up the Linux Kernel Configuration (Try Kernel Configuration) and enable:-
The latter two options should reflect the channel mapping you wish to use. The default configuration is to allocate all channels provided by the SoC. Additionally debugging prints can be enabled which print messages to the serial console in case of error. The default state is disabled This functionality is enabled by: -
It is now possible to have several DMAC's installed on top of a single DMA engine, such that they share the SoC's DMA channels between them. As such when the STM_DMA is enabled it is necessary to specify the range of channels which are to be allocated to the native Linux driver. These can be any number of channels >= 0 and =< the total available, which for the 710x is 16, please refer to the datasheets of particular SoC's for the number of channels provided by the on-chip FDMA for that part ( N.B. all 710x series have 16 channels). The default is to allocate all channels to the native DMA driver. Please note however that channels assigned to the STM driver must be in a contiguous range. Allocating 0 channels will cause the controller to be disabled. In order to make the API visible from your driver, three headers must be included
This makes API functions available from in-kernel drivers and kernel modules. As a general note, the explanations of the functions given below lack information about behaviour on error and return values. It can be assumed that all operations will return zero success, and in the case of error a -STDERR value, which are available from asm-generic/errno.h. Descriptions of exact behaviour and return codes for the functions can be found in linux/stm/stm-dma.h and asm/dma.h. Configuring a TransferThe initial operation is to declare a new transfer descriptor, and fill it with the mode parameters for your transfer. This is done via the declare_dma_parms() helper function. At this stage it is necessary to associate this transfer to a particular DMA controller, as there may be more than one available in the system. Note that attempting to recompile a transfer with different values than those given in declare_dma_params() without a call to dma_free_descriptor() is an undefined operation. The declare function takes 6 parameters as below.
Next we must specify our transfer specific params and configuration. This is done with a number of inline functions.
Is essential to all transfers, and the parameters represent source(src) and destination(dst) address', which must be bus address' pointing to physically contiguous, uncached regions. The bytes parameter is the total number of bytes to transfer. The transfer will be more efficient if these buffers are aligned on a 32 byte boundary - but this is not mandatory. The other functions required are specific to the transfer mode, for example if configuring a freerunning 0x1 move you would use dma_parms_DIM_0_x_1() and dma_parms_paced() for a paced transfer etc. And a complete list pulled from stm-dma.h is below.
There are also a number of optional parameters which enable additional functionality such as callback processing, interrupt configuration and node linking.
Provides the method for users to compile arbitrary linked lists of transfers. This function sets the 'child' to be the subsequent transfer from the node 'parent'. The node linkage must be set up before any transfers are compiled with dma_compile_list(). If the transfer ordering is modified after a call to dma_compile_list(), all node descriptors must be freed with dma_free_descriptor FDMA Interrupt Generation
This will enable FDMA interrupts, and based on the isrflag param, which is a bitmask, two behaviours are possible. Specifying STM_DMA_INTER_NODE_PAUSE will cause the FDMA to interrupt and pause the transfer after each node. The user is then for responsible for restarting paused transfers, this is described in the Control and Status functions section. for scatter-gather transfers this will illicit an interrupt for each element of the scatterlist. For circular transfers, an interrupt is generated each time a node is processed. Specifying the STM_DMA_LIST_COMP_INT mask will precipitate the generation of an interrupt to the FDMA after the last byte of a single node or last byte of the last node of a linked list. If both are required the two values should be 'or'ed into the isrflag parameter. CallbacksTwo functions enable the callback mechanism of the FDMA driver. These can be executed in either interrupt or BH-tasklet context, after each node, or at the end of a list / singleton node or in case of an error.
If the fn parameters are not specified it will be taken that callbacks are disabled. Likewise if the 'param' parameter is NULL then the callback function will be executed without parameters. It may be necessary for your transfer to specify whether the callback be executed in task let or interrupt context. Specifying :-
to the isr_context param achieves this. Specifying nothing assumes the default behaviour of tasklet context processing. If tasklet mode is chosen the callback will be scheduled at the time of the FDMA interrupt and executed as soon as Linux has no further pending interrupts. In isr context the callback is executed from within the FDMA interrupt handler. So be careful not to include any blocking operations in your callback function in this mode ! Callbacks will happen in two scenario's, when the transfer( end of single node / end of list) has completed(or node if STM_DMA_INTER_NODE_PAUSE is specified) or if the FDMA driver detects an error in the transfer ( note this does not include data / formatting errors, merely errors flagged by the FDMA engine). In this latter case, the current transfer will be aborted before the callback happens, and no further nodes in a linked list will be processed. Compiling a transfer or transfer listOnce the required parameters have been selected, the transfer must be compiled from the meta-descriptor in the stm_dma_params * struct into a linked list of nodes executable by the fdma. This is done with the dma_compile_list() function. Failure to specify valid or a complete set of parameters will cause this function to fail,and perhaps logging a msg to the serial console if the error is a fatal one.
Compiling a transfer causes the fdma driver to allocate memory for a node, or nodelist and populate that list in accordance with the parameters in the stm_dma_params structure. This operation may fail if sufficient memory is not available in the system or if the parameters are incorrect. In the case of a linked list of nodes, once linking has been established, dma_compile_list() should only be called for the first transfer descriptor in the list. The dma driver will then walk through the list of transfer descriptors and compile a self consistent nodelist. Executing a transferIn order to execute a compiled transfer on a particular channel, it is necessary first to request that channel for use by your driver. This can be done in a number of ways. Once a channel has been successfully requested, it is ensured that the channel is reserved for exclusive use by the requestee, until a matching call to free is executed. Channels can be claimed using three methods, by capability, by channel number, or by next available. for particular channels, and these can be claimed by specifying the capability string to the request_dma_bycap() function. Available capabilities are exported from linux/stm/stm-dma.h. Specifying a NULL string to this function searches and returns the lowest channel available.
It is important to release the channel back to the pool when it is no longer needed so it can be used by other devices. A typical usage scenario for the compile - transfer model may be as follows:
Although there is no ordering requirement between channel request and node compile - Node compilation and linking is independent of any channel. ordering constraints do exist between the following: where -> - happens before
A correctly compiled descriptor can be executed on a requested channel by the dma_xfer_list() function. The behaviour will be to start execution on the first node of the transfer, or block or return error in the case of a busy channel depending on the status input params (blocking), or an error value in the case of bad input parameters.
Recompiling and re-executing a transferAny transfer that has been executed can be either re-executed with no change in parameters, on the same, or any other reserved channel, or re-compiled with different parameters, please note changing any of the parameters of the transfer requires it to be re-compiled. This is a lightweight operation where new parameters are inserted directly into the existing nodelist. It should also be noted that scatter-gather transfers should be re-compiled every time the parameters or length of the scatterlist struct are changed, as this will alter the length of the nodelist required to describe the transfer. Changing the ordering of the nodelist, or any of the mode parameters given in the dma_declare_params() function require a dma_free_descriptor() call on each of the descriptors before a further call(s) to dma_compile_list(). Transfer cleanup & de-initOnce the transfer is no longer required, or any of the major parameters have changed, the descriptor should be freed. This is done with a call to
which frees all allocated node memory,and resets structure parameters, ready for the structure to be either re-used in an alternative mode or discarded. Additionally any reserved channels should be freed when they are no longer required, this is done with a call to:
which will release the channel if it is idle, or if it is busy, attempt to stop the channel, and then release. SH DMA API Backwards CompatibilityThe API for STLinux2.2 has been regressed to include a compatibility mode for any devices already using the existing SH-DMA API. Due to the limited applicability of this API to STMicroelectronics DMA controllers, only a small subset of mode and configurations are possible. For example it is not possible to perform paced, scatter-gather, or linked list transfers. Only memory-memory in freerunning mode is possible. The method for describing a transfer in this mode is considerably different to the rest of the API and is explained below. In this use case, configuration cannot be achieved while a transfer is in operation, as the descriptor is associated with the channel at the first instance. This forces a sequential mode of operation for compilation and execution of a dma transfer. It is still necessary to reserve a channel from the dma-controller. The compatible function is request_dma - where the parameter may be any integer in the valid range, or the flag DMA_REQ_ANY_CHANNEL. Next you need to gain the default status of the channel from the controller. This is done with a call to get_dma_channel() specifying the channel number which has previously been reserved Next a call to dma_configure_channel() is required, here we associate the channel number with the mode of operation. The flags parameter must contain a bitmask of three values -
next we call the transfer function(dma_xfer()) with our src and destination address', plus channel number and the mode. The same memory space constraints on src and dst locations apply as with other transfer modes. Function prototypes are given below.
Notification of completion can only be via the dma_wait_for_completion() API A worked example can be found here Freerunning ModeThis modes provides basic memory-memory move functionality. Transfers can be configured for Single location, linear or rectangular arrays. The method of specifying transfer parameters has been described in the Compiling a transfer or transfer list section. Further information about what linear and rectangular arrays are can be found in the STb7100 datasheet in section 47.4.1. It is possible to mix dimensions in source and destination, so we could for example, specify a 2d-0d transfer, or a 1d-1d transfer. A code example can be found here Scatter-Gather ModeScatter Gather transfers are supported in memory to memory mode, allowing buffer collation from non-contiguous memory areas. It is also possible to specify transfer to or from blocks of 0/1/2 Dimensional memory, as in Freerunning Mode. There is currently no explicit support for gathering directly into a paced peripheral or vice versa. The STM DMA API uses the generic Linux struct scatterlist support, and so a working knowledge of scatterlists and how to compile them is required to configure transfers in this mode correctly. Documentation on this system can be found in the Linux Kernel Documentation on DMA. This is available in the kernel sources at /Documentation/DMA-mapping.txt & DMA-API.txt. Scatter Gather is either from a non-contiguous memory area to a contiguous one or vice versa, it is not possible to transfer between two non-contiguous areas in this mode, though a linked list of generic memory-memory transfer could achieve this functionality. Therefore given the two modes of operation, the Mode parameter given to dma_declare_params() should either be
Paced ModePaced mode provides a way to connect a cpu to a peripheral and feed data in the correct format and rate for the device in question without cpu intervention. A number of different on-chip peripherals are supported. Which peripherals are available depends on cpu and revision number, so check your SoC's datasheet FDMA section for a mapping of the devices supported. When operating in paced mode, the dma system will traditionally be transferring to or from a peripheral's FIFO. Signals are generated by the peripheral to flag a data request(either read / write) which are sampled by the FDMA and a chunk of data transferred and the signal lowered. In this way peripheral's can be fed at the correct rate with no FIFO over/underrun or lost update at the peripheral. For each peripheral a default pacing configuration is supplied, which specifies the data format at the peripheral. For example, parameters such as data width and depth of the FIFO, size of data chunks issued by the FDMA etc. This is explained in more detail below. A paced dma transfer must be mapped against a particular device. An integer mapping exists for each peripheral. This is the req_line value. The values are exported from linux/stm/*cpu specific header*.h. The value corresponding to the peripheral is used as the req_line parameter of the function below.
Here we are specifying only the total number of bytes for the transfer, with pacing transfers we are assuming a model of 1x0 if writing from a memory area to a peripheral, or 0x1 if reading from a peripheral into memory. Therefore in this instance the xfer_size parameter is specifying both the line length of the memory side, and the total length of the transfer. Configuration of the peripheral side is selected by associating the transfer with a particular req_line mapping. This gives a default setup for each peripheral, it is however possible to specify these parameters manually if you wish. A code example which gives an example of a paced, circular, singleton transfer that feeds the pcm players can be found here If you wish to perform a transfer that is not 0x1 or 1x0, you must set up all node params manually with the manual_dim_params() function. Usage of this function implies knowledge of how the transfer is to increment through memory, and how the peripheral in question operates(FIFO size / trigger limit etc). Depending on transfer dimensions, some of the parameters may not be required, if unsure please refer to the (FDMA) driver implementation.
Please be aware that this is specific to the 710x series of CPU's. where Table 1 : fmdareq_RequestConfig_s definition
Control and Status functionsIt is possible to manipulate individual channels through the API, as well as gain current state information. It is possible to pause / unpause, stop, and sleep until completion. There are two methods for gaining the state of a particular channel, or ongoing transfer, these provide the activity status (running/paused/idle), and the number of bytes remaining to be transferred on that channel. These are the dma_get_status() and dma_get_residue() functions. A description of each follows.
This functions returns the number of bytes remaining to be processed for the current transfer, or for linked list transfers the number returned will be a total for the current transfer plus the total bytes for all remaining transfers. If the channel is currently inactive or completed zero will be returned.
This functions returns the current activity state of the channel specified in chan. It will be one of three values
This function will pause any active channel, the flags parameter specifies whether to flush the current operation from the DMA-engine's memory. In the case of an unpause after a flush operation, on a linked list with remaining transfers, the next transfer in the list will be loaded and executed automatically. In the case of the last element of a list, or singleton, the current operation is flushed and the FDMA returned to idle. A flush will cause the DMA engine to lapse into idle. If flush is not specified, the fdma will continue with its currently loaded transfer - from the point it was paused. It is possible for this operation to fail, if either the channel is not in a running state, or if the DMA engine fails to respond to the command.
This function will unpause any currently pause channel. If it is not in the paused state, then the operation equates to nop.
This function implements a wait_event call that will put the calling process to sleep while the given channel is in operation. As such it is not safe to call from interrupt context. The wake_up signal is raised after DMA engine completion interrupt generation and handling but before the processing of any callbacks associated with that channel. Caviats
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||