2005-07-11 02:58:15 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/arm/plat-omap/dma.c
|
|
|
|
*
|
2008-07-03 17:24:37 +08:00
|
|
|
* Copyright (C) 2003 - 2008 Nokia Corporation
|
2007-10-20 05:21:04 +08:00
|
|
|
* Author: Juha Yrjölä <juha.yrjola@nokia.com>
|
2005-07-11 02:58:15 +08:00
|
|
|
* DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com>
|
|
|
|
* Graphics DMA and LCD DMA graphics tranformations
|
|
|
|
* by Imre Deak <imre.deak@nokia.com>
|
2007-12-02 04:14:11 +08:00
|
|
|
* OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc.
|
2005-11-10 22:26:50 +08:00
|
|
|
* Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com>
|
2005-07-11 02:58:15 +08:00
|
|
|
* Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc.
|
|
|
|
*
|
2009-05-29 05:16:04 +08:00
|
|
|
* Copyright (C) 2009 Texas Instruments
|
|
|
|
* Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
|
|
|
|
*
|
2005-07-11 02:58:15 +08:00
|
|
|
* Support functions for the OMAP internal DMA channels.
|
|
|
|
*
|
2010-12-21 10:27:19 +08:00
|
|
|
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
|
|
|
|
* Converted DMA library into DMA platform driver.
|
|
|
|
* - G, Manjunath Kondaiah <manjugk@ti.com>
|
|
|
|
*
|
2005-07-11 02:58:15 +08:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/interrupt.h>
|
2006-07-02 05:32:41 +08:00
|
|
|
#include <linux/irq.h>
|
2008-07-03 17:24:37 +08:00
|
|
|
#include <linux/io.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2010-10-12 05:18:56 +08:00
|
|
|
#include <linux/delay.h>
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
#include <asm/system.h>
|
2008-08-05 23:14:15 +08:00
|
|
|
#include <mach/hardware.h>
|
2009-10-21 00:40:47 +08:00
|
|
|
#include <plat/dma.h>
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2009-10-21 00:40:47 +08:00
|
|
|
#include <plat/tc.h>
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
#undef DEBUG
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_OMAP1
|
|
|
|
enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED,
|
|
|
|
DMA_CH_QUEUED, DMA_CH_NOTSTARTED, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED
|
|
|
|
};
|
|
|
|
|
|
|
|
enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED };
|
2005-11-10 22:26:50 +08:00
|
|
|
#endif
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
#define OMAP_DMA_ACTIVE 0x01
|
2010-11-24 19:23:21 +08:00
|
|
|
#define OMAP2_DMA_CSR_CLEAR_MASK 0xffffffff
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec)
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
static struct omap_system_dma_plat_info *p;
|
|
|
|
static struct omap_dma_dev_attr *d;
|
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
static int enable_1510_mode;
|
2010-12-21 10:27:18 +08:00
|
|
|
static u32 errata;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-08-28 21:13:31 +08:00
|
|
|
static struct omap_dma_global_context_registers {
|
|
|
|
u32 dma_irqenable_l0;
|
|
|
|
u32 dma_ocp_sysconfig;
|
|
|
|
u32 dma_gcr;
|
|
|
|
} omap_dma_global_context;
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
struct dma_link_info {
|
|
|
|
int *linked_dmach_q;
|
|
|
|
int no_of_lchs_linked;
|
|
|
|
|
|
|
|
int q_count;
|
|
|
|
int q_tail;
|
|
|
|
int q_head;
|
|
|
|
|
|
|
|
int chain_state;
|
|
|
|
int chain_mode;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2008-07-03 17:24:31 +08:00
|
|
|
static struct dma_link_info *dma_linked_lch;
|
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_OMAP1
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
/* Chain handling macros */
|
|
|
|
#define OMAP_DMA_CHAIN_QINIT(chain_id) \
|
|
|
|
do { \
|
|
|
|
dma_linked_lch[chain_id].q_head = \
|
|
|
|
dma_linked_lch[chain_id].q_tail = \
|
|
|
|
dma_linked_lch[chain_id].q_count = 0; \
|
|
|
|
} while (0)
|
|
|
|
#define OMAP_DMA_CHAIN_QFULL(chain_id) \
|
|
|
|
(dma_linked_lch[chain_id].no_of_lchs_linked == \
|
|
|
|
dma_linked_lch[chain_id].q_count)
|
|
|
|
#define OMAP_DMA_CHAIN_QLAST(chain_id) \
|
|
|
|
do { \
|
|
|
|
((dma_linked_lch[chain_id].no_of_lchs_linked-1) == \
|
|
|
|
dma_linked_lch[chain_id].q_count) \
|
|
|
|
} while (0)
|
|
|
|
#define OMAP_DMA_CHAIN_QEMPTY(chain_id) \
|
|
|
|
(0 == dma_linked_lch[chain_id].q_count)
|
|
|
|
#define __OMAP_DMA_CHAIN_INCQ(end) \
|
|
|
|
((end) = ((end)+1) % dma_linked_lch[chain_id].no_of_lchs_linked)
|
|
|
|
#define OMAP_DMA_CHAIN_INCQHEAD(chain_id) \
|
|
|
|
do { \
|
|
|
|
__OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_head); \
|
|
|
|
dma_linked_lch[chain_id].q_count--; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define OMAP_DMA_CHAIN_INCQTAIL(chain_id) \
|
|
|
|
do { \
|
|
|
|
__OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_tail); \
|
|
|
|
dma_linked_lch[chain_id].q_count++; \
|
|
|
|
} while (0)
|
|
|
|
#endif
|
2008-07-03 17:24:31 +08:00
|
|
|
|
|
|
|
static int dma_lch_count;
|
2005-07-11 02:58:15 +08:00
|
|
|
static int dma_chan_count;
|
2009-03-24 09:07:48 +08:00
|
|
|
static int omap_dma_reserve_channels;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
static spinlock_t dma_chan_lock;
|
2008-07-03 17:24:31 +08:00
|
|
|
static struct omap_dma_lch *dma_chan;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
static inline void disable_lnk(int lch);
|
|
|
|
static void omap_disable_channel_irq(int lch);
|
|
|
|
static inline void omap_enable_channel_irq(int lch);
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
#define REVISIT_24XX() printk(KERN_ERR "FIXME: no %s on 24xx\n", \
|
2008-03-05 07:08:02 +08:00
|
|
|
__func__);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_OMAP15XX
|
|
|
|
/* Returns 1 if the DMA module is in OMAP1510-compatible mode, 0 otherwise */
|
2011-01-28 08:39:43 +08:00
|
|
|
static int omap_dma_in_1510_mode(void)
|
2005-11-10 22:26:50 +08:00
|
|
|
{
|
|
|
|
return enable_1510_mode;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define omap_dma_in_1510_mode() 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_OMAP1
|
2005-07-11 02:58:15 +08:00
|
|
|
static inline int get_gdma_dev(int req)
|
|
|
|
{
|
|
|
|
u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
|
|
|
|
int shift = ((req - 1) % 5) * 6;
|
|
|
|
|
|
|
|
return ((omap_readl(reg) >> shift) & 0x3f) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void set_gdma_dev(int req, int dev)
|
|
|
|
{
|
|
|
|
u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
|
|
|
|
int shift = ((req - 1) % 5) * 6;
|
|
|
|
u32 l;
|
|
|
|
|
|
|
|
l = omap_readl(reg);
|
|
|
|
l &= ~(0x3f << shift);
|
|
|
|
l |= (dev - 1) << shift;
|
|
|
|
omap_writel(l, reg);
|
|
|
|
}
|
2005-11-10 22:26:50 +08:00
|
|
|
#else
|
|
|
|
#define set_gdma_dev(req, dev) do {} while (0)
|
|
|
|
#endif
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2006-09-25 17:45:45 +08:00
|
|
|
void omap_set_dma_priority(int lch, int dst_port, int priority)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
|
|
|
unsigned long reg;
|
|
|
|
u32 l;
|
|
|
|
|
2006-09-25 17:45:45 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
|
|
|
switch (dst_port) {
|
|
|
|
case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */
|
|
|
|
reg = OMAP_TC_OCPT1_PRIOR;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */
|
|
|
|
reg = OMAP_TC_OCPT2_PRIOR;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */
|
|
|
|
reg = OMAP_TC_EMIFF_PRIOR;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */
|
|
|
|
reg = OMAP_TC_EMIFS_PRIOR;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
l = omap_readl(reg);
|
|
|
|
l &= ~(0xf << 8);
|
|
|
|
l |= (priority & 0xf) << 8;
|
|
|
|
omap_writel(l, reg);
|
|
|
|
}
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 ccr;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
ccr = p->dma_read(CCR, lch);
|
2006-09-25 17:45:45 +08:00
|
|
|
if (priority)
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr |= (1 << 6);
|
2006-09-25 17:45:45 +08:00
|
|
|
else
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr &= ~(1 << 6);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(ccr, CCR, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_priority);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
|
2005-11-10 22:26:50 +08:00
|
|
|
int frame_count, int sync_mode,
|
|
|
|
int dma_trigger, int src_or_dst_synch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~0x03;
|
|
|
|
l |= data_type;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
2008-07-03 17:24:36 +08:00
|
|
|
u16 ccr;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
ccr = p->dma_read(CCR, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr &= ~(1 << 5);
|
2005-11-10 22:26:50 +08:00
|
|
|
if (sync_mode == OMAP_DMA_SYNC_FRAME)
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr |= 1 << 5;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(ccr, CCR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
ccr = p->dma_read(CCR2, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr &= ~(1 << 2);
|
2005-11-10 22:26:50 +08:00
|
|
|
if (sync_mode == OMAP_DMA_SYNC_BLOCK)
|
2008-07-03 17:24:36 +08:00
|
|
|
ccr |= 1 << 2;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(ccr, CCR2, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2() && dma_trigger) {
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 val;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
val = p->dma_read(CCR, lch);
|
2009-01-15 19:09:53 +08:00
|
|
|
|
|
|
|
/* DMA_SYNCHRO_CONTROL_UPPER depends on the channel number */
|
2010-08-02 19:21:40 +08:00
|
|
|
val &= ~((1 << 23) | (3 << 19) | 0x1f);
|
2009-01-15 19:09:53 +08:00
|
|
|
val |= (dma_trigger & ~0x1f) << 14;
|
|
|
|
val |= dma_trigger & 0x1f;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (sync_mode & OMAP_DMA_SYNC_FRAME)
|
|
|
|
val |= 1 << 5;
|
2006-06-27 07:16:06 +08:00
|
|
|
else
|
|
|
|
val &= ~(1 << 5);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (sync_mode & OMAP_DMA_SYNC_BLOCK)
|
|
|
|
val |= 1 << 18;
|
2006-06-27 07:16:06 +08:00
|
|
|
else
|
|
|
|
val &= ~(1 << 18);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-08-02 19:21:40 +08:00
|
|
|
if (src_or_dst_synch == OMAP_DMA_DST_SYNC_PREFETCH) {
|
|
|
|
val &= ~(1 << 24); /* dest synch */
|
|
|
|
val |= (1 << 23); /* Prefetch */
|
|
|
|
} else if (src_or_dst_synch) {
|
2005-11-10 22:26:50 +08:00
|
|
|
val |= 1 << 24; /* source synch */
|
2010-08-02 19:21:40 +08:00
|
|
|
} else {
|
2005-11-10 22:26:50 +08:00
|
|
|
val &= ~(1 << 24); /* dest synch */
|
2010-08-02 19:21:40 +08:00
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(val, CCR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(elem_count, CEN, lch);
|
|
|
|
p->dma_write(frame_count, CFN, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_transfer_params);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color)
|
|
|
|
{
|
|
|
|
BUG_ON(omap_dma_in_1510_mode());
|
|
|
|
|
2009-05-29 04:23:51 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
|
|
|
u16 w;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
w = p->dma_read(CCR2, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
w &= ~0x03;
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case OMAP_DMA_CONSTANT_FILL:
|
|
|
|
w |= 0x01;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_TRANSPARENT_COPY:
|
|
|
|
w |= 0x02;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_COLOR_DIS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(w, CCR2, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
w = p->dma_read(LCH_CTRL, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
w &= ~0x0f;
|
|
|
|
/* Default is channel type 2D */
|
|
|
|
if (mode) {
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(color, COLOR, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
w |= 1; /* Channel type G */
|
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(w, LCH_CTRL, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2009-05-29 04:23:51 +08:00
|
|
|
|
|
|
|
if (cpu_class_is_omap2()) {
|
|
|
|
u32 val;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
val = p->dma_read(CCR, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
val &= ~((1 << 17) | (1 << 16));
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
case OMAP_DMA_CONSTANT_FILL:
|
|
|
|
val |= 1 << 16;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_TRANSPARENT_COPY:
|
|
|
|
val |= 1 << 17;
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_COLOR_DIS:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(val, CCR, lch);
|
2009-05-29 04:23:51 +08:00
|
|
|
|
|
|
|
color &= 0xffffff;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(color, COLOR, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_color_mode);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2006-09-25 17:45:45 +08:00
|
|
|
void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode)
|
|
|
|
{
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 csdp;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
csdp = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
csdp &= ~(0x3 << 16);
|
|
|
|
csdp |= (mode << 16);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(csdp, CSDP, lch);
|
2006-09-25 17:45:45 +08:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_write_mode);
|
2006-09-25 17:45:45 +08:00
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode)
|
|
|
|
{
|
|
|
|
if (cpu_class_is_omap1() && !cpu_is_omap15xx()) {
|
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(LCH_CTRL, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~0x7;
|
|
|
|
l |= mode;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, LCH_CTRL, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_set_dma_channel_mode);
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/* Note that src_port is only for omap1 */
|
2005-07-11 02:58:15 +08:00
|
|
|
void omap_set_dma_src_params(int lch, int src_port, int src_amode,
|
2005-11-10 22:26:50 +08:00
|
|
|
unsigned long src_start,
|
|
|
|
int src_ei, int src_fi)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2008-07-03 17:24:37 +08:00
|
|
|
u32 l;
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
2008-07-03 17:24:36 +08:00
|
|
|
u16 w;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
w = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
w &= ~(0x1f << 2);
|
|
|
|
w |= src_port << 2;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(w, CSDP, lch);
|
2008-07-03 17:24:37 +08:00
|
|
|
}
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2008-07-03 17:24:37 +08:00
|
|
|
l &= ~(0x03 << 12);
|
|
|
|
l |= src_amode << 12;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(src_start, CSSA, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(src_ei, CSEI, lch);
|
|
|
|
p->dma_write(src_fi, CSFI, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_src_params);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
void omap_set_dma_params(int lch, struct omap_dma_channel_params *params)
|
2005-11-10 22:26:50 +08:00
|
|
|
{
|
|
|
|
omap_set_dma_transfer_params(lch, params->data_type,
|
|
|
|
params->elem_count, params->frame_count,
|
|
|
|
params->sync_mode, params->trigger,
|
|
|
|
params->src_or_dst_synch);
|
|
|
|
omap_set_dma_src_params(lch, params->src_port,
|
|
|
|
params->src_amode, params->src_start,
|
|
|
|
params->src_ei, params->src_fi);
|
|
|
|
|
|
|
|
omap_set_dma_dest_params(lch, params->dst_port,
|
|
|
|
params->dst_amode, params->dst_start,
|
|
|
|
params->dst_ei, params->dst_fi);
|
2007-12-02 04:14:11 +08:00
|
|
|
if (params->read_prio || params->write_prio)
|
|
|
|
omap_dma_set_prio_lch(lch, params->read_prio,
|
|
|
|
params->write_prio);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_params);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_src_index(int lch, int eidx, int fidx)
|
|
|
|
{
|
2008-07-03 17:24:37 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2005-11-10 22:26:50 +08:00
|
|
|
return;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(eidx, CSEI, lch);
|
|
|
|
p->dma_write(fidx, CSFI, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_src_index);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_src_data_pack(int lch, int enable)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(1 << 6);
|
2005-11-10 22:26:50 +08:00
|
|
|
if (enable)
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= (1 << 6);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_src_data_pack);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
|
|
|
|
{
|
2006-06-27 07:16:14 +08:00
|
|
|
unsigned int burst = 0;
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x03 << 7);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
switch (burst_mode) {
|
|
|
|
case OMAP_DMA_DATA_BURST_DIS:
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_DATA_BURST_4:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x1;
|
|
|
|
else
|
|
|
|
burst = 0x2;
|
2005-07-11 02:58:15 +08:00
|
|
|
break;
|
|
|
|
case OMAP_DMA_DATA_BURST_8:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x2;
|
|
|
|
break;
|
|
|
|
}
|
2010-05-15 03:05:25 +08:00
|
|
|
/*
|
|
|
|
* not supported by current hardware on OMAP1
|
2005-07-11 02:58:15 +08:00
|
|
|
* w |= (0x03 << 7);
|
|
|
|
* fall through
|
|
|
|
*/
|
2006-06-27 07:16:14 +08:00
|
|
|
case OMAP_DMA_DATA_BURST_16:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x3;
|
|
|
|
break;
|
|
|
|
}
|
2010-05-15 03:05:25 +08:00
|
|
|
/*
|
|
|
|
* OMAP1 don't support burst 16
|
2006-06-27 07:16:14 +08:00
|
|
|
* fall through
|
|
|
|
*/
|
2005-07-11 02:58:15 +08:00
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2008-07-03 17:24:36 +08:00
|
|
|
|
|
|
|
l |= (burst << 7);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_src_burst_mode);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/* Note that dest_port is only for OMAP1 */
|
2005-07-11 02:58:15 +08:00
|
|
|
void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
|
2005-11-10 22:26:50 +08:00
|
|
|
unsigned long dest_start,
|
|
|
|
int dst_ei, int dst_fi)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x1f << 9);
|
|
|
|
l |= dest_port << 9;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x03 << 14);
|
|
|
|
l |= dest_amode << 14;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dest_start, CDSA, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dst_ei, CDEI, lch);
|
|
|
|
p->dma_write(dst_fi, CDFI, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_dest_params);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_dest_index(int lch, int eidx, int fidx)
|
|
|
|
{
|
2008-07-03 17:24:37 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2005-11-10 22:26:50 +08:00
|
|
|
return;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(eidx, CDEI, lch);
|
|
|
|
p->dma_write(fidx, CDFI, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_dest_index);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_dest_data_pack(int lch, int enable)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(1 << 13);
|
2005-11-10 22:26:50 +08:00
|
|
|
if (enable)
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= 1 << 13;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_dest_data_pack);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
|
|
|
|
{
|
2006-06-27 07:16:14 +08:00
|
|
|
unsigned int burst = 0;
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CSDP, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x03 << 14);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
switch (burst_mode) {
|
|
|
|
case OMAP_DMA_DATA_BURST_DIS:
|
|
|
|
break;
|
|
|
|
case OMAP_DMA_DATA_BURST_4:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x1;
|
|
|
|
else
|
|
|
|
burst = 0x2;
|
2005-07-11 02:58:15 +08:00
|
|
|
break;
|
|
|
|
case OMAP_DMA_DATA_BURST_8:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x2;
|
|
|
|
else
|
|
|
|
burst = 0x3;
|
2005-07-11 02:58:15 +08:00
|
|
|
break;
|
2006-06-27 07:16:14 +08:00
|
|
|
case OMAP_DMA_DATA_BURST_16:
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2006-06-27 07:16:14 +08:00
|
|
|
burst = 0x3;
|
|
|
|
break;
|
|
|
|
}
|
2010-05-15 03:05:25 +08:00
|
|
|
/*
|
|
|
|
* OMAP1 don't support burst 16
|
2006-06-27 07:16:14 +08:00
|
|
|
* fall through
|
|
|
|
*/
|
2005-07-11 02:58:15 +08:00
|
|
|
default:
|
|
|
|
printk(KERN_ERR "Invalid DMA burst mode\n");
|
|
|
|
BUG();
|
|
|
|
return;
|
|
|
|
}
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= (burst << 14);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CSDP, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_dest_burst_mode);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
static inline void omap_enable_channel_irq(int lch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2005-11-10 22:26:50 +08:00
|
|
|
u32 status;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2006-06-27 07:16:15 +08:00
|
|
|
/* Clear CSR */
|
|
|
|
if (cpu_class_is_omap1())
|
2010-12-21 10:27:19 +08:00
|
|
|
status = p->dma_read(CSR, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
else if (cpu_class_is_omap2())
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
/* Enable some nice interrupts. */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dma_chan[lch].enabled_irqs, CICR, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
static void omap_disable_channel_irq(int lch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CICR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void omap_enable_dma_irq(int lch, u16 bits)
|
|
|
|
{
|
|
|
|
dma_chan[lch].enabled_irqs |= bits;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_enable_dma_irq);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
void omap_disable_dma_irq(int lch, u16 bits)
|
|
|
|
{
|
|
|
|
dma_chan[lch].enabled_irqs &= ~bits;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_disable_dma_irq);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
static inline void enable_lnk(int lch)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CLNK_CTRL, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1())
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(1 << 14);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/* Set the ENABLE_LNK bits */
|
2005-07-11 02:58:15 +08:00
|
|
|
if (dma_chan[lch].next_lch != -1)
|
2008-07-03 17:24:36 +08:00
|
|
|
l = dma_chan[lch].next_lch | (1 << 15);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
#ifndef CONFIG_ARCH_OMAP1
|
2008-07-03 17:24:37 +08:00
|
|
|
if (cpu_class_is_omap2())
|
|
|
|
if (dma_chan[lch].next_linked_ch != -1)
|
|
|
|
l = dma_chan[lch].next_linked_ch | (1 << 15);
|
2007-12-02 04:14:11 +08:00
|
|
|
#endif
|
2008-07-03 17:24:36 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CLNK_CTRL, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void disable_lnk(int lch)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CLNK_CTRL, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
/* Disable interrupts */
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CICR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
/* Set the STOP_LNK bit */
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= 1 << 14;
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_disable_channel_irq(lch);
|
|
|
|
/* Clear the ENABLE_LNK bit */
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(1 << 15);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CLNK_CTRL, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
|
|
|
|
}
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
static inline void omap2_enable_irq_lch(int lch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2005-11-10 22:26:50 +08:00
|
|
|
u32 val;
|
2009-11-11 10:55:17 +08:00
|
|
|
unsigned long flags;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (!cpu_class_is_omap2())
|
2005-11-10 22:26:50 +08:00
|
|
|
return;
|
|
|
|
|
2009-11-11 10:55:17 +08:00
|
|
|
spin_lock_irqsave(&dma_chan_lock, flags);
|
2010-12-21 10:27:19 +08:00
|
|
|
val = p->dma_read(IRQENABLE_L0, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
val |= 1 << lch;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(val, IRQENABLE_L0, lch);
|
2009-11-11 10:55:17 +08:00
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
2010-05-15 03:05:25 +08:00
|
|
|
static inline void omap2_disable_irq_lch(int lch)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (!cpu_class_is_omap2())
|
|
|
|
return;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dma_chan_lock, flags);
|
2010-12-21 10:27:19 +08:00
|
|
|
val = p->dma_read(IRQENABLE_L0, lch);
|
2010-05-15 03:05:25 +08:00
|
|
|
val &= ~(1 << lch);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(val, IRQENABLE_L0, lch);
|
2010-05-15 03:05:25 +08:00
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
|
|
|
}
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
int omap_request_dma(int dev_id, const char *dev_name,
|
2008-07-03 17:24:37 +08:00
|
|
|
void (*callback)(int lch, u16 ch_status, void *data),
|
2005-11-10 22:26:50 +08:00
|
|
|
void *data, int *dma_ch_out)
|
|
|
|
{
|
|
|
|
int ch, free_ch = -1;
|
|
|
|
unsigned long flags;
|
|
|
|
struct omap_dma_lch *chan;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dma_chan_lock, flags);
|
|
|
|
for (ch = 0; ch < dma_chan_count; ch++) {
|
|
|
|
if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
|
|
|
|
free_ch = ch;
|
|
|
|
if (dev_id == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (free_ch == -1) {
|
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
chan = dma_chan + free_ch;
|
|
|
|
chan->dev_id = dev_id;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
if (p->clear_lch_regs)
|
|
|
|
p->clear_lch_regs(free_ch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2())
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_clear_dma(free_ch);
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
|
|
|
|
|
|
|
chan->dev_name = dev_name;
|
|
|
|
chan->callback = callback;
|
|
|
|
chan->data = data;
|
2009-01-30 00:57:12 +08:00
|
|
|
chan->flags = 0;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
#ifndef CONFIG_ARCH_OMAP1
|
2008-07-03 17:24:37 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
|
|
|
chan->chain_id = -1;
|
|
|
|
chan->next_linked_ch = -1;
|
|
|
|
}
|
2007-12-02 04:14:11 +08:00
|
|
|
#endif
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2006-06-27 07:16:15 +08:00
|
|
|
chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2006-06-27 07:16:15 +08:00
|
|
|
if (cpu_class_is_omap1())
|
|
|
|
chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ;
|
2007-12-02 04:14:11 +08:00
|
|
|
else if (cpu_class_is_omap2())
|
2006-06-27 07:16:15 +08:00
|
|
|
chan->enabled_irqs |= OMAP2_DMA_MISALIGNED_ERR_IRQ |
|
|
|
|
OMAP2_DMA_TRANS_ERR_IRQ;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
if (cpu_is_omap16xx()) {
|
|
|
|
/* If the sync device is set, configure it dynamically. */
|
|
|
|
if (dev_id != 0) {
|
|
|
|
set_gdma_dev(free_ch + 1, dev_id);
|
|
|
|
dev_id = free_ch + 1;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* Disable the 1510 compatibility mode and set the sync device
|
|
|
|
* id.
|
|
|
|
*/
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dev_id | (1 << 10), CCR, free_ch);
|
2009-03-24 09:07:44 +08:00
|
|
|
} else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dev_id, CCR, free_ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2005-11-10 22:26:50 +08:00
|
|
|
omap2_enable_irq_lch(free_ch);
|
|
|
|
omap_enable_channel_irq(free_ch);
|
|
|
|
/* Clear the CSR register and IRQ status register */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, free_ch);
|
|
|
|
p->dma_write(1 << free_ch, IRQSTATUS_L0, 0);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
*dma_ch_out = free_ch;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_request_dma);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
void omap_free_dma(int lch)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (dma_chan[lch].dev_id == -1) {
|
2008-07-03 17:24:37 +08:00
|
|
|
pr_err("omap_dma: trying to free unallocated DMA channel %d\n",
|
2005-11-10 22:26:50 +08:00
|
|
|
lch);
|
|
|
|
return;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
|
|
|
/* Disable all DMA interrupts for the channel. */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CICR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
/* Make sure the DMA transfer is stopped. */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CCR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2010-05-15 03:05:25 +08:00
|
|
|
omap2_disable_irq_lch(lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
/* Clear the CSR register and IRQ status register */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
|
|
|
|
p->dma_write(1 << lch, IRQSTATUS_L0, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
/* Disable all DMA interrupts for the channel. */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CICR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
/* Make sure the DMA transfer is stopped. */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CCR, lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_clear_dma(lch);
|
|
|
|
}
|
2009-04-24 02:10:40 +08:00
|
|
|
|
|
|
|
spin_lock_irqsave(&dma_chan_lock, flags);
|
|
|
|
dma_chan[lch].dev_id = -1;
|
|
|
|
dma_chan[lch].next_lch = -1;
|
|
|
|
dma_chan[lch].callback = NULL;
|
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_free_dma);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
/**
|
|
|
|
* @brief omap_dma_set_global_params : Set global priority settings for dma
|
|
|
|
*
|
|
|
|
* @param arb_rate
|
|
|
|
* @param max_fifo_depth
|
2009-10-15 00:56:34 +08:00
|
|
|
* @param tparams - Number of threads to reserve : DMA_THREAD_RESERVE_NORM
|
|
|
|
* DMA_THREAD_RESERVE_ONET
|
|
|
|
* DMA_THREAD_RESERVE_TWOT
|
|
|
|
* DMA_THREAD_RESERVE_THREET
|
2007-12-02 04:14:11 +08:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams)
|
|
|
|
{
|
|
|
|
u32 reg;
|
|
|
|
|
|
|
|
if (!cpu_class_is_omap2()) {
|
2008-03-05 07:08:02 +08:00
|
|
|
printk(KERN_ERR "FIXME: no %s on 15xx/16xx\n", __func__);
|
2007-12-02 04:14:11 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-10-15 00:56:34 +08:00
|
|
|
if (max_fifo_depth == 0)
|
|
|
|
max_fifo_depth = 1;
|
2007-12-02 04:14:11 +08:00
|
|
|
if (arb_rate == 0)
|
|
|
|
arb_rate = 1;
|
|
|
|
|
2009-10-15 00:56:34 +08:00
|
|
|
reg = 0xff & max_fifo_depth;
|
|
|
|
reg |= (0x3 & tparams) << 12;
|
|
|
|
reg |= (arb_rate & 0xff) << 16;
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(reg, GCR, 0);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dma_set_global_params);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief omap_dma_set_prio_lch : Set channel wise priority settings
|
|
|
|
*
|
|
|
|
* @param lch
|
|
|
|
* @param read_prio - Read priority
|
|
|
|
* @param write_prio - Write priority
|
|
|
|
* Both of the above can be set with one of the following values :
|
|
|
|
* DMA_CH_PRIO_HIGH/DMA_CH_PRIO_LOW
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
omap_dma_set_prio_lch(int lch, unsigned char read_prio,
|
|
|
|
unsigned char write_prio)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((lch < 0 || lch >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
printk(KERN_ERR "Invalid channel id\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~((1 << 6) | (1 << 26));
|
2009-05-29 05:16:04 +08:00
|
|
|
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx())
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= ((read_prio & 0x1) << 6) | ((write_prio & 0x1) << 26);
|
2007-12-02 04:14:11 +08:00
|
|
|
else
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= ((read_prio & 0x1) << 6);
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dma_set_prio_lch);
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/*
|
|
|
|
* Clears any DMA state so the DMA engine is ready to restart with new buffers
|
|
|
|
* through omap_start_dma(). Any buffers in flight are discarded.
|
|
|
|
*/
|
|
|
|
void omap_clear_dma(int lch)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
local_irq_save(flags);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->clear_dma(lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
local_irq_restore(flags);
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_clear_dma);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
void omap_start_dma(int lch)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2010-03-04 15:11:56 +08:00
|
|
|
/*
|
|
|
|
* The CPC/CDAC register needs to be initialized to zero
|
|
|
|
* before starting dma transfer.
|
|
|
|
*/
|
|
|
|
if (cpu_is_omap15xx())
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CPC, lch);
|
2010-03-04 15:11:56 +08:00
|
|
|
else
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CDAC, lch);
|
2010-03-04 15:11:56 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
|
|
|
|
int next_lch, cur_lch;
|
2010-12-21 10:27:19 +08:00
|
|
|
char dma_chan_link_map[dma_lch_count];
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
dma_chan_link_map[lch] = 1;
|
|
|
|
/* Set the link register of the first channel */
|
|
|
|
enable_lnk(lch);
|
|
|
|
|
|
|
|
memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
|
|
|
|
cur_lch = dma_chan[lch].next_lch;
|
|
|
|
do {
|
|
|
|
next_lch = dma_chan[cur_lch].next_lch;
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/* The loop case: we've been here already */
|
2005-07-11 02:58:15 +08:00
|
|
|
if (dma_chan_link_map[cur_lch])
|
|
|
|
break;
|
|
|
|
/* Mark the current channel */
|
|
|
|
dma_chan_link_map[cur_lch] = 1;
|
|
|
|
|
|
|
|
enable_lnk(cur_lch);
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_enable_channel_irq(cur_lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
cur_lch = next_lch;
|
|
|
|
} while (next_lch != -1);
|
2010-12-21 10:27:18 +08:00
|
|
|
} else if (IS_DMA_ERRATA(DMA_ERRATA_PARALLEL_CHANNELS))
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(lch, CLNK_CTRL, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_enable_channel_irq(lch);
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_IFRAME_BUFFERING))
|
|
|
|
l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= OMAP_DMA_CCR_EN;
|
2010-12-21 10:27:18 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_start_dma);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
void omap_stop_dma(int lch)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
|
|
|
|
2009-10-23 05:46:31 +08:00
|
|
|
/* Disable all interrupts on the channel */
|
|
|
|
if (cpu_class_is_omap1())
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0, CICR, lch);
|
2009-10-23 05:46:31 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_i541) &&
|
|
|
|
(l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) {
|
2010-10-12 05:18:56 +08:00
|
|
|
int i = 0;
|
|
|
|
u32 sys_cf;
|
|
|
|
|
|
|
|
/* Configure No-Standby */
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(OCP_SYSCONFIG, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
sys_cf = l;
|
|
|
|
l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
|
|
|
|
l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l , OCP_SYSCONFIG, 0);
|
2010-10-12 05:18:56 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
l &= ~OMAP_DMA_CCR_EN;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
|
|
|
|
/* Wait for sDMA FIFO drain */
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
while (i < 100 && (l & (OMAP_DMA_CCR_RD_ACTIVE |
|
|
|
|
OMAP_DMA_CCR_WR_ACTIVE))) {
|
|
|
|
udelay(5);
|
|
|
|
i++;
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
}
|
|
|
|
if (i >= 100)
|
|
|
|
printk(KERN_ERR "DMA drain did not complete on "
|
|
|
|
"lch %d\n", lch);
|
|
|
|
/* Restore OCP_SYSCONFIG */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(sys_cf, OCP_SYSCONFIG, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
} else {
|
|
|
|
l &= ~OMAP_DMA_CCR_EN;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2010-10-12 05:18:56 +08:00
|
|
|
}
|
2009-10-23 05:46:31 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
|
|
|
|
int next_lch, cur_lch = lch;
|
2010-12-21 10:27:19 +08:00
|
|
|
char dma_chan_link_map[dma_lch_count];
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
|
|
|
|
do {
|
|
|
|
/* The loop case: we've been here already */
|
|
|
|
if (dma_chan_link_map[cur_lch])
|
|
|
|
break;
|
|
|
|
/* Mark the current channel */
|
|
|
|
dma_chan_link_map[cur_lch] = 1;
|
|
|
|
|
|
|
|
disable_lnk(cur_lch);
|
|
|
|
|
|
|
|
next_lch = dma_chan[cur_lch].next_lch;
|
|
|
|
cur_lch = next_lch;
|
|
|
|
} while (next_lch != -1);
|
|
|
|
}
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_stop_dma);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2006-09-25 17:45:45 +08:00
|
|
|
/*
|
|
|
|
* Allows changing the DMA callback function or data. This may be needed if
|
|
|
|
* the driver shares a single DMA channel for multiple dma triggers.
|
|
|
|
*/
|
|
|
|
int omap_set_dma_callback(int lch,
|
2008-07-03 17:24:37 +08:00
|
|
|
void (*callback)(int lch, u16 ch_status, void *data),
|
2006-09-25 17:45:45 +08:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (lch < 0)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&dma_chan_lock, flags);
|
|
|
|
if (dma_chan[lch].dev_id == -1) {
|
|
|
|
printk(KERN_ERR "DMA callback for not set for free channel\n");
|
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
dma_chan[lch].callback = callback;
|
|
|
|
dma_chan[lch].data = data;
|
|
|
|
spin_unlock_irqrestore(&dma_chan_lock, flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_set_dma_callback);
|
2006-09-25 17:45:45 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/*
|
|
|
|
* Returns current physical source address for the given DMA channel.
|
|
|
|
* If the channel is running the caller must disable interrupts prior calling
|
|
|
|
* this function and process the returned value before re-enabling interrupt to
|
|
|
|
* prevent races with the interrupt handler. Note that in continuous mode there
|
2011-03-31 09:57:33 +08:00
|
|
|
* is a chance for CSSA_L register overflow between the two reads resulting
|
2005-11-10 22:26:50 +08:00
|
|
|
* in incorrect return value.
|
|
|
|
*/
|
|
|
|
dma_addr_t omap_get_dma_src_pos(int lch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2007-05-08 09:24:14 +08:00
|
|
|
dma_addr_t offset = 0;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
if (cpu_is_omap15xx())
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CPC, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
else
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CSAC, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_3_3) && offset == 0)
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CSAC, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
|
|
|
if (cpu_class_is_omap1())
|
2010-12-21 10:27:19 +08:00
|
|
|
offset |= (p->dma_read(CSSA, lch) & 0xFFFF0000);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
return offset;
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_get_dma_src_pos);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/*
|
|
|
|
* Returns current physical destination address for the given DMA channel.
|
|
|
|
* If the channel is running the caller must disable interrupts prior calling
|
|
|
|
* this function and process the returned value before re-enabling interrupt to
|
|
|
|
* prevent races with the interrupt handler. Note that in continuous mode there
|
2011-03-31 09:57:33 +08:00
|
|
|
* is a chance for CDSA_L register overflow between the two reads resulting
|
2005-11-10 22:26:50 +08:00
|
|
|
* in incorrect return value.
|
|
|
|
*/
|
|
|
|
dma_addr_t omap_get_dma_dst_pos(int lch)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2007-05-08 09:24:14 +08:00
|
|
|
dma_addr_t offset = 0;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
if (cpu_is_omap15xx())
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CPC, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
else
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CDAC, lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
/*
|
|
|
|
* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
|
|
|
|
* read before the DMA controller finished disabling the channel.
|
|
|
|
*/
|
|
|
|
if (!cpu_is_omap15xx() && offset == 0)
|
2010-12-21 10:27:19 +08:00
|
|
|
offset = p->dma_read(CDAC, lch);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
|
|
|
if (cpu_class_is_omap1())
|
2010-12-21 10:27:19 +08:00
|
|
|
offset |= (p->dma_read(CDSA, lch) & 0xFFFF0000);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
return offset;
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_get_dma_dst_pos);
|
2008-07-03 17:24:36 +08:00
|
|
|
|
|
|
|
int omap_get_dma_active_status(int lch)
|
|
|
|
{
|
2010-12-21 10:27:19 +08:00
|
|
|
return (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) != 0;
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
2008-07-03 17:24:36 +08:00
|
|
|
EXPORT_SYMBOL(omap_get_dma_active_status);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
int omap_dma_running(void)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
2005-11-10 22:26:50 +08:00
|
|
|
int lch;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2009-12-12 08:16:33 +08:00
|
|
|
if (cpu_class_is_omap1())
|
|
|
|
if (omap_lcd_dma_running())
|
2005-11-10 22:26:50 +08:00
|
|
|
return 1;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
for (lch = 0; lch < dma_chan_count; lch++)
|
2010-12-21 10:27:19 +08:00
|
|
|
if (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN)
|
2005-11-10 22:26:50 +08:00
|
|
|
return 1;
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
return 0;
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* lch_queue DMA will start right after lch_head one is finished.
|
|
|
|
* For this DMA link to start, you still need to start (see omap_start_dma)
|
|
|
|
* the first one. That will fire up the entire queue.
|
|
|
|
*/
|
2008-07-03 17:24:37 +08:00
|
|
|
void omap_dma_link_lch(int lch_head, int lch_queue)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
|
|
|
if (omap_dma_in_1510_mode()) {
|
2009-08-23 23:56:12 +08:00
|
|
|
if (lch_head == lch_queue) {
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(p->dma_read(CCR, lch_head) | (3 << 8),
|
2010-12-21 10:27:17 +08:00
|
|
|
CCR, lch_head);
|
2009-08-23 23:56:12 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-07-11 02:58:15 +08:00
|
|
|
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
|
|
|
|
BUG();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dma_chan[lch_head].dev_id == -1) ||
|
|
|
|
(dma_chan[lch_queue].dev_id == -1)) {
|
2005-11-10 22:26:50 +08:00
|
|
|
printk(KERN_ERR "omap_dma: trying to link "
|
|
|
|
"non requested channels\n");
|
2005-07-11 02:58:15 +08:00
|
|
|
dump_stack();
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_chan[lch_head].next_lch = lch_queue;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_dma_link_lch);
|
2005-07-11 02:58:15 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Once the DMA queue is stopped, we can destroy it.
|
|
|
|
*/
|
2008-07-03 17:24:37 +08:00
|
|
|
void omap_dma_unlink_lch(int lch_head, int lch_queue)
|
2005-07-11 02:58:15 +08:00
|
|
|
{
|
|
|
|
if (omap_dma_in_1510_mode()) {
|
2009-08-23 23:56:12 +08:00
|
|
|
if (lch_head == lch_queue) {
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(p->dma_read(CCR, lch_head) & ~(3 << 8),
|
2010-12-21 10:27:17 +08:00
|
|
|
CCR, lch_head);
|
2009-08-23 23:56:12 +08:00
|
|
|
return;
|
|
|
|
}
|
2005-07-11 02:58:15 +08:00
|
|
|
printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
|
|
|
|
BUG();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dma_chan[lch_head].next_lch != lch_queue ||
|
|
|
|
dma_chan[lch_head].next_lch == -1) {
|
2005-11-10 22:26:50 +08:00
|
|
|
printk(KERN_ERR "omap_dma: trying to unlink "
|
|
|
|
"non linked channels\n");
|
2005-07-11 02:58:15 +08:00
|
|
|
dump_stack();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) ||
|
2010-01-14 10:10:29 +08:00
|
|
|
(dma_chan[lch_queue].flags & OMAP_DMA_ACTIVE)) {
|
2005-11-10 22:26:50 +08:00
|
|
|
printk(KERN_ERR "omap_dma: You need to stop the DMA channels "
|
|
|
|
"before unlinking\n");
|
2005-07-11 02:58:15 +08:00
|
|
|
dump_stack();
|
|
|
|
}
|
|
|
|
|
|
|
|
dma_chan[lch_head].next_lch = -1;
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
EXPORT_SYMBOL(omap_dma_unlink_lch);
|
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
#ifndef CONFIG_ARCH_OMAP1
|
|
|
|
/* Create chain of DMA channesls */
|
|
|
|
static void create_dma_lch_chain(int lch_head, int lch_queue)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l;
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
/* Check if this is the first link in chain */
|
|
|
|
if (dma_chan[lch_head].next_linked_ch == -1) {
|
|
|
|
dma_chan[lch_head].next_linked_ch = lch_queue;
|
|
|
|
dma_chan[lch_head].prev_linked_ch = lch_queue;
|
|
|
|
dma_chan[lch_queue].next_linked_ch = lch_head;
|
|
|
|
dma_chan[lch_queue].prev_linked_ch = lch_head;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* a link exists, link the new channel in circular chain */
|
|
|
|
else {
|
|
|
|
dma_chan[lch_queue].next_linked_ch =
|
|
|
|
dma_chan[lch_head].next_linked_ch;
|
|
|
|
dma_chan[lch_queue].prev_linked_ch = lch_head;
|
|
|
|
dma_chan[lch_head].next_linked_ch = lch_queue;
|
|
|
|
dma_chan[dma_chan[lch_queue].next_linked_ch].prev_linked_ch =
|
|
|
|
lch_queue;
|
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CLNK_CTRL, lch_head);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x1f);
|
|
|
|
l |= lch_queue;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CLNK_CTRL, lch_head);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CLNK_CTRL, lch_queue);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(0x1f);
|
|
|
|
l |= (dma_chan[lch_queue].next_linked_ch);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CLNK_CTRL, lch_queue);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*
|
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 23:09:05 +08:00
|
|
|
* @return - Success : 0
|
2007-12-02 04:14:11 +08:00
|
|
|
* Failure: -EINVAL/-ENOMEM
|
|
|
|
*/
|
|
|
|
int omap_request_dma_chain(int dev_id, const char *dev_name,
|
2009-05-29 04:23:52 +08:00
|
|
|
void (*callback) (int lch, u16 ch_status,
|
2007-12-02 04:14:11 +08:00
|
|
|
void *data),
|
|
|
|
int *chain_id, int no_of_chans, int chain_mode,
|
|
|
|
struct omap_dma_channel_params params)
|
|
|
|
{
|
|
|
|
int *channels;
|
|
|
|
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
|
2008-07-03 17:24:31 +08:00
|
|
|
|| no_of_chans > dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
printk(KERN_ERR "Invalid Number of channels requested\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2010-05-15 03:05:25 +08:00
|
|
|
/*
|
|
|
|
* Allocate a queue to maintain the status of the channels
|
|
|
|
* in the chain
|
|
|
|
*/
|
2007-12-02 04:14:11 +08:00
|
|
|
channels = kmalloc(sizeof(*channels) * 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,
|
2008-09-05 22:10:27 +08:00
|
|
|
callback, NULL, &channels[i]);
|
2007-12-02 04:14:11 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
omap_set_dma_params(channels[i], ¶ms);
|
|
|
|
}
|
|
|
|
|
|
|
|
*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]);
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_request_dma_chain);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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 - Chained logical channel id.
|
|
|
|
* @param params
|
|
|
|
*
|
|
|
|
* @return - Success : 0
|
|
|
|
* Failure : -EINVAL
|
|
|
|
*/
|
|
|
|
int omap_modify_dma_chain_params(int chain_id,
|
|
|
|
struct omap_dma_channel_params params)
|
|
|
|
{
|
|
|
|
int *channels;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
/* Check for input params */
|
|
|
|
if (unlikely((chain_id < 0
|
2008-07-03 17:24:31 +08:00
|
|
|
|| chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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
|
|
|
|
*/
|
|
|
|
omap_set_dma_params(channels[i], ¶ms);
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_modify_dma_chain_params);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief omap_free_dma_chain - Free all the logical channels in a chain.
|
|
|
|
*
|
|
|
|
* @param chain_id
|
|
|
|
*
|
|
|
|
* @return - Success : 0
|
|
|
|
* Failure : -EINVAL
|
|
|
|
*/
|
|
|
|
int omap_free_dma_chain(int chain_id)
|
|
|
|
{
|
|
|
|
int *channels;
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_free_dma_chain);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief omap_dma_chain_status - Check if the chain is in
|
|
|
|
* active / inactive state.
|
|
|
|
* @param chain_id
|
|
|
|
*
|
|
|
|
* @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE
|
|
|
|
* Failure : -EINVAL
|
|
|
|
*/
|
|
|
|
int omap_dma_chain_status(int chain_id)
|
|
|
|
{
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
pr_debug("CHAINID=%d, qcnt=%d\n", chain_id,
|
|
|
|
dma_linked_lch[chain_id].q_count);
|
|
|
|
|
|
|
|
if (OMAP_DMA_CHAIN_QEMPTY(chain_id))
|
|
|
|
return OMAP_DMA_CHAIN_INACTIVE;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return OMAP_DMA_CHAIN_ACTIVE;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dma_chain_status);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
*
|
2008-03-11 03:40:35 +08:00
|
|
|
* @return - Success : 0
|
2007-12-02 04:14:11 +08:00
|
|
|
* 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;
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l, lch;
|
2007-12-02 04:14:11 +08:00
|
|
|
int start_dma = 0;
|
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* if buffer size is less than 1 then there is
|
|
|
|
* no use of starting the chain
|
|
|
|
*/
|
2007-12-02 04:14:11 +08:00
|
|
|
if (elem_count < 1) {
|
|
|
|
printk(KERN_ERR "Invalid buffer size\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check for input params */
|
|
|
|
if (unlikely((chain_id < 0
|
2008-07-03 17:24:31 +08:00
|
|
|
|| chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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 exist\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 */
|
|
|
|
OMAP_DMA_CHAIN_INCQTAIL(chain_id);
|
|
|
|
|
|
|
|
/* Set the params to the free channel */
|
|
|
|
if (src_start != 0)
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(src_start, CSSA, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
if (dest_start != 0)
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(dest_start, CDSA, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
/* Write the buffer size */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(elem_count, CEN, lch);
|
|
|
|
p->dma_write(frame_count, CFN, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* If the chain is dynamically linked,
|
|
|
|
* then we may have to start the chain if its not active
|
|
|
|
*/
|
2007-12-02 04:14:11 +08:00
|
|
|
if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) {
|
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* In Dynamic chain, if the chain is not started,
|
|
|
|
* queue the channel
|
|
|
|
*/
|
2007-12-02 04:14:11 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* Chain is already started, make sure its active,
|
|
|
|
* if not then start the chain
|
|
|
|
*/
|
2007-12-02 04:14:11 +08:00
|
|
|
else {
|
|
|
|
start_dma = 1;
|
|
|
|
|
|
|
|
if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
|
|
|
|
DMA_CH_STARTED) {
|
|
|
|
enable_lnk(dma_chan[lch].prev_linked_ch);
|
|
|
|
dma_chan[lch].state = DMA_CH_QUEUED;
|
|
|
|
start_dma = 0;
|
2010-12-21 10:27:19 +08:00
|
|
|
if (0 == ((1 << 7) & p->dma_read(
|
2010-12-21 10:27:17 +08:00
|
|
|
CCR, dma_chan[lch].prev_linked_ch))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
disable_lnk(dma_chan[lch].
|
|
|
|
prev_linked_ch);
|
|
|
|
pr_debug("\n prev ch is stopped\n");
|
|
|
|
start_dma = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
omap_enable_channel_irq(lch);
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
if ((0 == (l & (1 << 24))))
|
|
|
|
l &= ~(1 << 25);
|
2007-12-02 04:14:11 +08:00
|
|
|
else
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= (1 << 25);
|
2007-12-02 04:14:11 +08:00
|
|
|
if (start_dma == 1) {
|
2008-07-03 17:24:36 +08:00
|
|
|
if (0 == (l & (1 << 7))) {
|
|
|
|
l |= (1 << 7);
|
2007-12-02 04:14:11 +08:00
|
|
|
dma_chan[lch].state = DMA_CH_STARTED;
|
|
|
|
pr_debug("starting %d\n", lch);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
} else
|
|
|
|
start_dma = 0;
|
|
|
|
} else {
|
2008-07-03 17:24:36 +08:00
|
|
|
if (0 == (l & (1 << 7)))
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2008-03-11 03:40:35 +08:00
|
|
|
return 0;
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_dma_chain_a_transfer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l, i;
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
printk(KERN_ERR "Invalid chain id\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
channels = dma_linked_lch[chain_id].linked_dmach_q;
|
|
|
|
|
|
|
|
if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) {
|
|
|
|
printk(KERN_ERR "Chain is already started\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) {
|
|
|
|
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked;
|
|
|
|
i++) {
|
|
|
|
enable_lnk(channels[i]);
|
|
|
|
omap_enable_channel_irq(channels[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
omap_enable_channel_irq(channels[0]);
|
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, channels[0]);
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= (1 << 7);
|
2007-12-02 04:14:11 +08:00
|
|
|
dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED;
|
|
|
|
dma_chan[channels[0]].state = DMA_CH_STARTED;
|
|
|
|
|
2008-07-03 17:24:36 +08:00
|
|
|
if ((0 == (l & (1 << 24))))
|
|
|
|
l &= ~(1 << 25);
|
2007-12-02 04:14:11 +08:00
|
|
|
else
|
2008-07-03 17:24:36 +08:00
|
|
|
l |= (1 << 25);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, channels[0]);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE;
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_start_dma_chain_transfers);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 l, i;
|
2010-12-21 10:27:18 +08:00
|
|
|
u32 sys_cf = 0;
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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;
|
|
|
|
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_i88)) {
|
2010-12-21 10:27:19 +08:00
|
|
|
sys_cf = p->dma_read(OCP_SYSCONFIG, 0);
|
2010-12-21 10:27:18 +08:00
|
|
|
l = sys_cf;
|
|
|
|
/* Middle mode reg set no Standby */
|
|
|
|
l &= ~((1 << 12)|(1 << 13));
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, OCP_SYSCONFIG, 0);
|
2010-12-21 10:27:18 +08:00
|
|
|
}
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
|
|
|
|
|
|
|
|
/* Stop the Channel transmission */
|
2010-12-21 10:27:19 +08:00
|
|
|
l = p->dma_read(CCR, channels[i]);
|
2008-07-03 17:24:36 +08:00
|
|
|
l &= ~(1 << 7);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(l, CCR, channels[i]);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_i88))
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(sys_cf, OCP_SYSCONFIG, 0);
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_stop_dma_chain_transfers);
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
int *channels;
|
|
|
|
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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];
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
*ei = p->dma_read(CCEN, lch);
|
|
|
|
*fi = p->dma_read(CCFN, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_get_dma_chain_index);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
int *channels;
|
|
|
|
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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];
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
return p->dma_read(CDAC, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_get_dma_chain_dst_pos);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
int *channels;
|
|
|
|
|
|
|
|
/* Check for input params */
|
2008-07-03 17:24:31 +08:00
|
|
|
if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
|
2007-12-02 04:14:11 +08:00
|
|
|
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];
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
return p->dma_read(CSAC, lch);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(omap_get_dma_chain_src_pos);
|
2008-07-03 17:24:37 +08:00
|
|
|
#endif /* ifndef CONFIG_ARCH_OMAP1 */
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_OMAP1
|
|
|
|
|
|
|
|
static int omap1_dma_handle_ch(int ch)
|
|
|
|
{
|
2008-07-03 17:24:36 +08:00
|
|
|
u32 csr;
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
if (enable_1510_mode && ch >= 6) {
|
|
|
|
csr = dma_chan[ch].saved_csr;
|
|
|
|
dma_chan[ch].saved_csr = 0;
|
|
|
|
} else
|
2010-12-21 10:27:19 +08:00
|
|
|
csr = p->dma_read(CSR, ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) {
|
|
|
|
dma_chan[ch + 6].saved_csr = csr >> 7;
|
|
|
|
csr &= 0x7f;
|
|
|
|
}
|
|
|
|
if ((csr & 0x3f) == 0)
|
|
|
|
return 0;
|
|
|
|
if (unlikely(dma_chan[ch].dev_id == -1)) {
|
|
|
|
printk(KERN_WARNING "Spurious interrupt from DMA channel "
|
|
|
|
"%d (CSR %04x)\n", ch, csr);
|
|
|
|
return 0;
|
|
|
|
}
|
2006-06-27 07:16:15 +08:00
|
|
|
if (unlikely(csr & OMAP1_DMA_TOUT_IRQ))
|
2005-11-10 22:26:50 +08:00
|
|
|
printk(KERN_WARNING "DMA timeout with device %d\n",
|
|
|
|
dma_chan[ch].dev_id);
|
|
|
|
if (unlikely(csr & OMAP_DMA_DROP_IRQ))
|
|
|
|
printk(KERN_WARNING "DMA synchronization event drop occurred "
|
|
|
|
"with device %d\n", dma_chan[ch].dev_id);
|
|
|
|
if (likely(csr & OMAP_DMA_BLOCK_IRQ))
|
|
|
|
dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
|
|
|
|
if (likely(dma_chan[ch].callback != NULL))
|
|
|
|
dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
|
2008-07-03 17:24:37 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2006-10-07 01:53:39 +08:00
|
|
|
static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id)
|
2005-11-10 22:26:50 +08:00
|
|
|
{
|
|
|
|
int ch = ((int) dev_id) - 1;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
int handled_now = 0;
|
|
|
|
|
|
|
|
handled_now += omap1_dma_handle_ch(ch);
|
|
|
|
if (enable_1510_mode && dma_chan[ch + 6].saved_csr)
|
|
|
|
handled_now += omap1_dma_handle_ch(ch + 6);
|
|
|
|
if (!handled_now)
|
|
|
|
break;
|
|
|
|
handled += handled_now;
|
|
|
|
}
|
|
|
|
|
|
|
|
return handled ? IRQ_HANDLED : IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define omap1_dma_irq_handler NULL
|
|
|
|
#endif
|
|
|
|
|
2010-02-13 04:26:48 +08:00
|
|
|
#ifdef CONFIG_ARCH_OMAP2PLUS
|
2005-11-10 22:26:50 +08:00
|
|
|
|
|
|
|
static int omap2_dma_handle_ch(int ch)
|
|
|
|
{
|
2010-12-21 10:27:19 +08:00
|
|
|
u32 status = p->dma_read(CSR, ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2006-12-07 09:13:47 +08:00
|
|
|
if (!status) {
|
|
|
|
if (printk_ratelimit())
|
2008-07-03 17:24:37 +08:00
|
|
|
printk(KERN_WARNING "Spurious DMA IRQ for lch %d\n",
|
|
|
|
ch);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(1 << ch, IRQSTATUS_L0, ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
return 0;
|
2006-12-07 09:13:47 +08:00
|
|
|
}
|
|
|
|
if (unlikely(dma_chan[ch].dev_id == -1)) {
|
|
|
|
if (printk_ratelimit())
|
|
|
|
printk(KERN_WARNING "IRQ %04x for non-allocated DMA"
|
|
|
|
"channel %d\n", status, ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
return 0;
|
2006-12-07 09:13:47 +08:00
|
|
|
}
|
2005-11-10 22:26:50 +08:00
|
|
|
if (unlikely(status & OMAP_DMA_DROP_IRQ))
|
|
|
|
printk(KERN_INFO
|
|
|
|
"DMA synchronization event drop occurred with device "
|
|
|
|
"%d\n", dma_chan[ch].dev_id);
|
2008-12-11 09:36:53 +08:00
|
|
|
if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ)) {
|
2005-11-10 22:26:50 +08:00
|
|
|
printk(KERN_INFO "DMA transaction error with device %d\n",
|
|
|
|
dma_chan[ch].dev_id);
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ERRATA_i378)) {
|
2008-12-11 09:36:53 +08:00
|
|
|
u32 ccr;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
ccr = p->dma_read(CCR, ch);
|
2008-12-11 09:36:53 +08:00
|
|
|
ccr &= ~OMAP_DMA_CCR_EN;
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(ccr, CCR, ch);
|
2008-12-11 09:36:53 +08:00
|
|
|
dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
|
|
|
|
}
|
|
|
|
}
|
2006-06-27 07:16:15 +08:00
|
|
|
if (unlikely(status & OMAP2_DMA_SECURE_ERR_IRQ))
|
|
|
|
printk(KERN_INFO "DMA secure error with device %d\n",
|
|
|
|
dma_chan[ch].dev_id);
|
|
|
|
if (unlikely(status & OMAP2_DMA_MISALIGNED_ERR_IRQ))
|
|
|
|
printk(KERN_INFO "DMA misaligned error with device %d\n",
|
|
|
|
dma_chan[ch].dev_id);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2010-11-24 19:23:21 +08:00
|
|
|
p->dma_write(status, CSR, ch);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(1 << ch, IRQSTATUS_L0, ch);
|
2010-10-25 22:35:24 +08:00
|
|
|
/* read back the register to flush the write */
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_read(IRQSTATUS_L0, ch);
|
2005-11-10 22:26:50 +08:00
|
|
|
|
2007-12-02 04:14:11 +08:00
|
|
|
/* If the ch is not chained then chain_id will be -1 */
|
|
|
|
if (dma_chan[ch].chain_id != -1) {
|
|
|
|
int chain_id = dma_chan[ch].chain_id;
|
|
|
|
dma_chan[ch].state = DMA_CH_NOTSTARTED;
|
2010-12-21 10:27:19 +08:00
|
|
|
if (p->dma_read(CLNK_CTRL, ch) & (1 << 15))
|
2007-12-02 04:14:11 +08:00
|
|
|
dma_chan[dma_chan[ch].next_linked_ch].state =
|
|
|
|
DMA_CH_STARTED;
|
|
|
|
if (dma_linked_lch[chain_id].chain_mode ==
|
|
|
|
OMAP_DMA_DYNAMIC_CHAIN)
|
|
|
|
disable_lnk(ch);
|
|
|
|
|
|
|
|
if (!OMAP_DMA_CHAIN_QEMPTY(chain_id))
|
|
|
|
OMAP_DMA_CHAIN_INCQHEAD(chain_id);
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
status = p->dma_read(CSR, ch);
|
2010-11-24 19:23:21 +08:00
|
|
|
p->dma_write(status, CSR, ch);
|
2007-12-02 04:14:11 +08:00
|
|
|
}
|
|
|
|
|
2008-02-13 17:47:29 +08:00
|
|
|
if (likely(dma_chan[ch].callback != NULL))
|
|
|
|
dma_chan[ch].callback(ch, status, dma_chan[ch].data);
|
2007-12-02 04:14:11 +08:00
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* STATUS register count is from 1-32 while our is 0-31 */
|
2006-10-07 01:53:39 +08:00
|
|
|
static irqreturn_t omap2_dma_irq_handler(int irq, void *dev_id)
|
2005-11-10 22:26:50 +08:00
|
|
|
{
|
2009-03-24 09:07:49 +08:00
|
|
|
u32 val, enable_reg;
|
2005-11-10 22:26:50 +08:00
|
|
|
int i;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
val = p->dma_read(IRQSTATUS_L0, 0);
|
2006-12-07 09:13:47 +08:00
|
|
|
if (val == 0) {
|
|
|
|
if (printk_ratelimit())
|
|
|
|
printk(KERN_WARNING "Spurious DMA IRQ\n");
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
2010-12-21 10:27:19 +08:00
|
|
|
enable_reg = p->dma_read(IRQENABLE_L0, 0);
|
2009-03-24 09:07:49 +08:00
|
|
|
val &= enable_reg; /* Dispatch only relevant interrupts */
|
2008-07-03 17:24:31 +08:00
|
|
|
for (i = 0; i < dma_lch_count && val != 0; i++) {
|
2006-12-07 09:13:47 +08:00
|
|
|
if (val & 1)
|
|
|
|
omap2_dma_handle_ch(i);
|
|
|
|
val >>= 1;
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct irqaction omap24xx_dma_irq = {
|
|
|
|
.name = "DMA",
|
|
|
|
.handler = omap2_dma_irq_handler,
|
2006-07-03 08:20:05 +08:00
|
|
|
.flags = IRQF_DISABLED
|
2005-11-10 22:26:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#else
|
|
|
|
static struct irqaction omap24xx_dma_irq;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2008-08-28 21:13:31 +08:00
|
|
|
void omap_dma_global_context_save(void)
|
|
|
|
{
|
|
|
|
omap_dma_global_context.dma_irqenable_l0 =
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_read(IRQENABLE_L0, 0);
|
2008-08-28 21:13:31 +08:00
|
|
|
omap_dma_global_context.dma_ocp_sysconfig =
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_read(OCP_SYSCONFIG, 0);
|
|
|
|
omap_dma_global_context.dma_gcr = p->dma_read(GCR, 0);
|
2008-08-28 21:13:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void omap_dma_global_context_restore(void)
|
|
|
|
{
|
2009-05-20 21:58:30 +08:00
|
|
|
int ch;
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(omap_dma_global_context.dma_gcr, GCR, 0);
|
|
|
|
p->dma_write(omap_dma_global_context.dma_ocp_sysconfig,
|
2010-12-21 10:27:17 +08:00
|
|
|
OCP_SYSCONFIG, 0);
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(omap_dma_global_context.dma_irqenable_l0,
|
2010-12-21 10:27:17 +08:00
|
|
|
IRQENABLE_L0, 0);
|
2008-08-28 21:13:31 +08:00
|
|
|
|
2010-12-21 10:27:18 +08:00
|
|
|
if (IS_DMA_ERRATA(DMA_ROMCODE_BUG))
|
2010-12-21 10:27:19 +08:00
|
|
|
p->dma_write(0x3 , IRQSTATUS_L0, 0);
|
2009-05-20 21:58:30 +08:00
|
|
|
|
|
|
|
for (ch = 0; ch < dma_chan_count; ch++)
|
|
|
|
if (dma_chan[ch].dev_id != -1)
|
|
|
|
omap_clear_dma(ch);
|
2008-08-28 21:13:31 +08:00
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
static int __devinit omap_system_dma_probe(struct platform_device *pdev)
|
2010-12-21 10:27:18 +08:00
|
|
|
{
|
2010-12-21 10:27:19 +08:00
|
|
|
int ch, ret = 0;
|
|
|
|
int dma_irq;
|
|
|
|
char irq_name[4];
|
|
|
|
int irq_rel;
|
|
|
|
|
|
|
|
p = pdev->dev.platform_data;
|
|
|
|
if (!p) {
|
|
|
|
dev_err(&pdev->dev, "%s: System DMA initialized without"
|
|
|
|
"platform data\n", __func__);
|
|
|
|
return -EINVAL;
|
2008-07-03 17:24:36 +08:00
|
|
|
}
|
2008-07-03 17:24:31 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
d = p->dma_attr;
|
|
|
|
errata = p->errata;
|
2010-12-21 10:27:17 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
if ((d->dev_caps & RESERVE_CHANNEL) && omap_dma_reserve_channels
|
2009-03-24 09:07:48 +08:00
|
|
|
&& (omap_dma_reserve_channels <= dma_lch_count))
|
2010-12-21 10:27:19 +08:00
|
|
|
d->lch_count = omap_dma_reserve_channels;
|
2009-03-24 09:07:48 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
dma_lch_count = d->lch_count;
|
|
|
|
dma_chan_count = dma_lch_count;
|
|
|
|
dma_chan = d->chan;
|
|
|
|
enable_1510_mode = d->dev_caps & ENABLE_1510_MODE;
|
2008-07-03 17:24:31 +08:00
|
|
|
|
|
|
|
if (cpu_class_is_omap2()) {
|
|
|
|
dma_linked_lch = kzalloc(sizeof(struct dma_link_info) *
|
|
|
|
dma_lch_count, GFP_KERNEL);
|
|
|
|
if (!dma_linked_lch) {
|
2010-12-21 10:27:19 +08:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto exit_dma_lch_fail;
|
2008-07-03 17:24:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
spin_lock_init(&dma_chan_lock);
|
|
|
|
for (ch = 0; ch < dma_chan_count; ch++) {
|
2005-11-10 22:26:50 +08:00
|
|
|
omap_clear_dma(ch);
|
2010-05-15 03:05:25 +08:00
|
|
|
if (cpu_class_is_omap2())
|
|
|
|
omap2_disable_irq_lch(ch);
|
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
dma_chan[ch].dev_id = -1;
|
|
|
|
dma_chan[ch].next_lch = -1;
|
|
|
|
|
|
|
|
if (ch >= 6 && enable_1510_mode)
|
|
|
|
continue;
|
|
|
|
|
2005-11-10 22:26:50 +08:00
|
|
|
if (cpu_class_is_omap1()) {
|
2008-07-03 17:24:37 +08:00
|
|
|
/*
|
|
|
|
* request_irq() doesn't like dev_id (ie. ch) being
|
|
|
|
* zero, so we have to kludge around this.
|
|
|
|
*/
|
2010-12-21 10:27:19 +08:00
|
|
|
sprintf(&irq_name[0], "%d", ch);
|
|
|
|
dma_irq = platform_get_irq_byname(pdev, irq_name);
|
|
|
|
|
|
|
|
if (dma_irq < 0) {
|
|
|
|
ret = dma_irq;
|
|
|
|
goto exit_dma_irq_fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* INT_DMA_LCD is handled in lcd_dma.c */
|
|
|
|
if (dma_irq == INT_DMA_LCD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
ret = request_irq(dma_irq,
|
2005-11-10 22:26:50 +08:00
|
|
|
omap1_dma_irq_handler, 0, "DMA",
|
|
|
|
(void *) (ch + 1));
|
2010-12-21 10:27:19 +08:00
|
|
|
if (ret != 0)
|
|
|
|
goto exit_dma_irq_fail;
|
2005-11-10 22:26:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-29 05:16:04 +08:00
|
|
|
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx())
|
2007-12-02 04:14:11 +08:00
|
|
|
omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE,
|
|
|
|
DMA_DEFAULT_FIFO_DEPTH, 0);
|
|
|
|
|
2009-05-29 05:16:04 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
2010-12-21 10:27:19 +08:00
|
|
|
strcpy(irq_name, "0");
|
|
|
|
dma_irq = platform_get_irq_byname(pdev, irq_name);
|
|
|
|
if (dma_irq < 0) {
|
|
|
|
dev_err(&pdev->dev, "failed: request IRQ %d", dma_irq);
|
|
|
|
goto exit_dma_lch_fail;
|
|
|
|
}
|
|
|
|
ret = setup_irq(dma_irq, &omap24xx_dma_irq);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&pdev->dev, "set_up failed for IRQ %d"
|
|
|
|
"for DMA (error %d)\n", dma_irq, ret);
|
|
|
|
goto exit_dma_lch_fail;
|
2009-03-26 21:59:00 +08:00
|
|
|
}
|
2009-06-23 18:30:24 +08:00
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
/* reserve dma channels 0 and 1 in high security devices */
|
|
|
|
if (cpu_is_omap34xx() &&
|
|
|
|
(omap_type() != OMAP2_DEVICE_TYPE_GP)) {
|
|
|
|
printk(KERN_INFO "Reserving DMA channels 0 and 1 for "
|
|
|
|
"HS ROM code\n");
|
|
|
|
dma_chan[0].dev_id = 0;
|
|
|
|
dma_chan[1].dev_id = 1;
|
|
|
|
}
|
|
|
|
p->show_dma_caps();
|
2005-07-11 02:58:15 +08:00
|
|
|
return 0;
|
2009-10-20 06:25:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
exit_dma_irq_fail:
|
|
|
|
dev_err(&pdev->dev, "unable to request IRQ %d"
|
|
|
|
"for DMA (error %d)\n", dma_irq, ret);
|
|
|
|
for (irq_rel = 0; irq_rel < ch; irq_rel++) {
|
|
|
|
dma_irq = platform_get_irq(pdev, irq_rel);
|
|
|
|
free_irq(dma_irq, (void *)(irq_rel + 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
exit_dma_lch_fail:
|
|
|
|
kfree(p);
|
|
|
|
kfree(d);
|
2009-10-20 06:25:15 +08:00
|
|
|
kfree(dma_chan);
|
2010-12-21 10:27:19 +08:00
|
|
|
return ret;
|
|
|
|
}
|
2009-10-20 06:25:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
static int __devexit omap_system_dma_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
int dma_irq;
|
2009-10-20 06:25:15 +08:00
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
if (cpu_class_is_omap2()) {
|
|
|
|
char irq_name[4];
|
|
|
|
strcpy(irq_name, "0");
|
|
|
|
dma_irq = platform_get_irq_byname(pdev, irq_name);
|
|
|
|
remove_irq(dma_irq, &omap24xx_dma_irq);
|
|
|
|
} else {
|
|
|
|
int irq_rel = 0;
|
|
|
|
for ( ; irq_rel < dma_chan_count; irq_rel++) {
|
|
|
|
dma_irq = platform_get_irq(pdev, irq_rel);
|
|
|
|
free_irq(dma_irq, (void *)(irq_rel + 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kfree(p);
|
|
|
|
kfree(d);
|
|
|
|
kfree(dma_chan);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver omap_system_dma_driver = {
|
|
|
|
.probe = omap_system_dma_probe,
|
|
|
|
.remove = omap_system_dma_remove,
|
|
|
|
.driver = {
|
|
|
|
.name = "omap_dma_system"
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init omap_system_dma_init(void)
|
|
|
|
{
|
|
|
|
return platform_driver_register(&omap_system_dma_driver);
|
|
|
|
}
|
|
|
|
arch_initcall(omap_system_dma_init);
|
|
|
|
|
|
|
|
static void __exit omap_system_dma_exit(void)
|
|
|
|
{
|
|
|
|
platform_driver_unregister(&omap_system_dma_driver);
|
2005-07-11 02:58:15 +08:00
|
|
|
}
|
|
|
|
|
2010-12-21 10:27:19 +08:00
|
|
|
MODULE_DESCRIPTION("OMAP SYSTEM DMA DRIVER");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
|
|
MODULE_AUTHOR("Texas Instruments Inc");
|
2005-07-11 02:58:15 +08:00
|
|
|
|
2009-03-24 09:07:48 +08:00
|
|
|
/*
|
|
|
|
* Reserve the omap SDMA channels using cmdline bootarg
|
|
|
|
* "omap_dma_reserve_ch=". The valid range is 1 to 32
|
|
|
|
*/
|
|
|
|
static int __init omap_dma_cmdline_reserve_ch(char *str)
|
|
|
|
{
|
|
|
|
if (get_option(&str, &omap_dma_reserve_channels) != 1)
|
|
|
|
omap_dma_reserve_channels = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
__setup("omap_dma_reserve_ch=", omap_dma_cmdline_reserve_ch);
|
|
|
|
|
2005-07-11 02:58:15 +08:00
|
|
|
|