From patchwork Tue Oct 26 13:25:12 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: manjugk manjugk X-Patchwork-Id: 282552 X-Patchwork-Delegate: tony@atomide.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id o9QDPfTN025576 for ; Tue, 26 Oct 2010 13:25:42 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933242Ab0JZNZk (ORCPT ); Tue, 26 Oct 2010 09:25:40 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:51003 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933153Ab0JZNZk (ORCPT ); Tue, 26 Oct 2010 09:25:40 -0400 Received: from dlep33.itg.ti.com ([157.170.170.112]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id o9QDPX5f032509 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 26 Oct 2010 08:25:33 -0500 Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id o9QDPTtL017134; Tue, 26 Oct 2010 08:25:29 -0500 (CDT) Received: from localhost (glpp-machine.apr.dhcp.ti.com [172.24.137.105]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id o9QDPQf10024; Tue, 26 Oct 2010 08:25:27 -0500 (CDT) From: "G, Manjunath Kondaiah" To: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org, Venkatraman S , Benoit Cousson , Kevin Hilman , Santosh Shilimkar Subject: [PATCH v3 12/13] OMAP2+: DMA: descriptor autoloading feature Date: Tue, 26 Oct 2010 18:55:12 +0530 Message-Id: <1288099513-1854-13-git-send-email-manjugk@ti.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1288099513-1854-1-git-send-email-manjugk@ti.com> References: <1288099513-1854-1-git-send-email-manjugk@ti.com> Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Tue, 26 Oct 2010 13:25:42 +0000 (UTC) diff --git a/arch/arm/mach-omap2/dma.c b/arch/arm/mach-omap2/dma.c index 6df4835..397e90e 100644 --- a/arch/arm/mach-omap2/dma.c +++ b/arch/arm/mach-omap2/dma.c @@ -41,6 +41,7 @@ static u32 errata; static int dma_chan_count; +static int dma_caps0_status; static struct omap_dma_dev_attr *d; static struct dma_link_info *dma_linked_lch; @@ -240,6 +241,76 @@ static inline void omap2_disable_lnk(int lch) dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE; } +static inline void omap_dma_list_set_ntype(struct omap_dma_sglist_node *node, + int value) +{ + node->num_of_elem |= ((value) << 29); +} + +static void omap_set_dma_sglist_pausebit( + struct omap_dma_list_config_params *lcfg, int nelem, int set) +{ + struct omap_dma_sglist_node *sgn = lcfg->sghead; + + if (nelem > 0 && nelem < lcfg->num_elem) { + lcfg->pausenode = nelem; + sgn += nelem; + + if (set) + sgn->next_desc_add_ptr |= DMA_LIST_DESC_PAUSE; + else + sgn->next_desc_add_ptr &= ~(DMA_LIST_DESC_PAUSE); + } +} + +static int dma_sglist_set_phy_params(struct omap_dma_sglist_node *sghead, + dma_addr_t phyaddr, int nelem) +{ + struct omap_dma_sglist_node *sgcurr, *sgprev; + dma_addr_t elem_paddr = phyaddr; + + for (sgprev = sghead; + sgprev < sghead + nelem; + sgprev++) { + + sgcurr = sgprev + 1; + sgprev->next = sgcurr; + elem_paddr += (int)sizeof(*sgcurr); + sgprev->next_desc_add_ptr = elem_paddr; + + switch (sgcurr->desc_type) { + case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE1: + omap_dma_list_set_ntype(sgprev, 1); + break; + + case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2a: + /* intentional no break */ + case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE2b: + omap_dma_list_set_ntype(sgprev, 2); + break; + + case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3a: + /* intentional no break */ + case OMAP_DMA_SGLIST_DESCRIPTOR_TYPE3b: + omap_dma_list_set_ntype(sgprev, 3); + break; + + default: + return -EINVAL; + + } + if (sgcurr->flags & OMAP_DMA_LIST_SRC_VALID) + sgprev->num_of_elem |= DMA_LIST_DESC_SRC_VALID; + if (sgcurr->flags & OMAP_DMA_LIST_DST_VALID) + sgprev->num_of_elem |= DMA_LIST_DESC_DST_VALID; + if (sgcurr->flags & OMAP_DMA_LIST_NOTIFY_BLOCK_END) + sgprev->num_of_elem |= DMA_LIST_DESC_BLK_END; + } + sgprev--; + sgprev->next_desc_add_ptr = OMAP_DMA_INVALID_DESCRIPTOR_POINTER; + return 0; +} + static void omap2_set_color_mode(int lch, enum omap_dma_color_mode mode, u32 color) { @@ -1207,6 +1278,190 @@ int omap_get_dma_chain_src_pos(int chain_id) } EXPORT_SYMBOL(omap_get_dma_chain_src_pos); +int omap_set_dma_sglist_mode(int lch, struct omap_dma_sglist_node *sgparams, + dma_addr_t padd, int nelem, struct omap_dma_channel_params *chparams) +{ + struct omap_dma_list_config_params *lcfg; + int l = DMA_LIST_CDP_LISTMODE; /* Enable Linked list mode in CDP */ + + if ((dma_caps0_status & DMA_CAPS_SGLIST_SUPPORT) == 0) { + printk(KERN_ERR "omap DMA: sglist feature not supported\n"); + return -EPERM; + } + if (dma_chan[lch].flags & OMAP_DMA_ACTIVE) { + printk(KERN_ERR "omap DMA: configuring active DMA channel\n"); + return -EPERM; + } + + if (padd == 0) { + printk(KERN_ERR "omap DMA: sglist invalid dma_addr\n"); + return -EINVAL; + } + lcfg = &dma_chan[lch].list_config; + + lcfg->sghead = sgparams; + lcfg->num_elem = nelem; + lcfg->sgheadphy = padd; + lcfg->pausenode = -1; + + if (NULL == chparams) + l |= DMA_LIST_CDP_FASTMODE; + else + omap_set_dma_params(lch, chparams); + + dma_write(l, CDP, lch); + dma_write(0, CCDN, lch); /* Reset List index numbering */ + /* Initialize frame and element counters to invalid values */ + dma_write(OMAP_DMA_INVALID_FRAME_COUNT, CCFN, lch); + dma_write(OMAP_DMA_INVALID_ELEM_COUNT, CCEN, lch); + + return dma_sglist_set_phy_params(sgparams, lcfg->sgheadphy, nelem); + +} +EXPORT_SYMBOL(omap_set_dma_sglist_mode); + +void omap_clear_dma_sglist_mode(int lch) +{ + /* Clear entire CDP which is related to sglist handling */ + dma_write(0, CDP, lch); + dma_write(0, CCDN, lch); + /** + * Put back the original enabled irqs, which + * could have been overwritten by type 1 or type 2 + * descriptors + */ + dma_write(dma_chan[lch].enabled_irqs, CICR, lch); + return; +} +EXPORT_SYMBOL(omap_clear_dma_sglist_mode); + +int omap_start_dma_sglist_transfers(int lch, int pauseafter) +{ + struct omap_dma_list_config_params *lcfg; + struct omap_dma_sglist_node *sgn; + unsigned int l, type_id; + + lcfg = &dma_chan[lch].list_config; + sgn = lcfg->sghead; + + lcfg->pausenode = 0; + omap_set_dma_sglist_pausebit(lcfg, pauseafter, 1); + + /* 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, lch); + 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, lch); + dma_write((lcfg->sgheadphy), CNDP, lch); + /** + * Barrier needed as writes to the + * descriptor memory needs to be flushed + * before it's used by DMA controller + */ + wmb(); + omap_start_dma(lch); + + return 0; +} +EXPORT_SYMBOL(omap_start_dma_sglist_transfers); + +int omap_resume_dma_sglist_transfers(int lch, int pauseafter) +{ + struct omap_dma_list_config_params *lcfg; + struct omap_dma_sglist_node *sgn; + int l; + u32 sys_cf = 0; + + lcfg = &dma_chan[lch].list_config; + sgn = lcfg->sghead; + + /* Maintain the pause state in descriptor */ + omap_set_dma_sglist_pausebit(lcfg, lcfg->pausenode, 0); + omap_set_dma_sglist_pausebit(lcfg, pauseafter, 1); + + /** + * Barrier needed as writes to the + * descriptor memory needs to be flushed + * before it's used by DMA controller + */ + wmb(); + + /* Configure No-Standby */ + if (IS_DMA_ERRATA(DMA_ERRATA_i541)) + omap_device_mstandby(pd, &sys_cf, true); + + /* Clear pause bit in CDP */ + l = dma_read(CDP, lch); + l &= ~(DMA_LIST_CDP_PAUSEMODE); + dma_write(l, CDP, lch); + + omap_start_dma(lch); + + if (IS_DMA_ERRATA(DMA_ERRATA_i541)) + omap_device_mstandby(pd, &sys_cf, false); + + return 0; +} +EXPORT_SYMBOL(omap_resume_dma_sglist_transfers); + +void omap_release_dma_sglist(int lch) +{ + omap_clear_dma_sglist_mode(lch); + omap_free_dma(lch); + + return; +} +EXPORT_SYMBOL(omap_release_dma_sglist); + +int omap_get_completed_sglist_nodes(int lch) +{ + int list_count; + + list_count = dma_read(CCDN, lch); + return list_count & 0xffff; /* only 16 LSB bits are valid */ +} +EXPORT_SYMBOL(omap_get_completed_sglist_nodes); + +int omap_dma_sglist_is_paused(int lch) +{ + int list_state; + list_state = dma_read(CDP, lch); + return (list_state & DMA_LIST_CDP_PAUSEMODE) ? 1 : 0; +} +EXPORT_SYMBOL(omap_dma_sglist_is_paused); + +void omap_dma_set_sglist_fastmode(int lch, int fastmode) +{ + int l = dma_read(CDP, lch); + + if (fastmode) + l |= DMA_LIST_CDP_FASTMODE; + else + l &= ~(DMA_LIST_CDP_FASTMODE); + dma_write(l, CDP, lch); +} +EXPORT_SYMBOL(omap_dma_set_sglist_fastmode); + /* One time initializations */ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused) { @@ -1245,6 +1500,7 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused) p->disable_lnk = omap2_disable_lnk; p->set_global_params = omap_dma_set_global_params; + p->clear_dma_sglist_mode = omap_clear_dma_sglist_mode; p->clear_lch_regs = NULL; p->get_gdma_dev = NULL; @@ -1306,6 +1562,7 @@ static int __init omap2_system_dma_init_dev(struct omap_hwmod *oh, void *unused) return -ENOMEM; } dma_chan = d->chan; + dma_caps0_status = dma_read(CAPS_0, 0); return 0; } diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index eb00b22..41b14b0 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -450,6 +450,7 @@ void omap_free_dma(int lch) /* Make sure the DMA transfer is stopped. */ dma_write(0, CCR, lch); omap_clear_dma(lch); + p->clear_dma_sglist_mode(lch); } spin_lock_irqsave(&dma_chan_lock, flags); dma_chan[lch].dev_id = -1; diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h index 477f0d2..3e324b7 100644 --- a/arch/arm/plat-omap/include/plat/dma.h +++ b/arch/arm/plat-omap/include/plat/dma.h @@ -351,6 +351,113 @@ struct omap_dma_channel_params { #endif }; +/* CDP Register bitmaps */ +#define DMA_LIST_CDP_DST_VALID (BIT(0)) +#define DMA_LIST_CDP_SRC_VALID (BIT(2)) +#define DMA_LIST_CDP_TYPE1 (BIT(4)) +#define DMA_LIST_CDP_TYPE2 (BIT(5)) +#define DMA_LIST_CDP_TYPE3 (BIT(4) | BIT(5)) +#define DMA_LIST_CDP_PAUSEMODE (BIT(7)) +#define DMA_LIST_CDP_LISTMODE (BIT(8)) +#define DMA_LIST_CDP_FASTMODE (BIT(10)) +/* CAPS register bitmaps */ +#define DMA_CAPS_SGLIST_SUPPORT (BIT(20)) + +#define DMA_LIST_DESC_PAUSE (BIT(0)) +#define DMA_LIST_DESC_SRC_VALID (BIT(24)) +#define DMA_LIST_DESC_DST_VALID (BIT(26)) +#define DMA_LIST_DESC_BLK_END (BIT(28)) + +#define OMAP_DMA_INVALID_FRAME_COUNT (0xffff) +#define OMAP_DMA_INVALID_ELEM_COUNT (0xffffff) +#define OMAP_DMA_INVALID_DESCRIPTOR_POINTER (0xfffffffc) + +struct omap_dma_list_config_params { + unsigned int num_elem; + struct omap_dma_sglist_node *sghead; + dma_addr_t sgheadphy; + unsigned int pausenode; +}; + +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 */ + dma_addr_t next_desc_add_ptr; + u32 num_of_elem; + /* Type specific elements */ + union omap_dma_sglist_node_type sg_node; + /* Control fields */ + unsigned short flags; + /* Fields that can be set in flags variable */ + #define OMAP_DMA_LIST_SRC_VALID BIT(0) + #define OMAP_DMA_LIST_DST_VALID BIT(1) + #define OMAP_DMA_LIST_NOTIFY_BLOCK_END BIT(2) + enum omap_dma_sglist_descriptor_select desc_type; + struct omap_dma_sglist_node *next; +}; + + #include struct omap_dma_lch { int next_lch; @@ -367,6 +474,7 @@ struct omap_dma_lch { int state; int chain_id; int status; + struct omap_dma_list_config_params list_config; }; struct omap_dma_dev_attr { @@ -410,6 +518,7 @@ struct omap_system_dma_plat_info { void (*set_global_params)(int arb_rate, int max_fifo_depth, int tparams); + void (*clear_dma_sglist_mode)(int lch); }; extern void omap_set_dma_priority(int lch, int dst_port, int priority); @@ -472,7 +581,7 @@ void omap_dma_global_context_save(void); void omap_dma_global_context_restore(void); extern void omap_dma_disable_irq(int lch); - +void omap_clear_dma_sglist_mode(int lch); #if defined(CONFIG_ARCH_OMAP1) && defined(CONFIG_FB_OMAP) #include #else