* [RFC] Generic DMA chaining APIs
@ 2007-06-06 5:02 Syed Mohammed, Khasim
2007-08-10 7:33 ` Tony Lindgren
0 siblings, 1 reply; 2+ messages in thread
From: Syed Mohammed, Khasim @ 2007-06-06 5:02 UTC (permalink / raw)
To: Linux OMAP
Hi all:
I would like to port Generic DMA chaining API support from TI's 2430 DMA driver to our OMAP GIT DMA driver.
- Basically these APIs give flexibility for handling DMA chaining for drivers like McBSP (for audio), McSPI (for LCD), UART, etc.
- Chaining is very important for drivers like Audio, LCD etc.
- Our current DMA driver on GIT does support APIs to enable /configure DMA logical channel chaining, but they have to be again used in client drivers to implement the queuing logic.
- Since there are multiple drivers in need of these kind of chaining support, it's preferable to implement them in a generic way so that all other drivers can leverage the code instead of creating a redundant copy in every driver.
A code snippet (from TI's DMA driver) is as shown below. Please do let me know if you find any issues with this kind of implementation/approach before I start (this week) this porting activity.
Thanks in advance.
APIs to be ported are:
=====================
omap_request_dma_chain
omap_free_dma_chain
omap_dma_chain_a_transfer
omap_modify_dma_chain_params
omap_start_dma_chain_transfers
omap_stop_dma_chain_transfers
omap_dma_chain_status
omap_get_dma_chain_index
omap_get_dma_chain_dst_pos
omap_get_dma_chain_src_pos
This code has to be ported to OMAP GIT, please comment on design (not coding aspects).
/**
* @brief omap_request_dma_chain : Request a chain of DMA channels
*
* @param dev_id - Device id using the dma channel
* @param dev_name - Device name
* @param callback - Call back function
* @chain_id -
* @no_of_chans - Number of channels requested
* @chain_mode - Dynamic or static chaining : OMAP_DMA_STATIC_CHAIN
* OMAP_DMA_DYNAMIC_CHAIN
* @params - Channel parameters
*
* @return - Succes : 0
* Failure: EINVAL/EINVAL/EBUSY
*/
int
omap_request_dma_chain(int dev_id, const char *dev_name,
void (*callback) (int chain_id, u16 ch_status,
void *data), int *chain_id,
int no_of_chans, int chain_mode,
dma_channel_params params)
{
int *channels = NULL;
int i, err;
/* Is the chain mode valid ? */
if (chain_mode != OMAP_DMA_STATIC_CHAIN
&& chain_mode != OMAP_DMA_DYNAMIC_CHAIN) {
printk(KERN_ERR "Invalid chain mode requested \n");
return -EINVAL;
}
if (unlikely((no_of_chans < 1 || no_of_chans > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid Number of channels requested\n");
return -EINVAL;
}
/* Allocate a queue to maintain the status of the channels in the chain */
channels = (int *)kmalloc(sizeof(int) * no_of_chans, GFP_KERNEL);
if (channels == NULL) {
printk(KERN_ERR "omap_dma: No memory for channel queue\n");
return -ENOMEM;
}
/* request and reserve DMA channels for the chain */
for (i = 0; i < no_of_chans; i++) {
err =
omap_request_dma(dev_id, dev_name, callback, 0,
&channels[i]);
if (err < 0) {
/* Save duplicating code */
goto chain_fail;
}
dma_chan[channels[i]].next_linked_ch = -1;
dma_chan[channels[i]].prev_linked_ch = -1;
dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
/*
* Allowing client drivers to set common parameters now,
* so that later only relevant (src_start, dest_start and element count)
* can be set
*/
err = omap_set_dma_params(channels[i], params);
chain_fail:
if (err < 0) {
int j;
for (j = 0; j < i; j++) {
omap_free_dma(channels[j]);
}
kfree(channels);
printk(KERN_ERR "omap_dma: Request failed %d\n",err);
return err;
}
}
*chain_id = channels[0];
dma_linked_lch[*chain_id].linked_dmach_q = channels;
dma_linked_lch[*chain_id].chain_mode = chain_mode;
dma_linked_lch[*chain_id].chain_state = DMA_CHAIN_NOTSTARTED;
dma_linked_lch[*chain_id].no_of_lchs_linked = no_of_chans;
for (i = 0; i < no_of_chans; i++)
dma_chan[channels[i]].chain_id = *chain_id;
/* Reset the Queue pointers */
OMAP_DMA_CHAIN_QINIT(*chain_id);
/* Set up the chain */
if (no_of_chans == 1)
create_dma_lch_chain(channels[0], channels[0]);
else {
for (i = 0; i < (no_of_chans - 1); i++)
create_dma_lch_chain(channels[i], channels[i + 1]);
}
return 0;
}
/**
* @brief omap_modify_dma_chain_param : Modify the chain's params - Modify the
* params after setting it. Dont do this while dma is running!!
*
* @param chain_id - Chianed logical channel id.
* @param params
*
* @return - Success : 0
* Failure : EINVAL
*/
int
omap_modify_dma_chain_params(int chain_id, dma_channel_params params)
{
int *channels;
u32 i , ret;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
/*
* Allowing client drivers to set common parameters now,
* so that later only relevant (src_start, dest_start and element count)
* can be set
*/
if (0 != (ret = omap_set_dma_params(channels[i], params))) {
printk(KERN_ERR "Setting params of ch: %d failed.\n",
channels[i]);
return ret;
}
}
return 0;
}
/**
* @brief omap_free_dma_chain - Free all the logical channels in a chain.
*
* @param chain_id
*
* @return - Success : 0
* Failure :
*/
int
omap_free_dma_chain(int chain_id)
{
int *channels;
u32 i;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
dma_chan[channels[i]].next_linked_ch = -1;
dma_chan[channels[i]].prev_linked_ch = -1;
dma_chan[channels[i]].chain_id = -1;
dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
omap_free_dma(channels[i]);
}
kfree(channels);
dma_linked_lch[chain_id].linked_dmach_q = NULL;
dma_linked_lch[chain_id].chain_mode = -1;
dma_linked_lch[chain_id].chain_state = -1;
return (0);
}
/**
* @brief omap_dma_chain_status - Check if the chain is in active / inactive state.
*
* @param chain_id
*
* @return - Success : 0
* Failure : -EINVAL
*/
int
omap_dma_chain_status(int chain_id)
{
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
debug_printk((KERN_INFO "CHAINID=%d, qcnt=%d\n", chain_id,
dma_linked_lch[chain_id].q_count));
/* Check if all the channels in chain are in use */
if (OMAP_DMA_CHAIN_QEMPTY(chain_id)) {
return OMAP_DMA_CHAIN_INACTIVE;
}
return OMAP_DMA_CHAIN_ACTIVE;
}
/**
* @brief omap_dma_chain_a_transfer - Get a free channel from a chain, set the params and
* start the transfer.
*
* @param chain_id
* @param src_start - buffer start address
* @param dest_start - Dest address
* @param elem_count
* @param frame_count
* @param callbk_data - channel callback parameter data.
*
* @return - Success : start_dma status
* Failure: EINVAL/EBUSY
*/
int
omap_dma_chain_a_transfer(int chain_id, int src_start, int dest_start,
int elem_count, int frame_count, void *callbk_data)
{
int *channels;
u32 w, lch;
int start_dma = 0;
/* if buffer size is less than 1 then there is no use of starting the chain */
if (elem_count < 1) {
printk(KERN_ERR "Invalid buffer size\n");
return -EINVAL;
}
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
/* Check if all the channels in chain are in use */
if (OMAP_DMA_CHAIN_QFULL(chain_id)) {
return -EBUSY;
}
/* Frame count may be negative in case of indexed transfers */
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* Get a free channel */
lch = channels[dma_linked_lch[chain_id].q_tail];
/* Store the callback data */
dma_chan[lch].data = callbk_data;
/* Increment the q_tail */
if (~OMAP_DMA_CHAIN_QFULL(chain_id)) {
OMAP_DMA_CHAIN_INCQTAIL(chain_id);
}
/* Set the params to the free channel */
if (src_start != 0) {
writel(src_start, OMAP_DMA4_CSSA_REG(lch));
}
if (dest_start != 0) {
writel(dest_start, OMAP_DMA4_CDSA_REG(lch));
}
/* Write the buffer size */
writel(elem_count, OMAP_DMA4_CEN_REG(lch));
writel(frame_count, OMAP_DMA4_CFN_REG(lch));
/* If the chain is dynamically linked, then we may have to start the chain if its not active */
if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) {
/* In Dynamic chain, if the chain is not started queue the channel */
if (dma_linked_lch[chain_id].chain_state ==
DMA_CHAIN_NOTSTARTED) {
/* Enable the link in previous channel */
if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
DMA_CH_QUEUED) {
enable_lnk(dma_chan[lch].prev_linked_ch);
}
dma_chan[lch].state = DMA_CH_QUEUED;
}
/* Chain is already started, make sure its active, if not then start the chain */
else {
start_dma = 1;
/*
* if the previous channel is already started,
* then don't start DMA for this channel, just enable the link
*/
if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
DMA_CH_STARTED) {
/* link it with the prev one */
enable_lnk(dma_chan[lch].prev_linked_ch);
dma_chan[lch].state = DMA_CH_QUEUED;
start_dma = 0;
/* check if link enabled channel is already completed and stopped */
if (0 ==
(readl
(OMAP_DMA4_CCR_REG
(dma_chan[lch].
prev_linked_ch)) & CCR_EN)) {
/* the prev channel is already stopped,
start the dma for current channel */
/* disable the link that we enabled before */
disable_lnk(dma_chan[lch].
prev_linked_ch);
debug_printk((KERN_INFO "\n prev ch is stopped\n"));
start_dma = 1;
}
}
/* if the previous channel is not started, but its linked to its prev chan */
else if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
DMA_CH_QUEUED) {
enable_lnk(dma_chan[lch].prev_linked_ch);
dma_chan[lch].state = DMA_CH_QUEUED;
start_dma = 0;
}
enable_dma_irq(lch);
w = readl(OMAP_DMA4_CCR_REG(lch));
if ((0 == (w & (1 << 24)))) {
/* 2420 ES1 - not implemented, and dest sync - dont use */
w &= ~(CCR_BUF_DIS);
} else {
w |= CCR_BUF_DIS;
}
if (start_dma == 1) {
/* for chaining case, don't start if the channel is already started */
if (0 == (w & CCR_EN)) {
w |= CCR_EN;
dma_chan[lch].state = DMA_CH_STARTED;
debug_printk((KERN_INFO "starting %d\n", lch));
writel(w, OMAP_DMA4_CCR_REG(lch));
} else
start_dma = 0;
} else {
/* DMA will not be started, just BUF DIS settings will be done */
/* if DMA was already started, then don't write to CCR, it will
affect the ongoing transmission */
if (0 == (w & CCR_EN)) {
writel(w, OMAP_DMA4_CCR_REG(lch));
}
}
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
} /* else dynamic chain is started */
} /* if dynamic chain */
#ifdef DEBUG_DMA_DUMP
{ int i;
debug_printk((KERN_INFO "CHAINATRANFER\n"));
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
omap_dump_lch_reg(channels[i]);
}
}
#endif
return start_dma;
}
/**
* @brief omap_start_dma_chain_transfers - Start the chain
*
* @param chain_id
*
* @return - Success : 0
* Failure : EINVAL/EBUSY
*/
int
omap_start_dma_chain_transfers(int chain_id)
{
int *channels = NULL;
u32 w, i;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* If the chain is started, then don't start it again */
if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) {
printk(KERN_ERR "Chain is already started \n");
return -EBUSY;
}
/* If chain is static */
if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) {
/* Set the ENABLE_LNK bit */
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
/* Enable link */
enable_lnk(channels[i]);
/* Enable irq */
enable_dma_irq(channels[i]);
}
} else {
enable_dma_irq(channels[0]);
}
w = readl(OMAP_DMA4_CCR_REG(channels[0]));
w |= CCR_EN;
dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED;
dma_chan[channels[0]].state = DMA_CH_STARTED;
if ((0 == (w & CCR_SEL_SRC_DST_SYNC))) {
/* 2420 ES1 - not implemented, and dest sync - dont use */
w &= ~(CCR_BUF_DIS);
} else {
w |= CCR_BUF_DIS;
}
/* Dump Registers */
#ifdef DEBUG_DMA_DUMP
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
omap_dump_lch_reg(channels[i]);
}
#endif
writel(w, OMAP_DMA4_CCR_REG(channels[0]));
dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE;
return 0;
}
/**
* @brief omap_stop_dma_chain_transfers - Stop the dma transfer of a chain.
*
* @param chain_id
*
* @return - Success : 0
* Failure : EINVAL
*/
int
omap_stop_dma_chain_transfers(int chain_id)
{
int *channels;
u32 w, i, lch = 0;
u32 sys_cf;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* DMA Errata:
* Special programming model needed to disable DMA before end of block
*/
w = sys_cf = readl(OMAP_DMA4_OCP_SYSCONFIG);
/* Middle mode reg set no Standby */
w &= ~(OCPSYS_MIDMODE_NOSTDBY|OCPSYS_MIDMODE_STDBY);
writel(w, OMAP_DMA4_OCP_SYSCONFIG);
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
/* Stop the Channel transmission */
w = readl(OMAP_DMA4_CCR_REG(channels[i]));
w &= ~CCR_EN;
writel(w, OMAP_DMA4_CCR_REG(channels[i]));
/* Disable the link in all the channels */
disable_lnk(channels[i]);
dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
}
dma_linked_lch[chain_id].chain_state = DMA_CHAIN_NOTSTARTED;
/* Reset the Queue pointers */
OMAP_DMA_CHAIN_QINIT(chain_id);
/* Errata - put in the old value */
writel(sys_cf, OMAP_DMA4_OCP_SYSCONFIG);
dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
return 0;
}
/* Get the index of the ongoing DMA in chain */
/**
* @brief omap_get_dma_chain_index - Get the element and frame index
* of the ongoing DMA in chain
*
* @param chain_id
* @param ei - Element index
* @param fi - Frame index
*
* @return - Success : 0
* Failure : EINVAL
*/
int
omap_get_dma_chain_index(int chain_id, int *ei, int *fi)
{
int lch = 0;
int *channels;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
if ((!ei) || (!fi)) {
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* Get the current channel */
lch = channels[dma_linked_lch[chain_id].q_head];
*ei = (readl(OMAP_DMA4_CCEN_REG(lch)));
*fi = (readl(OMAP_DMA4_CCFN_REG(lch)));
return 0;
}
/**
* @brief omap_get_dma_chain_dst_pos - Get the destination position of the
* ongoing DMA in chain
*
* @param chain_id
*
* @return - Success : Destination position
* Failure : EINVAL
*/
int
omap_get_dma_chain_dst_pos(int chain_id)
{
int lch = 0;
int *channels;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* Get the current channel */
lch = channels[dma_linked_lch[chain_id].q_head];
return (readl(OMAP_DMA4_CDAC_REG(lch)));
}
/**
* @brief omap_get_dma_chain_src_pos - Get the source position of the ongoing DMA in chain
*
* @param chain_id
*
* @return - Success : Destination position
* Failure : -EINVAL
*/
int
omap_get_dma_chain_src_pos(int chain_id)
{
int lch = 0;
int *channels;
/* Check for input params */
if (unlikely((chain_id < 0 || chain_id > OMAP_LOGICAL_DMA_CH_COUNT))) {
printk(KERN_ERR "Invalid chain id\n");
return -EINVAL;
}
/* Check if the chain exists */
if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
printk(KERN_ERR "Chain doesn't exists\n");
return -EINVAL;
}
channels = dma_linked_lch[chain_id].linked_dmach_q;
/* Get the current channel */
lch = channels[dma_linked_lch[chain_id].q_head];
return (readl(OMAP_DMA4_CSAC_REG(lch)));
}
Regards,
Khasim
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [RFC] Generic DMA chaining APIs
2007-06-06 5:02 [RFC] Generic DMA chaining APIs Syed Mohammed, Khasim
@ 2007-08-10 7:33 ` Tony Lindgren
0 siblings, 0 replies; 2+ messages in thread
From: Tony Lindgren @ 2007-08-10 7:33 UTC (permalink / raw)
To: Syed Mohammed, Khasim; +Cc: Linux OMAP
* Syed Mohammed, Khasim <x0khasim@ti.com> [070605 22:07]:
> Hi all:
>
> I would like to port Generic DMA chaining API support from TI's 2430 DMA driver to our OMAP GIT DMA driver.
>
> - Basically these APIs give flexibility for handling DMA chaining for drivers like McBSP (for audio), McSPI (for LCD), UART, etc.
>
> - Chaining is very important for drivers like Audio, LCD etc.
>
> - Our current DMA driver on GIT does support APIs to enable /configure DMA logical channel chaining, but they have to be again used in client drivers to implement the queuing logic.
>
> - Since there are multiple drivers in need of these kind of chaining support, it's preferable to implement them in a generic way so that all other drivers can leverage the code instead of creating a redundant copy in every driver.
>
> A code snippet (from TI's DMA driver) is as shown below. Please do let me know if you find any issues with this kind of implementation/approach before I start (this week) this porting activity.
>
> Thanks in advance.
>
> APIs to be ported are:
> =====================
> omap_request_dma_chain
> omap_free_dma_chain
> omap_dma_chain_a_transfer
> omap_modify_dma_chain_params
> omap_start_dma_chain_transfers
> omap_stop_dma_chain_transfers
>
> omap_dma_chain_status
> omap_get_dma_chain_index
> omap_get_dma_chain_dst_pos
> omap_get_dma_chain_src_pos
The new functions look OK to me.
Regards,
Tony
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2007-08-10 7:33 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-06-06 5:02 [RFC] Generic DMA chaining APIs Syed Mohammed, Khasim
2007-08-10 7:33 ` Tony Lindgren
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox