This patch adds the essential APIs for using the linked list mode of OMAP4 sDMA.

Signed-off-by: Venkatraman S <[email protected]>

---
 arch/arm/plat-omap/dma.c              |  284 +++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/mach/dma.h |  100 ++++++++++++
 2 files changed, 384 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c
index 7677a4a..dd4990a 100644
--- a/arch/arm/plat-omap/dma.c
+++ b/arch/arm/plat-omap/dma.c
@@ -29,6 +29,7 @@
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/system.h>
 #include <mach/hardware.h>
@@ -52,8 +53,23 @@ enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED };
 
 #define OMAP_FUNC_MUX_ARM_BASE         (0xfffe1000 + 0xec)
 
+#define OMAP_DMALIST_SET_NTYPE(nod_, val_) \
+       do { \
+               (nod_)->num_of_elem |= ((val_) << 29); \
+       } while (0)
+
 static int enable_1510_mode;
 
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+struct omap_dma_list_config_params {
+       int chid;
+       int num_elem;
+       struct omap_dma_sglist_node *sghead;
+       int sgheadphy;
+       int pausenode;
+};
+#endif
+
 struct omap_dma_lch {
        int next_lch;
        int dev_id;
@@ -72,6 +88,10 @@ struct omap_dma_lch {
 
        int status;
 #endif
+
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+       void *list_config;
+#endif
        long flags;
 };
 
@@ -1787,6 +1807,267 @@ EXPORT_SYMBOL(omap_get_dma_chain_src_pos);
 #endif /* ifndef CONFIG_ARCH_OMAP1 */
 
 
/*----------------------------------------------------------------------------*/
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+
+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(nelem <= 2)) {
+               printk(KERN_ERR "omap DMA: Need >2 elements in the list\n");
+               return -EINVAL;
+       }
+       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);
+               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;
+       }
+       desc->next_desc_add_ptr = 0xfffffffc;
+
+       /* TRM Sec 1.4.21.4.3 */
+       dma_write(0, CCDN(dma_lch));
+       dma_write(0xffff, CCFN(dma_lch));
+       dma_write(0xffffff, 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 = 0x100; /* 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 (NULL == chparams)
+               l |= 0x400;  /* FAST mode bit:10 */
+       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_DMALIST_SET_NTYPE(sgitprev, 1);
+                       break;
+
+               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
+                       /* intentional no break */
+               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
+                       OMAP_DMALIST_SET_NTYPE(sgitprev, 2);
+                       break;
+
+               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
+                       /* intentional no break */
+               case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
+                       OMAP_DMALIST_SET_NTYPE(sgitprev, 3);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+               if (sgitcurr->flags & OMAP_DMA_LIST_SRC_VALID)
+                       sgitprev->next_desc_add_ptr |= 1<<24;
+               if (sgitcurr->flags & OMAP_DMA_LIST_DST_VALID)
+                       sgitprev->next_desc_add_ptr |= 1<<26;
+               if (sgitcurr->flags & OMAP_DMA_LIST_NOTIFY_BLOCK_END)
+                       sgitprev->next_desc_add_ptr |= 1<<28;
+
+               sgitprev = sgitcurr;
+               sgitcurr = sgitcurr->next;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(omap_set_dma_sglist_params);
+
+int omap_start_dma_sglist_transfers(const int listid, const int pauseafter)
+{
+       struct omap_dma_list_config_params *lcfg;
+       struct omap_dma_sglist_node *sgn;
+       unsigned int l, typeid;
+
+       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;
+               sgn->next_desc_add_ptr |= 0x1;
+               lcfg->pausenode = pauseafter;
+       }
+
+       switch (lcfg->sghead->desc_type) {
+       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1:
+               typeid = 1;
+               break;
+       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a:
+       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b:
+               typeid = 2;
+               break;
+       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a:
+       case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b:
+               typeid = 3;
+               break;
+       default:
+               BUG();
+       }
+
+       l = dma_read(CDP(listid));
+       l |= (typeid << 4);
+       if (lcfg->sghead->flags & OMAP_DMA_LIST_SRC_VALID)
+               l |= (1 << 2);
+       if (lcfg->sghead->flags & OMAP_DMA_LIST_DST_VALID)
+               l |= 1;
+
+       dma_write(l, CDP(listid));
+       dma_write(0, CCDN(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)
+{
+       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 |= 0x1;
+               lcfg->pausenode = pauseafter;
+       }
+
+       /* Clear pause bit in CDP */
+       l = dma_read(CDP(listid));
+       printk(KERN_DEBUG "Resuming after pause CDP=%x\n", l);
+       l &= 0x77f;
+       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);
+       }
+       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;
+}
+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 & 0x80) ? 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 |= 0x400;
+       else
+               l &= 0xffffffdf;
+       dma_write(l, CDP(listid));
+}
+EXPORT_SYMBOL(omap_dma_set_sglist_fastmode);
+#endif
 
 #ifdef CONFIG_ARCH_OMAP1
 
@@ -2418,6 +2699,9 @@ static int __init omap_init_dma(void)
                omap_clear_dma(ch);
                dma_chan[ch].dev_id = -1;
                dma_chan[ch].next_lch = -1;
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+               dma_chan[ch].list_config = NULL;
+#endif
 
                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..df4a7b8 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)
@@ -576,6 +580,86 @@ struct omap_dma_channel_params {
 #endif
 };
 
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+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;
+       u32 num_of_elem;
+       /* Type specific elements */
+       union omap_dma_sglist_node_type sg_node;
+       /* Control fields */
+       int flags;
+       /* 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)
+       u32 desc_type;
+       struct omap_dma_sglist_node *next;
+};
+
+#endif
 
 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,
@@ -656,6 +740,22 @@ extern int omap_modify_dma_chain_params(int chain_id,
 extern int omap_dma_chain_status(int chain_id);
 #endif
 
+#ifdef CONFIG_OMAP_DMA_DESCRIPTOR_LOAD
+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);
+extern int omap_set_dma_sglist_params(const int listid,
+                                     struct omap_dma_sglist_node *sghead,
+                                     struct omap_dma_channel_params *chparams);
+extern int omap_start_dma_sglist_transfers(const int listid,
+                                          const int pauseafter);
+extern int omap_resume_dma_sglist_transfers(const int listid,
+                                           const int pauseafter);
+extern int omap_release_dma_sglist(const int listid);
+int omap_get_completed_sglist_nodes(const int listid);
+int omap_dma_sglist_is_paused(const int listid);
+void omap_dma_set_sglist_fastmode(const int listid, const int fastmode);
+#endif
 /* LCD DMA functions */
 extern int omap_request_lcd_dma(void (*callback)(u16 status, void *data),
                                void *data);
---
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to