Paced mode

Paced mode allows data to be transferred from a peripheral to memory or vice versa, under the control of the peripheral, without intervention by the CPU. In this mode the DMA hardware reads or writes data from the peripheral's memory mapped FIFO registers, as the CPU would, however instead of using interrupts and/or the peripheral's status registers to determine when there is data available to be read or written, it uses additional hardware connections between the peripheral device and the DMA hardware. These signals are used to request a data transfer (and so are frequently referred to as request lines). The signals are sampled by the FDMA, a packet of data is transferred and the signal lowered. This mechanism ensures that the data is passed to the peripheral at the optimum rate with no FIFO overrun, underrun or loss of data.

A range of different on-chip peripherals are supported. The FDMA section of the device datasheet provides a mapping of the supported peripherals.

A paced transfer descriptor is initialised by calling dma_params_init with a mode of MODE_PACED. This then needs to be followed by a number of other calls to initialise the parameters.

As with freerunning transfers the dimensions of the transfer need to be specified, by calling one of the dma_params_DIM_a_x_b() functions. Similarly the addresses need to be specified by calling dma_params_addrs(). In the case of a paced transfer one of the addresses will be the memory address and the other the address of the peripherial's FIFO register.

The additional parameters which control the pacing behaviour are specified in a struct stm_dma_req_config. This is defined as:

struct stm_dma_req_config
{
        unsigned char req_line;         /* Request line (set by dma_req_config) */
        unsigned char rw;               /* Access type: Read or Write */
        unsigned char opcode;           /* Size of word access */
        unsigned char count;            /* Number of transfers per request */
        unsigned char increment;        /* Whether to increment */
        unsigned char hold_off;         /* Holdoff value between req signal samples (in clock cycles)*/
        unsigned char initiator;        /* Which STBus initiatator to use */
};

These are mostly low level parameters which describe the exact behaviour of the DMA transfer. For more details of what values to assign to these parameters please see the FDMA and chip specific documentation.

To associate the request parameters with a particular DMA channel, call dma_req_config():

struct stm_dma_req *dma_req_config(unsigned int chan,
                                   unsigned int req_line,
                                   struct stm_dma_req_config* req_config);

where:

Parameters Description
chan This is the DMA channel which must previously have been allocated.
req_line The hardware request line with which these pacing parameters will be used.
req_config A pointer to an initialized struct stm_dma_req which describes the additional pacing parameters.

This returns a struct stm_dma_req* which must be associated with the transfer descriptor, by calling:

void dma_params_req(struct stm_dma_params *p,
                    struct stm_dma_req *req);

where

Parameters Description
p This is a pointer to the stm_dma_params structure for the transfer.
req A pointer to the struct stm_dma_req returned by a previous call to dma_req_config().

When shutting down a channel which has previously been used with a pacing signal, and additional call must be made to free the struct stm_dma_req* which is returned by calling dma_req_config:

void dma_req_free(unsigned int chan, struct stm_dma_req *req);

The API function for initiating a paced mode transfer is dma_params_paced():

static inline void dma_parms_paced(
				struct stm_dma_params *p,
				unsigned long xfer_size,
				int req_line)

This function accepts the following parameters

Parameters Description
p This is a pointer to the stm_dma_params structure for the transfer.
xfer_size The total number of bytes of data to be transferred. For pacing transfers, this is assumed to be a 1 x 0 transfer if writing from memory to the peripheral, or 0 x 1 if reading from the peripheral to memory; and not a one- or two-dimensional array transfer.
req_line A Paced DMA transfer is mapped to a specific device. The mapping is specified using the integer parameter req_line. The permitted values for this parameter are specific to the SoC in use and are defined in the appropriate SoC-specfic header file linux/stm/<SoC_specific_header>.h.

To perform a transfer that is not 0 x 1 or 1 x 0, set up all node params manually with the manual_dim_params() function. Use of this function requires knowledge of how the transfer increments through memory, and how the peripheral operates in terms of FIFO size, trigger limit and so forth. Depending on the transfer dimensions, some of the parameters may not be required; if unsure please refer to the (FDMA) driver implementation.

For each peripheral, the API supplies a default pacing configuration, which specifies the data format required by the peripheral. This includes parameters such as the data width and depth of the FIFO, size of data packets issued by the FDMA, and so forth. The pacing for a peripheral can be defined manually using the function dma_manual_stbus_pacing().

static inline int dma_manual_stbus_pacing(
						struct stm_dma_params *params,
						struct fmdareq_RequestConfig_s * rq)

Note: The function dma_manual_stbus_pacing() is specific to the 710x series of SoCs only.

The parameter rq is a pointer to a structure that specifies the new pacing configuration. The structure fmdareq_RequestConfig_s is defined as follows:

typedef struct fmdareq_RequestConfig_s {
	char index;		/* See CPU specific header */
	char access; 	/* READ or WRITE */
	char opcode; 	/* STBUS LoadStore word size
			 OPCODE_1, OPCODE_2, OPCODE_4, OPCODE_8, OPCODE_16 or OPCODE_32*/
	char count; 	/* Iterations of opcode */
	char increment; /* Increment peripheral side address by bytes between STBUS packets
			 ENABLE_FLG or DISABLE_FLG */
	char holdoff;	/* The minimum time between req signal resample in microseconds
			HOLDOFF_0US, HOLDOFF_1US or HOLDOFF_2US */
	char initiator;	/* STBUS target for transactions
			  STBUS_INT0 or STBUS_INT1 */
}

An example of a paced, circular, singleton transfer that feeds the PCM players:

	const char * dmac_id =STM_DMAC_ID;
	const char * lb_cap_channel = STM_DMA_CAP_LOW_BW;
	const char * hb_cap_channel = STM_DMA_CAP_HIGH_BW;
	struct stm_dma_params dmap;
 
	if(chip->fdma_channel <0){
		if((err=request_dma_bycap(&dmac_id,&hb_cap_channel,"STB7100_PCM_DMA"))<0){
			if((err=request_dma_bycap(&dmac_id,&lb_cap_channel,"STB7100_PCM_DMA"))<0){
				return -ENODEV;
			}
		}
		chip->fdma_channel= err;
	}
 
	declare_dma_parms(	&dmap,
				MODE_PACED,
				STM_DMA_LIST_CIRC,
				STM_DMA_SETUP_CONTEXT_TASK,
				STM_DMA_NOBLOCK_MODE,
			     	(char*)STM_DMAC_ID);
 
	chip->buffer_start_addr = (unsigned long)runtime->dma_addr;
 
	dma_parms_paced(&dmap,
			snd_pcm_lib_buffer_bytes(substream),
			chip->fdma_req);
 
	dma_parms_addrs(&dmap,runtime->dma_addr,
			virt_to_phys(chip->pcm_player+STM_PCMP_DATA_FIFO),
			snd_pcm_lib_buffer_bytes(substream));
 
	dma_compile_list(&dmap);
	chip->dmap = dmap;
	/*play the audio...*/
	int res = dma_xfer_list(chip->fdma_channel,&chip->dmap);
 
	/*now stop*/
	dma_stop_channel(chip->fdma_channel);
	dma_free_descriptor(&chip->dmap);
	dma_free((chip->fdma_channel);
 
	return err;