[PATCH v3] OMAP4: sDMA driver: descriptor autoloading feature

Venkatraman S svenkatr at ti.com
Fri Dec 11 10:03:34 EST 2009


Hi Russell,
On Mon, Oct 12, 2009 at 3:23 PM, Russell King - ARM Linux
<linux at arm.linux.org.uk> wrote:
> On Fri, Sep 04, 2009 at 12:04:08AM +0530, S, Venkatraman wrote:
>> +static int dma_caps0_status;
>> +
>> +struct omap_dma_list_config_params {
>> +       int chid;
>
> This seems to be unused.
>
>> +       int num_elem;
>
> This might want to be unsigned.
>
>> +       struct omap_dma_sglist_node *sghead;
>> +       int sgheadphy;
>
> This should be dma_addr_t.
>
>> +       int pausenode;
>
> Should this also be unsigned?  (If so, the 'pauseafter' parameter should
> be as well.
>
>> +};
>
>
>> +int omap_request_dma_sglist(int dev_id, const char *dev_name,
>> +       void (*callback) (int channel_id, u16 ch_status, void *data),
>> +       int *listid, int nelem, struct omap_dma_sglist_node **elems)
>> +{
>> +       struct omap_dma_list_config_params *lcfg;
>> +       struct omap_dma_sglist_node *desc;
>> +       int dma_lch;
>> +       int rc, i;
>> +
>> +       if (unlikely((dma_caps0_status & DMA_CAPS_SGLIST_SUPPORT) == 0)) {
>> +               printk(KERN_ERR "omap DMA: sglist feature not supported\n");
>> +               return -EPERM;
>> +       }
>> +       if (unlikely(nelem <= 2)) {
>> +               printk(KERN_ERR "omap DMA: Need >2 elements in the list\n");
>> +               return -EINVAL;
>> +       }
>
> I'm not sure these unlikely()'s help for two reasons:
> 1. gcc probably has already worked that out (later versions have become
>   quite a bit better at this.)
> 2. since we're calling allocation functions, this isn't a hot path, so
>   the few cycles the above _may_ save really isn't worth it
>
>> +       rc = omap_request_dma(dev_id, dev_name,
>> +                         callback, NULL, &dma_lch);
>> +       if (rc < 0) {
>> +               printk(KERN_ERR "omap_dma_list: Request failed %d\n", rc);
>
> You use a prefix of "omap DMA" above, but "omap_dma_list" here - it would
> be nice to be consistent.
>
>> +               return rc;
>> +       }
>> +       *listid = dma_lch;
>> +       dma_chan[dma_lch].state = DMA_CH_NOTSTARTED;
>> +       lcfg = kmalloc(sizeof(*lcfg), GFP_KERNEL);
>> +       if (NULL == lcfg)
>> +               goto error1;
>> +       dma_chan[dma_lch].list_config = lcfg;
>> +
>> +       lcfg->num_elem = nelem;
>> +
>> +       *elems = desc = lcfg->sghead = dma_alloc_coherent(NULL,
>> +               sizeof(*desc), &(lcfg->sgheadphy), 0);
>> +       if (NULL == desc)
>> +               goto error1;
>> +
>> +       for (i = 1; i < nelem; i++) {
>> +               desc->next = dma_alloc_coherent(NULL,
>> +                       sizeof(*(desc->next)), &(desc->next_desc_add_ptr), 0);
>> +               if (NULL == desc->next)
>> +                       goto error1;
>> +               desc = desc->next;
>> +               desc->next = NULL;
>> +       }
>
> The smallest allocation unit available from dma_alloc_coherent() is one
> page.  You really don't want to allocate one page per descriptor.
> Instead, you want to do this:
>
>        lcfg->sghead = dma_alloc_coherent(/*some device*/,
>                        sizeof(*desc) * nelem, &lcfg->sgheadphy, 0);
>        if (!lcfg->sghead)
>                goto error1;
>
>        *elems = desc = lcfg->sghead;
>        desc_dma = lcfg->sgheadphy;
>        for (i = 1; i < nelem; i++, desc++) {
>                desc->next = desc + 1;
>                desc->next_desc_add_ptr = desc_dma + sizeof(*desc);
>        }
>
> This might also mean that you can get rid of the desc->next walking
> and just use a plain for loop.
>
> Also, using a struct device for the device actually doing the DMA would
> be a Good Thing - this becomes much more relevent when we want to be
> able to allocate coherent DMA pages from highmem (since passing NULL
> means we have to allocate from the low DMA region.)
>
>> +       desc->next_desc_add_ptr = OMAP_DMA_INVALID_DESCRIPTOR_POINTER;
>> +
>> +       dma_write(0, CCDN(dma_lch)); /* Reset List index numbering */
>> +       /* Initialize frame and element counters to invalid values */
>> +       dma_write(OMAP_DMA_INVALID_FRAME_COUNT, CCFN(dma_lch));
>> +       dma_write(OMAP_DMA_INVALID_ELEM_COUNT, CCEN(dma_lch));
>> +       return 0;
>> +
>> +error1:
>> +       omap_release_dma_sglist(dma_lch);
>> +       return -ENOMEM;
>> +
>> +}
>> +EXPORT_SYMBOL(omap_request_dma_sglist);
>> +
>> +/* The client can choose to not preconfigure the DMA registers
>> + * In fast mode,the DMA controller uses the first element in the list to
>> + * program the registers first, and then starts the transfer
>> + */
>> +
>> +int omap_set_dma_sglist_params(const int listid,
>> +       struct omap_dma_sglist_node *sghead,
>> +       struct omap_dma_channel_params *chparams)
>> +{
>> +       struct omap_dma_list_config_params *lcfg;
>> +       struct omap_dma_sglist_node *sgitcurr, *sgitprev;
>> +       int l = DMA_LIST_CDP_LISTMODE; /* Enable Linked list mode in CDP */
>> +
>> +       lcfg = dma_chan[listid].list_config;
>> +       if (lcfg->sghead != sghead) {
>> +               printk(KERN_ERR "Unknown config pointer passed\n");
>> +               return -EPERM;
>> +       }
>
> If sghead can't be anything different from lcfg->sghead, what is the
> purpose of passing sghead into this function?
>
>> +
>> +       if (NULL == chparams)
>> +               l |= DMA_LIST_CDP_FASTMODE;
>> +       else
>> +               omap_set_dma_params(listid, chparams);
>> +               /* The client can set the dma params and still use fast mode
>> +                * by using the set fast mode api
>> +                */
>> +       dma_write(l, CDP(listid));
>> +
>> +       sgitprev = sghead;
>> +       sgitcurr = sgitprev->next;
>> +
>> +       while (sgitcurr != NULL) {
>> +               switch (sgitcurr->desc_type) {
>> +               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1:
>> +                       omap_dma_list_set_ntype(sgitprev, 1);
>> +                       break;
>> +
>> +               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
>> +                       /* intentional no break */
>> +               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
>> +                       omap_dma_list_set_ntype(sgitprev, 2);
>> +                       break;
>> +
>> +               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
>> +                       /* intentional no break */
>> +               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
>> +                       omap_dma_list_set_ntype(sgitprev, 3);
>> +                       break;
>> +               default:
>> +                       return -EINVAL;
>> +               }
>> +               if (sgitcurr->flags & OMAP_DMA_LIST_SRC_VALID)
>> +                       sgitprev->num_of_elem |= DMA_LIST_DESC_SRC_VALID;
>> +               if (sgitcurr->flags & OMAP_DMA_LIST_DST_VALID)
>> +                       sgitprev->num_of_elem |= DMA_LIST_DESC_DST_VALID;
>> +               if (sgitcurr->flags & OMAP_DMA_LIST_NOTIFY_BLOCK_END)
>> +                       sgitprev->num_of_elem |= DMA_LIST_DESC_BLK_END;
>> +
>> +               sgitprev = sgitcurr;
>> +               sgitcurr = sgitcurr->next;
>> +       }
>
> This becomes:
>        for (sgprev = lcfg->sghead;
>             sgcurr = sgprev + 1, sgcurr < lcfg->sghead + lcfg->num_elem;
>             sgprev = sgcurr) {
>                switch (sgcurr->desc_type) {
>                        ...
>                }
>        }
>
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(omap_set_dma_sglist_params);
>> +
>> +int omap_start_dma_sglist_transfers(const int listid, const int pauseafter)
>
> 'const' for non-pointer function parameters doesn't have a benefit for
> code generation - the only thing it does is issue a warning if you write
> to them.  Are they serving a special purpose here?
>
>> +{
>> +       struct omap_dma_list_config_params *lcfg;
>> +       struct omap_dma_sglist_node *sgn;
>> +       unsigned int l, type_id;
>> +
>> +       lcfg = dma_chan[listid].list_config;
>> +       lcfg->pausenode = 0;
>> +       sgn = lcfg->sghead;
>> +       if (pauseafter > 0 && pauseafter <= lcfg->num_elem) {
>> +               for (l = 0; l < pauseafter; l++)
>> +                       sgn = sgn->next;
>
> Are you sure this code is correct?  Two problems:
>
> 1. If 'pauseafter' is 1, sgn is the second entry in the list, not the
>   first, so you can't pause after the first descriptor.
>
> 2. If 'pauseafter' is 'nelem' then sgn will be NULL, resulting in an oops.
>
>> +               sgn->next_desc_add_ptr |= DMA_LIST_DESC_PAUSE;
>
> Assuming you have an out-by-one error above, this becomes:
>                sgn[pauseafter].next_desc_add_ptr |= DMA_LIST_DESC_PAUSE;
>
>> +               lcfg->pausenode = pauseafter;
>> +       }
>> +
>> +       /* Program the head descriptor's properties into CDP */
>> +       switch (lcfg->sghead->desc_type) {
>> +       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1:
>> +               type_id = DMA_LIST_CDP_TYPE1;
>> +               break;
>> +       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
>> +       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
>> +               type_id = DMA_LIST_CDP_TYPE2;
>> +               break;
>> +       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
>> +       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
>> +               type_id = DMA_LIST_CDP_TYPE3;
>> +               break;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +       l = dma_read(CDP(listid));
>> +       l |= type_id;
>> +       if (lcfg->sghead->flags & OMAP_DMA_LIST_SRC_VALID)
>> +               l |= DMA_LIST_CDP_SRC_VALID;
>> +       if (lcfg->sghead->flags & OMAP_DMA_LIST_DST_VALID)
>> +               l |= DMA_LIST_CDP_DST_VALID;
>> +
>> +       dma_write(l, CDP(listid));
>> +
>> +       dma_write((lcfg->sgheadphy), CNDP(listid));
>> +       printk(KERN_DEBUG "Start list transfer for list %x\n",
>> +               lcfg->sgheadphy);
>> +       omap_start_dma(listid);
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(omap_start_dma_sglist_transfers);
>> +
>> +int omap_resume_dma_sglist_transfers(const int listid, const int pauseafter)
>
> Same comment as above wrt 'const' parameters.
>
>> +{
>> +       struct omap_dma_list_config_params *lcfg;
>> +       struct omap_dma_sglist_node *sgn;
>> +       int l;
>> +
>> +       lcfg = dma_chan[listid].list_config;
>> +       sgn = lcfg->sghead;
>> +       /* Clear the previous pause, if any */
>> +       lcfg->pausenode = 0;
>> +
>> +       if (pauseafter > 0 && pauseafter <= lcfg->num_elem) {
>> +               for (l = 0; l < pauseafter; l++)
>> +                       sgn = sgn->next;
>> +               sgn->next_desc_add_ptr |= DMA_LIST_DESC_PAUSE;
>
> Same comments as per omap_start_dma_sglist_transfers().
>
>> +               lcfg->pausenode = pauseafter;
>> +       }
>
> Incidentally, shouldn't this be an inline function or something like that,
> since this seems to be a duplication of what's happening in
> omap_start_dma_sglist_transfers() ?
>
>> +
>> +       /* Clear pause bit in CDP */
>> +       l = dma_read(CDP(listid));
>> +       printk(KERN_DEBUG "Resuming after pause: CDP=%x\n", l);
>> +       l &= ~(DMA_LIST_CDP_PAUSEMODE);
>> +       dma_write(l, CDP(listid));
>> +       omap_start_dma(listid);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(omap_resume_dma_sglist_transfers);
>> +
>> +int omap_release_dma_sglist(const int listid)
>> +{
>> +       struct omap_dma_list_config_params *lcfg;
>> +       struct omap_dma_sglist_node *sgn, *sgn2;
>> +
>> +       lcfg = dma_chan[listid].list_config;
>> +       sgn = lcfg->sghead;
>> +
>> +       if (NULL != sgn) {
>> +               sgn = sgn->next;
>> +               do {
>> +                       sgn2 = sgn->next;
>> +                       dma_free_coherent(NULL, sizeof(*sgn), sgn,
>> +                               sgn->next_desc_add_ptr);
>> +                       sgn = sgn2;
>> +               } while (sgn2 != NULL);
>> +
>> +               dma_free_coherent(NULL, sizeof(*(lcfg->sghead)),
>> +                       lcfg->sghead, lcfg->sgheadphy);
>
> As a result of the way these are allocated, this can become the much
> simpler:
>
>                dma_free_coherent(/*some device */,
>                        sizeof(*lcfg->sghead) * lcfg->num_elem,
>                        lcfg->sghead, lcfg->sgheadphy);
>
>> +       }
>> +       if (NULL != dma_chan[listid].list_config)
>> +               kfree(dma_chan[listid].list_config);
>> +       dma_chan[listid].list_config = NULL;
>> +       omap_free_dma(listid);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(omap_release_dma_sglist);
>> +
>> +int omap_get_completed_sglist_nodes(const int listid)
>> +{
>> +       int list_count;
>> +
>> +       list_count = dma_read(CCDN(listid));
>> +       return list_count & 0xffff; /* only 16 LSB bits are valid */
>> +}
>> +EXPORT_SYMBOL(omap_get_completed_sglist_nodes);
>> +
>> +int omap_dma_sglist_is_paused(const int listid)
>> +{
>> +       int list_state;
>> +
>> +       list_state = dma_read(CDP(listid));
>> +       return (list_state & DMA_LIST_CDP_PAUSEMODE) ? 1 : 0;
>> +}
>> +EXPORT_SYMBOL(omap_dma_sglist_is_paused);
>> +
>> +void omap_dma_set_sglist_fastmode(const int listid, const int fastmode)
>> +{
>> +       int l = dma_read(CDP(listid));
>> +
>> +       if (fastmode)
>> +               l |= DMA_LIST_CDP_FASTMODE;
>> +       else
>> +               l &= ~(DMA_LIST_CDP_FASTMODE);
>> +       dma_write(l, CDP(listid));
>> +}
>> +EXPORT_SYMBOL(omap_dma_set_sglist_fastmode);
>> +
>>
>>  #ifdef CONFIG_ARCH_OMAP1
>>
>> @@ -2367,6 +2668,7 @@ static int __init omap_init_dma(void)
>>                         kfree(dma_chan);
>>                         return -ENOMEM;
>>                 }
>> +               dma_caps0_status = dma_read(CAPS_0);
>>         }
>>
>>         if (cpu_is_omap15xx()) {
>> @@ -2418,6 +2720,7 @@ static int __init omap_init_dma(void)
>>                 omap_clear_dma(ch);
>>                 dma_chan[ch].dev_id = -1;
>>                 dma_chan[ch].next_lch = -1;
>> +               dma_chan[ch].list_config = NULL;
>>
>>                 if (ch >= 6 && enable_1510_mode)
>>                         continue;
>> diff --git a/arch/arm/plat-omap/include/mach/dma.h b/arch/arm/plat-omap/include/mach/dma.h
>> index 72f680b..ca2970d 100644
>> --- a/arch/arm/plat-omap/include/mach/dma.h
>> +++ b/arch/arm/plat-omap/include/mach/dma.h
>> @@ -112,8 +112,12 @@
>>  #define OMAP1_DMA_COLOR_U(n)           (0x40 * (n) + 0x22)
>>  #define OMAP1_DMA_CCR2(n)              (0x40 * (n) + 0x24)
>>  #define OMAP1_DMA_LCH_CTRL(n)          (0x40 * (n) + 0x2a)     /* not on 15xx */
>> +#define OMAP1_DMA_COLOR(n)             0
>>  #define OMAP1_DMA_CCEN(n)              0
>>  #define OMAP1_DMA_CCFN(n)              0
>> +#define OMAP1_DMA_CDP(n)               0
>> +#define OMAP1_DMA_CNDP(n)              0
>> +#define OMAP1_DMA_CCDN(n)              0
>>
>>  /* Channel specific registers only on omap2 */
>>  #define OMAP_DMA4_CSSA(n)              (0x60 * (n) + 0x9c)
>> @@ -132,6 +136,8 @@
>>  #define OMAP1_DMA_IRQSTATUS_L0         0
>>  #define OMAP1_DMA_IRQENABLE_L0         0
>>  #define OMAP1_DMA_OCP_SYSCONFIG                0
>> +#define OMAP1_DMA_CAPS_0               0
>> +
>>  #define OMAP_DMA4_HW_ID                        0
>>  #define OMAP_DMA4_CAPS_0_L             0
>>  #define OMAP_DMA4_CAPS_0_U             0
>> @@ -576,6 +582,83 @@ struct omap_dma_channel_params {
>>  #endif
>>  };
>>
>> +struct omap_dma_sglist_type1_params {
>> +       u32 src_addr;
>> +       u32 dst_addr;
>> +       u16 cfn_fn;
>> +       u16 cicr;
>> +       u16 dst_elem_idx;
>> +       u16 src_elem_idx;
>> +       u32 dst_frame_idx_or_pkt_size;
>> +       u32 src_frame_idx_or_pkt_size;
>> +       u32 color;
>> +       u32 csdp;
>> +       u32 clnk_ctrl;
>> +       u32 ccr;
>> +};
>> +
>> +struct omap_dma_sglist_type2a_params {
>> +       u32 src_addr;
>> +       u32 dst_addr;
>> +       u16 cfn_fn;
>> +       u16 cicr;
>> +       u16 dst_elem_idx;
>> +       u16 src_elem_idx;
>> +       u32 dst_frame_idx_or_pkt_size;
>> +       u32 src_frame_idx_or_pkt_size;
>> +};
>> +
>> +struct omap_dma_sglist_type2b_params {
>> +       u32 src_or_dest_addr;
>> +       u16 cfn_fn;
>> +       u16 cicr;
>> +       u16 dst_elem_idx;
>> +       u16 src_elem_idx;
>> +       u32 dst_frame_idx_or_pkt_size;
>> +       u32 src_frame_idx_or_pkt_size;
>> +};
>> +
>> +struct omap_dma_sglist_type3a_params {
>> +       u32 src_addr;
>> +       u32 dst_addr;
>> +};
>> +
>> +struct omap_dma_sglist_type3b_params {
>> +       u32 src_or_dest_addr;
>> +};
>> +
>> +enum omap_dma_sglist_descriptor_select {
>> +       OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1,
>> +       OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a,
>> +       OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b,
>> +       OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a,
>> +       OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b,
>> +};
>> +
>> +union omap_dma_sglist_node_type{
>> +       struct omap_dma_sglist_type1_params t1;
>> +       struct omap_dma_sglist_type2a_params t2a;
>> +       struct omap_dma_sglist_type2b_params t2b;
>> +       struct omap_dma_sglist_type3a_params t3a;
>> +       struct omap_dma_sglist_type3b_params t3b;
>> +};
>> +
>> +struct omap_dma_sglist_node {
>> +
>> +       /* Common elements for all descriptors */
>> +       u32 next_desc_add_ptr;
>
> For correct typing, this should be:
>
>        dma_addr_t next_desc_add_ptr;
>
> DMA addresses have type 'dma_addr_t' not u32.
>
>> +       u32 num_of_elem;
>> +       /* Type specific elements */
>> +       union omap_dma_sglist_node_type sg_node;
>> +       /* Control fields */
>> +       int flags;
>
> Maybe unsigned?
>
>> +       /* Fields that can be set in flags variable */
>> +       #define OMAP_DMA_LIST_SRC_VALID         (1)
>> +       #define OMAP_DMA_LIST_DST_VALID         (2)
>> +       #define OMAP_DMA_LIST_NOTIFY_BLOCK_END  (4)
>
> And if you're only using a small number of flags, do you need 32 bits for
> them?  Maybe something a little smaller like unsigned short?
>
>> +       u32 desc_type;
>
> Isn't this enum omap_dma_sglist_descriptor_select ?  If you correctly
> type this, the compiler can check that you're handling all cases in a
> switch statement automatically, which means if you add to the enum,
> it'll tell you the place you missed.
>
>> +       struct omap_dma_sglist_node *next;
>> +};
>>
>>  extern void omap_set_dma_priority(int lch, int dst_port, int priority);
>>  extern int omap_request_dma(int dev_id, const char *dev_name,
>> @@ -655,6 +738,59 @@ extern int omap_modify_dma_chain_params(int chain_id,
>>                                         struct omap_dma_channel_params params);
>>  extern int omap_dma_chain_status(int chain_id);
>>  #endif
>> +/* omap_request_dma_sglist:
>> + * Request to setup a DMA channel to transfer in linked list mode of nelem
>> + * elements. The memory for the list will be allocated and returned in
>> + * elems structure
>> + */
>> +extern int omap_request_dma_sglist(int dev_id, const char *dev_name,
>> +               void (*callback) (int channel_id, u16 ch_status, void *data),
>> +               int *listid, int nelem, struct omap_dma_sglist_node **elems);
>> +/* omap_set_dma_sglist_params
>> + * Provide the configuration parameters for the sglist channel
>> + * sghead should contain a fully populated list of nelems
>> + * which completely describe the transfer. chparams, if not NULL, will
>> + * set the appropriate parameters directly into the DMA register.
>> + * If chparams is NULL, fastmode will be enabled automatically
>> + */
>> +extern int omap_set_dma_sglist_params(const int listid,
>> +                                     struct omap_dma_sglist_node *sghead,
>> +                                     struct omap_dma_channel_params *chparams);
>> +/* omap_start_dma_sglist_transfers
>> + * Starts the linked list based DMA transfer for the specified listid
>> + * If no pause is required, -1 is to be set in pauseafter.
>> + * Else, the transfer will suspend after pauseafter elements.
>> + */
>> +extern int omap_start_dma_sglist_transfers(const int listid,
>> +                                          const int pauseafter);
>> +/* omap_resume_dma_sglist_transfers
>> + * Resumes the previously paused transfer.
>> + * Can be again set to pause at pauseafter node of the linked list
>> + * The index is absolute (from the head of the list)
>> + */
>> +extern int omap_resume_dma_sglist_transfers(const int listid,
>> +                                           const int pauseafter);
>> +/* omap_release_dma_sglist
>> + * Releases the list based DMA channel and the associated list descriptors
>> + */
>> +extern int omap_release_dma_sglist(const int listid);
>> +/* omap_get_completed_sglist_nodes
>> + * Returns the number of completed elements in the linked list
>> + * The value is transient if the API is invoked for an ongoing transfer
>> + */
>> +int omap_get_completed_sglist_nodes(const int listid);
>> +/* omap_dma_sglist_is_paused
>> + * Returns non zero if the linked list is currently in pause state
>> + */
>> +int omap_dma_sglist_is_paused(const int listid);
>> +/* omap_dma_set_sglist_fastmode
>> + * Set or clear the fastmode status of the transfer
>> + * In fastmode, DMA register settings are updated from the first element
>> + * of the linked list, before initiating the tranfer.
>> + * In non-fastmode, the first element is used only after completing the
>> + * transfer as already configured in the registers
>> + */
>> +void omap_dma_set_sglist_fastmode(const int listid, const int fastmode);
>>
>>  /* LCD DMA functions */
>>  extern int omap_request_lcd_dma(void (*callback)(u16 status, void *data),
>> ---

Apologies for a long break in this thread. Thanks for your detailed
review. I have posted a patch with all your comments taken in.

Best regards,
Venkat.



More information about the linux-arm-kernel mailing list