blob: 4b3829ae533750625854f18b7b2a77d5055f0e4a [file] [log] [blame]
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001/*
2 * linux/arch/arm/plat-omap/dma.c
3 *
Tony Lindgren97b7f712008-07-03 12:24:37 +03004 * Copyright (C) 2003 - 2008 Nokia Corporation
Jan Engelhardt96de0e22007-10-19 23:21:04 +02005 * Author: Juha Yrjölä <juha.yrjola@nokia.com>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01006 * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com>
7 * Graphics DMA and LCD DMA graphics tranformations
8 * by Imre Deak <imre.deak@nokia.com>
Anand Gadiyarf8151e52007-12-01 12:14:11 -08009 * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc.
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000010 * Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010011 * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc.
12 *
Santosh Shilimkar44169072009-05-28 14:16:04 -070013 * Copyright (C) 2009 Texas Instruments
14 * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com>
15 *
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010016 * Support functions for the OMAP internal DMA channels.
17 *
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -080018 * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
19 * Converted DMA library into DMA platform driver.
20 * - G, Manjunath Kondaiah <manjugk@ti.com>
21 *
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010022 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License version 2 as
24 * published by the Free Software Foundation.
25 *
26 */
27
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/sched.h>
31#include <linux/spinlock.h>
32#include <linux/errno.h>
33#include <linux/interrupt.h>
Thomas Gleixner418ca1f02006-07-01 22:32:41 +010034#include <linux/irq.h>
Tony Lindgren97b7f712008-07-03 12:24:37 +030035#include <linux/io.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090036#include <linux/slab.h>
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -070037#include <linux/delay.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010038
Russell Kinga09e64f2008-08-05 16:14:15 +010039#include <mach/hardware.h>
Tony Lindgrence491cf2009-10-20 09:40:47 -070040#include <plat/dma.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010041
Tony Lindgrence491cf2009-10-20 09:40:47 -070042#include <plat/tc.h>
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010043
Paul Walmsleybc4d8b52012-04-13 06:34:30 -060044/*
45 * MAX_LOGICAL_DMA_CH_COUNT: the maximum number of logical DMA
46 * channels that an instance of the SDMA IP block can support. Used
47 * to size arrays. (The actual maximum on a particular SoC may be less
48 * than this -- for example, OMAP1 SDMA instances only support 17 logical
49 * DMA channels.)
50 */
51#define MAX_LOGICAL_DMA_CH_COUNT 32
52
Anand Gadiyarf8151e52007-12-01 12:14:11 -080053#undef DEBUG
54
55#ifndef CONFIG_ARCH_OMAP1
56enum { DMA_CH_ALLOC_DONE, DMA_CH_PARAMS_SET_DONE, DMA_CH_STARTED,
57 DMA_CH_QUEUED, DMA_CH_NOTSTARTED, DMA_CH_PAUSED, DMA_CH_LINK_ENABLED
58};
59
60enum { DMA_CHAIN_STARTED, DMA_CHAIN_NOTSTARTED };
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000061#endif
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010062
Tony Lindgren97b7f712008-07-03 12:24:37 +030063#define OMAP_DMA_ACTIVE 0x01
Adrian Hunter4fb699b2010-11-24 13:23:21 +020064#define OMAP2_DMA_CSR_CLEAR_MASK 0xffffffff
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010065
Tony Lindgren97b7f712008-07-03 12:24:37 +030066#define OMAP_FUNC_MUX_ARM_BASE (0xfffe1000 + 0xec)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010067
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -080068static struct omap_system_dma_plat_info *p;
69static struct omap_dma_dev_attr *d;
70
Tony Lindgren97b7f712008-07-03 12:24:37 +030071static int enable_1510_mode;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -080072static u32 errata;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +010073
Tero Kristof2d11852008-08-28 13:13:31 +000074static struct omap_dma_global_context_registers {
75 u32 dma_irqenable_l0;
76 u32 dma_ocp_sysconfig;
77 u32 dma_gcr;
78} omap_dma_global_context;
79
Anand Gadiyarf8151e52007-12-01 12:14:11 -080080struct dma_link_info {
81 int *linked_dmach_q;
82 int no_of_lchs_linked;
83
84 int q_count;
85 int q_tail;
86 int q_head;
87
88 int chain_state;
89 int chain_mode;
90
91};
92
Tony Lindgren4d963722008-07-03 12:24:31 +030093static struct dma_link_info *dma_linked_lch;
94
95#ifndef CONFIG_ARCH_OMAP1
Anand Gadiyarf8151e52007-12-01 12:14:11 -080096
97/* Chain handling macros */
98#define OMAP_DMA_CHAIN_QINIT(chain_id) \
99 do { \
100 dma_linked_lch[chain_id].q_head = \
101 dma_linked_lch[chain_id].q_tail = \
102 dma_linked_lch[chain_id].q_count = 0; \
103 } while (0)
104#define OMAP_DMA_CHAIN_QFULL(chain_id) \
105 (dma_linked_lch[chain_id].no_of_lchs_linked == \
106 dma_linked_lch[chain_id].q_count)
107#define OMAP_DMA_CHAIN_QLAST(chain_id) \
108 do { \
109 ((dma_linked_lch[chain_id].no_of_lchs_linked-1) == \
110 dma_linked_lch[chain_id].q_count) \
111 } while (0)
112#define OMAP_DMA_CHAIN_QEMPTY(chain_id) \
113 (0 == dma_linked_lch[chain_id].q_count)
114#define __OMAP_DMA_CHAIN_INCQ(end) \
115 ((end) = ((end)+1) % dma_linked_lch[chain_id].no_of_lchs_linked)
116#define OMAP_DMA_CHAIN_INCQHEAD(chain_id) \
117 do { \
118 __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_head); \
119 dma_linked_lch[chain_id].q_count--; \
120 } while (0)
121
122#define OMAP_DMA_CHAIN_INCQTAIL(chain_id) \
123 do { \
124 __OMAP_DMA_CHAIN_INCQ(dma_linked_lch[chain_id].q_tail); \
125 dma_linked_lch[chain_id].q_count++; \
126 } while (0)
127#endif
Tony Lindgren4d963722008-07-03 12:24:31 +0300128
129static int dma_lch_count;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100130static int dma_chan_count;
Santosh Shilimkar2263f022009-03-23 18:07:48 -0700131static int omap_dma_reserve_channels;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100132
133static spinlock_t dma_chan_lock;
Tony Lindgren4d963722008-07-03 12:24:31 +0300134static struct omap_dma_lch *dma_chan;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100135
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800136static inline void disable_lnk(int lch);
137static void omap_disable_channel_irq(int lch);
138static inline void omap_enable_channel_irq(int lch);
139
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000140#define REVISIT_24XX() printk(KERN_ERR "FIXME: no %s on 24xx\n", \
Harvey Harrison8e86f422008-03-04 15:08:02 -0800141 __func__);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000142
143#ifdef CONFIG_ARCH_OMAP15XX
144/* Returns 1 if the DMA module is in OMAP1510-compatible mode, 0 otherwise */
Aaro Koskinenc7767582011-01-27 16:39:43 -0800145static int omap_dma_in_1510_mode(void)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000146{
147 return enable_1510_mode;
148}
149#else
150#define omap_dma_in_1510_mode() 0
151#endif
152
153#ifdef CONFIG_ARCH_OMAP1
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100154static inline int get_gdma_dev(int req)
155{
156 u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
157 int shift = ((req - 1) % 5) * 6;
158
159 return ((omap_readl(reg) >> shift) & 0x3f) + 1;
160}
161
162static inline void set_gdma_dev(int req, int dev)
163{
164 u32 reg = OMAP_FUNC_MUX_ARM_BASE + ((req - 1) / 5) * 4;
165 int shift = ((req - 1) % 5) * 6;
166 u32 l;
167
168 l = omap_readl(reg);
169 l &= ~(0x3f << shift);
170 l |= (dev - 1) << shift;
171 omap_writel(l, reg);
172}
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000173#else
174#define set_gdma_dev(req, dev) do {} while (0)
Tony Lindgren2c799ce2012-02-24 10:34:35 -0800175#define omap_readl(reg) 0
176#define omap_writel(val, reg) do {} while (0)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000177#endif
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100178
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300179void omap_set_dma_priority(int lch, int dst_port, int priority)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100180{
181 unsigned long reg;
182 u32 l;
183
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300184 if (cpu_class_is_omap1()) {
185 switch (dst_port) {
186 case OMAP_DMA_PORT_OCP_T1: /* FFFECC00 */
187 reg = OMAP_TC_OCPT1_PRIOR;
188 break;
189 case OMAP_DMA_PORT_OCP_T2: /* FFFECCD0 */
190 reg = OMAP_TC_OCPT2_PRIOR;
191 break;
192 case OMAP_DMA_PORT_EMIFF: /* FFFECC08 */
193 reg = OMAP_TC_EMIFF_PRIOR;
194 break;
195 case OMAP_DMA_PORT_EMIFS: /* FFFECC04 */
196 reg = OMAP_TC_EMIFS_PRIOR;
197 break;
198 default:
199 BUG();
200 return;
201 }
202 l = omap_readl(reg);
203 l &= ~(0xf << 8);
204 l |= (priority & 0xf) << 8;
205 omap_writel(l, reg);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100206 }
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300207
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800208 if (cpu_class_is_omap2()) {
Tony Lindgren0499bde2008-07-03 12:24:36 +0300209 u32 ccr;
210
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800211 ccr = p->dma_read(CCR, lch);
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300212 if (priority)
Tony Lindgren0499bde2008-07-03 12:24:36 +0300213 ccr |= (1 << 6);
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300214 else
Tony Lindgren0499bde2008-07-03 12:24:36 +0300215 ccr &= ~(1 << 6);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800216 p->dma_write(ccr, CCR, lch);
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300217 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100218}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300219EXPORT_SYMBOL(omap_set_dma_priority);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100220
221void omap_set_dma_transfer_params(int lch, int data_type, int elem_count,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000222 int frame_count, int sync_mode,
223 int dma_trigger, int src_or_dst_synch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100224{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300225 u32 l;
226
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800227 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300228 l &= ~0x03;
229 l |= data_type;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800230 p->dma_write(l, CSDP, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100231
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000232 if (cpu_class_is_omap1()) {
Tony Lindgren0499bde2008-07-03 12:24:36 +0300233 u16 ccr;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100234
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800235 ccr = p->dma_read(CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300236 ccr &= ~(1 << 5);
237 if (sync_mode == OMAP_DMA_SYNC_FRAME)
238 ccr |= 1 << 5;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800239 p->dma_write(ccr, CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300240
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800241 ccr = p->dma_read(CCR2, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300242 ccr &= ~(1 << 2);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000243 if (sync_mode == OMAP_DMA_SYNC_BLOCK)
Tony Lindgren0499bde2008-07-03 12:24:36 +0300244 ccr |= 1 << 2;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800245 p->dma_write(ccr, CCR2, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000246 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100247
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800248 if (cpu_class_is_omap2() && dma_trigger) {
Tony Lindgren0499bde2008-07-03 12:24:36 +0300249 u32 val;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100250
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800251 val = p->dma_read(CCR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100252
Anand Gadiyar4b3cf442009-01-15 13:09:53 +0200253 /* DMA_SYNCHRO_CONTROL_UPPER depends on the channel number */
Samu Onkalo72a11792010-08-02 14:21:40 +0300254 val &= ~((1 << 23) | (3 << 19) | 0x1f);
Anand Gadiyar4b3cf442009-01-15 13:09:53 +0200255 val |= (dma_trigger & ~0x1f) << 14;
256 val |= dma_trigger & 0x1f;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000257
258 if (sync_mode & OMAP_DMA_SYNC_FRAME)
259 val |= 1 << 5;
Peter Ujfalusieca9e562006-06-26 16:16:06 -0700260 else
261 val &= ~(1 << 5);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000262
263 if (sync_mode & OMAP_DMA_SYNC_BLOCK)
264 val |= 1 << 18;
Peter Ujfalusieca9e562006-06-26 16:16:06 -0700265 else
266 val &= ~(1 << 18);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000267
Samu Onkalo72a11792010-08-02 14:21:40 +0300268 if (src_or_dst_synch == OMAP_DMA_DST_SYNC_PREFETCH) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000269 val &= ~(1 << 24); /* dest synch */
Samu Onkalo72a11792010-08-02 14:21:40 +0300270 val |= (1 << 23); /* Prefetch */
271 } else if (src_or_dst_synch) {
272 val |= 1 << 24; /* source synch */
273 } else {
274 val &= ~(1 << 24); /* dest synch */
275 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800276 p->dma_write(val, CCR, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000277 }
278
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800279 p->dma_write(elem_count, CEN, lch);
280 p->dma_write(frame_count, CFN, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100281}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300282EXPORT_SYMBOL(omap_set_dma_transfer_params);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000283
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100284void omap_set_dma_color_mode(int lch, enum omap_dma_color_mode mode, u32 color)
285{
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100286 BUG_ON(omap_dma_in_1510_mode());
287
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700288 if (cpu_class_is_omap1()) {
289 u16 w;
290
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800291 w = p->dma_read(CCR2, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700292 w &= ~0x03;
293
294 switch (mode) {
295 case OMAP_DMA_CONSTANT_FILL:
296 w |= 0x01;
297 break;
298 case OMAP_DMA_TRANSPARENT_COPY:
299 w |= 0x02;
300 break;
301 case OMAP_DMA_COLOR_DIS:
302 break;
303 default:
304 BUG();
305 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800306 p->dma_write(w, CCR2, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700307
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800308 w = p->dma_read(LCH_CTRL, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700309 w &= ~0x0f;
310 /* Default is channel type 2D */
311 if (mode) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800312 p->dma_write(color, COLOR, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700313 w |= 1; /* Channel type G */
314 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800315 p->dma_write(w, LCH_CTRL, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700316 }
317
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800318 if (cpu_class_is_omap2()) {
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700319 u32 val;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000320
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800321 val = p->dma_read(CCR, lch);
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700322 val &= ~((1 << 17) | (1 << 16));
Tony Lindgren0499bde2008-07-03 12:24:36 +0300323
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700324 switch (mode) {
325 case OMAP_DMA_CONSTANT_FILL:
326 val |= 1 << 16;
327 break;
328 case OMAP_DMA_TRANSPARENT_COPY:
329 val |= 1 << 17;
330 break;
331 case OMAP_DMA_COLOR_DIS:
332 break;
333 default:
334 BUG();
335 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800336 p->dma_write(val, CCR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100337
Tomi Valkeinen0815f8e2009-05-28 13:23:51 -0700338 color &= 0xffffff;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800339 p->dma_write(color, COLOR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100340 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100341}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300342EXPORT_SYMBOL(omap_set_dma_color_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100343
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300344void omap_set_dma_write_mode(int lch, enum omap_dma_write_mode mode)
345{
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800346 if (cpu_class_is_omap2()) {
Tony Lindgren0499bde2008-07-03 12:24:36 +0300347 u32 csdp;
348
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800349 csdp = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300350 csdp &= ~(0x3 << 16);
351 csdp |= (mode << 16);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800352 p->dma_write(csdp, CSDP, lch);
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300353 }
354}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300355EXPORT_SYMBOL(omap_set_dma_write_mode);
Tony Lindgren709eb3e52006-09-25 12:45:45 +0300356
Tony Lindgren0499bde2008-07-03 12:24:36 +0300357void omap_set_dma_channel_mode(int lch, enum omap_dma_channel_mode mode)
358{
359 if (cpu_class_is_omap1() && !cpu_is_omap15xx()) {
360 u32 l;
361
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800362 l = p->dma_read(LCH_CTRL, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300363 l &= ~0x7;
364 l |= mode;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800365 p->dma_write(l, LCH_CTRL, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300366 }
367}
368EXPORT_SYMBOL(omap_set_dma_channel_mode);
369
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000370/* Note that src_port is only for omap1 */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100371void omap_set_dma_src_params(int lch, int src_port, int src_amode,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000372 unsigned long src_start,
373 int src_ei, int src_fi)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100374{
Tony Lindgren97b7f712008-07-03 12:24:37 +0300375 u32 l;
376
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000377 if (cpu_class_is_omap1()) {
Tony Lindgren0499bde2008-07-03 12:24:36 +0300378 u16 w;
379
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800380 w = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300381 w &= ~(0x1f << 2);
382 w |= src_port << 2;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800383 p->dma_write(w, CSDP, lch);
Tony Lindgren97b7f712008-07-03 12:24:37 +0300384 }
Tony Lindgren0499bde2008-07-03 12:24:36 +0300385
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800386 l = p->dma_read(CCR, lch);
Tony Lindgren97b7f712008-07-03 12:24:37 +0300387 l &= ~(0x03 << 12);
388 l |= src_amode << 12;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800389 p->dma_write(l, CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300390
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800391 p->dma_write(src_start, CSSA, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100392
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800393 p->dma_write(src_ei, CSEI, lch);
394 p->dma_write(src_fi, CSFI, lch);
Tony Lindgren97b7f712008-07-03 12:24:37 +0300395}
396EXPORT_SYMBOL(omap_set_dma_src_params);
397
398void omap_set_dma_params(int lch, struct omap_dma_channel_params *params)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000399{
400 omap_set_dma_transfer_params(lch, params->data_type,
401 params->elem_count, params->frame_count,
402 params->sync_mode, params->trigger,
403 params->src_or_dst_synch);
404 omap_set_dma_src_params(lch, params->src_port,
405 params->src_amode, params->src_start,
406 params->src_ei, params->src_fi);
407
408 omap_set_dma_dest_params(lch, params->dst_port,
409 params->dst_amode, params->dst_start,
410 params->dst_ei, params->dst_fi);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800411 if (params->read_prio || params->write_prio)
412 omap_dma_set_prio_lch(lch, params->read_prio,
413 params->write_prio);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100414}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300415EXPORT_SYMBOL(omap_set_dma_params);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100416
417void omap_set_dma_src_index(int lch, int eidx, int fidx)
418{
Tony Lindgren97b7f712008-07-03 12:24:37 +0300419 if (cpu_class_is_omap2())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000420 return;
Tony Lindgren97b7f712008-07-03 12:24:37 +0300421
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800422 p->dma_write(eidx, CSEI, lch);
423 p->dma_write(fidx, CSFI, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100424}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300425EXPORT_SYMBOL(omap_set_dma_src_index);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100426
427void omap_set_dma_src_data_pack(int lch, int enable)
428{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300429 u32 l;
430
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800431 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300432 l &= ~(1 << 6);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000433 if (enable)
Tony Lindgren0499bde2008-07-03 12:24:36 +0300434 l |= (1 << 6);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800435 p->dma_write(l, CSDP, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100436}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300437EXPORT_SYMBOL(omap_set_dma_src_data_pack);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100438
439void omap_set_dma_src_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
440{
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700441 unsigned int burst = 0;
Tony Lindgren0499bde2008-07-03 12:24:36 +0300442 u32 l;
443
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800444 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300445 l &= ~(0x03 << 7);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100446
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100447 switch (burst_mode) {
448 case OMAP_DMA_DATA_BURST_DIS:
449 break;
450 case OMAP_DMA_DATA_BURST_4:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800451 if (cpu_class_is_omap2())
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700452 burst = 0x1;
453 else
454 burst = 0x2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100455 break;
456 case OMAP_DMA_DATA_BURST_8:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800457 if (cpu_class_is_omap2()) {
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700458 burst = 0x2;
459 break;
460 }
manjugk manjugkea221a62010-05-14 12:05:25 -0700461 /*
462 * not supported by current hardware on OMAP1
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100463 * w |= (0x03 << 7);
464 * fall through
465 */
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700466 case OMAP_DMA_DATA_BURST_16:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800467 if (cpu_class_is_omap2()) {
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700468 burst = 0x3;
469 break;
470 }
manjugk manjugkea221a62010-05-14 12:05:25 -0700471 /*
472 * OMAP1 don't support burst 16
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700473 * fall through
474 */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100475 default:
476 BUG();
477 }
Tony Lindgren0499bde2008-07-03 12:24:36 +0300478
479 l |= (burst << 7);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800480 p->dma_write(l, CSDP, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100481}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300482EXPORT_SYMBOL(omap_set_dma_src_burst_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100483
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000484/* Note that dest_port is only for OMAP1 */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100485void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000486 unsigned long dest_start,
487 int dst_ei, int dst_fi)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100488{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300489 u32 l;
490
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000491 if (cpu_class_is_omap1()) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800492 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300493 l &= ~(0x1f << 9);
494 l |= dest_port << 9;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800495 p->dma_write(l, CSDP, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000496 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100497
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800498 l = p->dma_read(CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300499 l &= ~(0x03 << 14);
500 l |= dest_amode << 14;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800501 p->dma_write(l, CCR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100502
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800503 p->dma_write(dest_start, CDSA, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100504
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800505 p->dma_write(dst_ei, CDEI, lch);
506 p->dma_write(dst_fi, CDFI, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100507}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300508EXPORT_SYMBOL(omap_set_dma_dest_params);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100509
510void omap_set_dma_dest_index(int lch, int eidx, int fidx)
511{
Tony Lindgren97b7f712008-07-03 12:24:37 +0300512 if (cpu_class_is_omap2())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000513 return;
Tony Lindgren97b7f712008-07-03 12:24:37 +0300514
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800515 p->dma_write(eidx, CDEI, lch);
516 p->dma_write(fidx, CDFI, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100517}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300518EXPORT_SYMBOL(omap_set_dma_dest_index);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100519
520void omap_set_dma_dest_data_pack(int lch, int enable)
521{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300522 u32 l;
523
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800524 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300525 l &= ~(1 << 13);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000526 if (enable)
Tony Lindgren0499bde2008-07-03 12:24:36 +0300527 l |= 1 << 13;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800528 p->dma_write(l, CSDP, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100529}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300530EXPORT_SYMBOL(omap_set_dma_dest_data_pack);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100531
532void omap_set_dma_dest_burst_mode(int lch, enum omap_dma_burst_mode burst_mode)
533{
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700534 unsigned int burst = 0;
Tony Lindgren0499bde2008-07-03 12:24:36 +0300535 u32 l;
536
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800537 l = p->dma_read(CSDP, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300538 l &= ~(0x03 << 14);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100539
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100540 switch (burst_mode) {
541 case OMAP_DMA_DATA_BURST_DIS:
542 break;
543 case OMAP_DMA_DATA_BURST_4:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800544 if (cpu_class_is_omap2())
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700545 burst = 0x1;
546 else
547 burst = 0x2;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100548 break;
549 case OMAP_DMA_DATA_BURST_8:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800550 if (cpu_class_is_omap2())
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700551 burst = 0x2;
552 else
553 burst = 0x3;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100554 break;
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700555 case OMAP_DMA_DATA_BURST_16:
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800556 if (cpu_class_is_omap2()) {
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700557 burst = 0x3;
558 break;
559 }
manjugk manjugkea221a62010-05-14 12:05:25 -0700560 /*
561 * OMAP1 don't support burst 16
Kyungmin Park6dc3c8f2006-06-26 16:16:14 -0700562 * fall through
563 */
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100564 default:
565 printk(KERN_ERR "Invalid DMA burst mode\n");
566 BUG();
567 return;
568 }
Tony Lindgren0499bde2008-07-03 12:24:36 +0300569 l |= (burst << 14);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800570 p->dma_write(l, CSDP, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100571}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300572EXPORT_SYMBOL(omap_set_dma_dest_burst_mode);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100573
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000574static inline void omap_enable_channel_irq(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100575{
Tony Lindgren7ff879d2006-06-26 16:16:15 -0700576 /* Clear CSR */
577 if (cpu_class_is_omap1())
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700578 p->dma_read(CSR, lch);
579 else
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800580 p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000581
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100582 /* Enable some nice interrupts. */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800583 p->dma_write(dma_chan[lch].enabled_irqs, CICR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100584}
585
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700586static inline void omap_disable_channel_irq(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100587{
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700588 /* disable channel interrupts */
589 p->dma_write(0, CICR, lch);
590 /* Clear CSR */
591 if (cpu_class_is_omap1())
592 p->dma_read(CSR, lch);
593 else
594 p->dma_write(OMAP2_DMA_CSR_CLEAR_MASK, CSR, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100595}
596
597void omap_enable_dma_irq(int lch, u16 bits)
598{
599 dma_chan[lch].enabled_irqs |= bits;
600}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300601EXPORT_SYMBOL(omap_enable_dma_irq);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100602
603void omap_disable_dma_irq(int lch, u16 bits)
604{
605 dma_chan[lch].enabled_irqs &= ~bits;
606}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300607EXPORT_SYMBOL(omap_disable_dma_irq);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100608
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000609static inline void enable_lnk(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100610{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300611 u32 l;
612
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800613 l = p->dma_read(CLNK_CTRL, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300614
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000615 if (cpu_class_is_omap1())
Tony Lindgren0499bde2008-07-03 12:24:36 +0300616 l &= ~(1 << 14);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100617
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000618 /* Set the ENABLE_LNK bits */
619 if (dma_chan[lch].next_lch != -1)
Tony Lindgren0499bde2008-07-03 12:24:36 +0300620 l = dma_chan[lch].next_lch | (1 << 15);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800621
622#ifndef CONFIG_ARCH_OMAP1
Tony Lindgren97b7f712008-07-03 12:24:37 +0300623 if (cpu_class_is_omap2())
624 if (dma_chan[lch].next_linked_ch != -1)
625 l = dma_chan[lch].next_linked_ch | (1 << 15);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800626#endif
Tony Lindgren0499bde2008-07-03 12:24:36 +0300627
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800628 p->dma_write(l, CLNK_CTRL, lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100629}
630
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000631static inline void disable_lnk(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100632{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300633 u32 l;
634
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800635 l = p->dma_read(CLNK_CTRL, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300636
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000637 /* Disable interrupts */
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700638 omap_disable_channel_irq(lch);
639
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000640 if (cpu_class_is_omap1()) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000641 /* Set the STOP_LNK bit */
Tony Lindgren0499bde2008-07-03 12:24:36 +0300642 l |= 1 << 14;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100643 }
644
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800645 if (cpu_class_is_omap2()) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000646 /* Clear the ENABLE_LNK bit */
Tony Lindgren0499bde2008-07-03 12:24:36 +0300647 l &= ~(1 << 15);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000648 }
649
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800650 p->dma_write(l, CLNK_CTRL, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000651 dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
652}
653
654static inline void omap2_enable_irq_lch(int lch)
655{
656 u32 val;
Tao Huee907322009-11-10 18:55:17 -0800657 unsigned long flags;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000658
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800659 if (!cpu_class_is_omap2())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000660 return;
661
Tao Huee907322009-11-10 18:55:17 -0800662 spin_lock_irqsave(&dma_chan_lock, flags);
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700663 /* clear IRQ STATUS */
664 p->dma_write(1 << lch, IRQSTATUS_L0, lch);
665 /* Enable interrupt */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800666 val = p->dma_read(IRQENABLE_L0, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000667 val |= 1 << lch;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800668 p->dma_write(val, IRQENABLE_L0, lch);
Tao Huee907322009-11-10 18:55:17 -0800669 spin_unlock_irqrestore(&dma_chan_lock, flags);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100670}
671
Mika Westerbergada8d4a2010-05-14 12:05:25 -0700672static inline void omap2_disable_irq_lch(int lch)
673{
674 u32 val;
675 unsigned long flags;
676
677 if (!cpu_class_is_omap2())
678 return;
679
680 spin_lock_irqsave(&dma_chan_lock, flags);
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700681 /* Disable interrupt */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800682 val = p->dma_read(IRQENABLE_L0, lch);
Mika Westerbergada8d4a2010-05-14 12:05:25 -0700683 val &= ~(1 << lch);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800684 p->dma_write(val, IRQENABLE_L0, lch);
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700685 /* clear IRQ STATUS */
686 p->dma_write(1 << lch, IRQSTATUS_L0, lch);
Mika Westerbergada8d4a2010-05-14 12:05:25 -0700687 spin_unlock_irqrestore(&dma_chan_lock, flags);
688}
689
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100690int omap_request_dma(int dev_id, const char *dev_name,
Tony Lindgren97b7f712008-07-03 12:24:37 +0300691 void (*callback)(int lch, u16 ch_status, void *data),
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100692 void *data, int *dma_ch_out)
693{
694 int ch, free_ch = -1;
695 unsigned long flags;
696 struct omap_dma_lch *chan;
697
698 spin_lock_irqsave(&dma_chan_lock, flags);
699 for (ch = 0; ch < dma_chan_count; ch++) {
700 if (free_ch == -1 && dma_chan[ch].dev_id == -1) {
701 free_ch = ch;
702 if (dev_id == 0)
703 break;
704 }
705 }
706 if (free_ch == -1) {
707 spin_unlock_irqrestore(&dma_chan_lock, flags);
708 return -EBUSY;
709 }
710 chan = dma_chan + free_ch;
711 chan->dev_id = dev_id;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000712
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800713 if (p->clear_lch_regs)
714 p->clear_lch_regs(free_ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000715
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800716 if (cpu_class_is_omap2())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000717 omap_clear_dma(free_ch);
718
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100719 spin_unlock_irqrestore(&dma_chan_lock, flags);
720
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100721 chan->dev_name = dev_name;
722 chan->callback = callback;
723 chan->data = data;
Jarkko Nikulaa92fda12009-01-29 08:57:12 -0800724 chan->flags = 0;
Tony Lindgren97b7f712008-07-03 12:24:37 +0300725
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800726#ifndef CONFIG_ARCH_OMAP1
Tony Lindgren97b7f712008-07-03 12:24:37 +0300727 if (cpu_class_is_omap2()) {
728 chan->chain_id = -1;
729 chan->next_linked_ch = -1;
730 }
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800731#endif
Tony Lindgren97b7f712008-07-03 12:24:37 +0300732
Tony Lindgren7ff879d2006-06-26 16:16:15 -0700733 chan->enabled_irqs = OMAP_DMA_DROP_IRQ | OMAP_DMA_BLOCK_IRQ;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000734
Tony Lindgren7ff879d2006-06-26 16:16:15 -0700735 if (cpu_class_is_omap1())
736 chan->enabled_irqs |= OMAP1_DMA_TOUT_IRQ;
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800737 else if (cpu_class_is_omap2())
Tony Lindgren7ff879d2006-06-26 16:16:15 -0700738 chan->enabled_irqs |= OMAP2_DMA_MISALIGNED_ERR_IRQ |
739 OMAP2_DMA_TRANS_ERR_IRQ;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100740
741 if (cpu_is_omap16xx()) {
742 /* If the sync device is set, configure it dynamically. */
743 if (dev_id != 0) {
744 set_gdma_dev(free_ch + 1, dev_id);
745 dev_id = free_ch + 1;
746 }
Tony Lindgren97b7f712008-07-03 12:24:37 +0300747 /*
748 * Disable the 1510 compatibility mode and set the sync device
749 * id.
750 */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800751 p->dma_write(dev_id | (1 << 10), CCR, free_ch);
Zebediah C. McClure557096f2009-03-23 18:07:44 -0700752 } else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800753 p->dma_write(dev_id, CCR, free_ch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100754 }
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000755
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800756 if (cpu_class_is_omap2()) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000757 omap_enable_channel_irq(free_ch);
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700758 omap2_enable_irq_lch(free_ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000759 }
760
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100761 *dma_ch_out = free_ch;
762
763 return 0;
764}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300765EXPORT_SYMBOL(omap_request_dma);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100766
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000767void omap_free_dma(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100768{
769 unsigned long flags;
770
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000771 if (dma_chan[lch].dev_id == -1) {
Tony Lindgren97b7f712008-07-03 12:24:37 +0300772 pr_err("omap_dma: trying to free unallocated DMA channel %d\n",
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000773 lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100774 return;
775 }
Tony Lindgren97b7f712008-07-03 12:24:37 +0300776
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700777 /* Disable interrupt for logical channel */
778 if (cpu_class_is_omap2())
Mika Westerbergada8d4a2010-05-14 12:05:25 -0700779 omap2_disable_irq_lch(lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000780
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700781 /* Disable all DMA interrupts for the channel. */
782 omap_disable_channel_irq(lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000783
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700784 /* Make sure the DMA transfer is stopped. */
785 p->dma_write(0, CCR, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000786
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700787 /* Clear registers */
788 if (cpu_class_is_omap2())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000789 omap_clear_dma(lch);
Santosh Shilimkarda1b94e2009-04-23 11:10:40 -0700790
791 spin_lock_irqsave(&dma_chan_lock, flags);
792 dma_chan[lch].dev_id = -1;
793 dma_chan[lch].next_lch = -1;
794 dma_chan[lch].callback = NULL;
795 spin_unlock_irqrestore(&dma_chan_lock, flags);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100796}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300797EXPORT_SYMBOL(omap_free_dma);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100798
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800799/**
800 * @brief omap_dma_set_global_params : Set global priority settings for dma
801 *
802 * @param arb_rate
803 * @param max_fifo_depth
Anuj Aggarwal70cf6442009-10-14 09:56:34 -0700804 * @param tparams - Number of threads to reserve : DMA_THREAD_RESERVE_NORM
805 * DMA_THREAD_RESERVE_ONET
806 * DMA_THREAD_RESERVE_TWOT
807 * DMA_THREAD_RESERVE_THREET
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800808 */
809void
810omap_dma_set_global_params(int arb_rate, int max_fifo_depth, int tparams)
811{
812 u32 reg;
813
814 if (!cpu_class_is_omap2()) {
Harvey Harrison8e86f422008-03-04 15:08:02 -0800815 printk(KERN_ERR "FIXME: no %s on 15xx/16xx\n", __func__);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800816 return;
817 }
818
Anuj Aggarwal70cf6442009-10-14 09:56:34 -0700819 if (max_fifo_depth == 0)
820 max_fifo_depth = 1;
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800821 if (arb_rate == 0)
822 arb_rate = 1;
823
Anuj Aggarwal70cf6442009-10-14 09:56:34 -0700824 reg = 0xff & max_fifo_depth;
825 reg |= (0x3 & tparams) << 12;
826 reg |= (arb_rate & 0xff) << 16;
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800827
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800828 p->dma_write(reg, GCR, 0);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800829}
830EXPORT_SYMBOL(omap_dma_set_global_params);
831
832/**
833 * @brief omap_dma_set_prio_lch : Set channel wise priority settings
834 *
835 * @param lch
836 * @param read_prio - Read priority
837 * @param write_prio - Write priority
838 * Both of the above can be set with one of the following values :
839 * DMA_CH_PRIO_HIGH/DMA_CH_PRIO_LOW
840 */
841int
842omap_dma_set_prio_lch(int lch, unsigned char read_prio,
843 unsigned char write_prio)
844{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300845 u32 l;
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800846
Tony Lindgren4d963722008-07-03 12:24:31 +0300847 if (unlikely((lch < 0 || lch >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800848 printk(KERN_ERR "Invalid channel id\n");
849 return -EINVAL;
850 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800851 l = p->dma_read(CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300852 l &= ~((1 << 6) | (1 << 26));
Santosh Shilimkard07c3df2012-04-28 20:19:10 +0530853 if (cpu_class_is_omap2() && !cpu_is_omap242x())
Tony Lindgren0499bde2008-07-03 12:24:36 +0300854 l |= ((read_prio & 0x1) << 6) | ((write_prio & 0x1) << 26);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800855 else
Tony Lindgren0499bde2008-07-03 12:24:36 +0300856 l |= ((read_prio & 0x1) << 6);
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800857
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800858 p->dma_write(l, CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300859
Anand Gadiyarf8151e52007-12-01 12:14:11 -0800860 return 0;
861}
862EXPORT_SYMBOL(omap_dma_set_prio_lch);
863
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000864/*
865 * Clears any DMA state so the DMA engine is ready to restart with new buffers
866 * through omap_start_dma(). Any buffers in flight are discarded.
867 */
868void omap_clear_dma(int lch)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +0100869{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000870 unsigned long flags;
871
872 local_irq_save(flags);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800873 p->clear_dma(lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000874 local_irq_restore(flags);
875}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300876EXPORT_SYMBOL(omap_clear_dma);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000877
878void omap_start_dma(int lch)
879{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300880 u32 l;
881
manjugk manjugk519e6162010-03-04 07:11:56 +0000882 /*
883 * The CPC/CDAC register needs to be initialized to zero
884 * before starting dma transfer.
885 */
886 if (cpu_is_omap15xx())
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800887 p->dma_write(0, CPC, lch);
manjugk manjugk519e6162010-03-04 07:11:56 +0000888 else
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800889 p->dma_write(0, CDAC, lch);
manjugk manjugk519e6162010-03-04 07:11:56 +0000890
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000891 if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
892 int next_lch, cur_lch;
Paul Walmsleybc4d8b52012-04-13 06:34:30 -0600893 char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT];
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000894
895 dma_chan_link_map[lch] = 1;
896 /* Set the link register of the first channel */
897 enable_lnk(lch);
898
899 memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
900 cur_lch = dma_chan[lch].next_lch;
901 do {
902 next_lch = dma_chan[cur_lch].next_lch;
903
904 /* The loop case: we've been here already */
905 if (dma_chan_link_map[cur_lch])
906 break;
907 /* Mark the current channel */
908 dma_chan_link_map[cur_lch] = 1;
909
910 enable_lnk(cur_lch);
911 omap_enable_channel_irq(cur_lch);
912
913 cur_lch = next_lch;
914 } while (next_lch != -1);
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -0800915 } else if (IS_DMA_ERRATA(DMA_ERRATA_PARALLEL_CHANNELS))
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800916 p->dma_write(lch, CLNK_CTRL, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000917
918 omap_enable_channel_irq(lch);
919
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800920 l = p->dma_read(CCR, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +0300921
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -0800922 if (IS_DMA_ERRATA(DMA_ERRATA_IFRAME_BUFFERING))
923 l |= OMAP_DMA_CCR_BUFFERING_DISABLE;
Tony Lindgren0499bde2008-07-03 12:24:36 +0300924 l |= OMAP_DMA_CCR_EN;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -0800925
Russell King35453582012-04-14 18:57:10 +0100926 /*
927 * As dma_write() uses IO accessors which are weakly ordered, there
928 * is no guarantee that data in coherent DMA memory will be visible
929 * to the DMA device. Add a memory barrier here to ensure that any
930 * such data is visible prior to enabling DMA.
931 */
932 mb();
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800933 p->dma_write(l, CCR, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000934
935 dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
936}
Tony Lindgren97b7f712008-07-03 12:24:37 +0300937EXPORT_SYMBOL(omap_start_dma);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000938
939void omap_stop_dma(int lch)
940{
Tony Lindgren0499bde2008-07-03 12:24:36 +0300941 u32 l;
942
Santosh Shilimkar9da65a92009-10-22 14:46:31 -0700943 /* Disable all interrupts on the channel */
Oleg Matcovschibedfb7a2012-05-15 14:35:08 -0700944 omap_disable_channel_irq(lch);
Santosh Shilimkar9da65a92009-10-22 14:46:31 -0700945
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800946 l = p->dma_read(CCR, lch);
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -0800947 if (IS_DMA_ERRATA(DMA_ERRATA_i541) &&
948 (l & OMAP_DMA_CCR_SEL_SRC_DST_SYNC)) {
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700949 int i = 0;
950 u32 sys_cf;
951
952 /* Configure No-Standby */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800953 l = p->dma_read(OCP_SYSCONFIG, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700954 sys_cf = l;
955 l &= ~DMA_SYSCONFIG_MIDLEMODE_MASK;
956 l |= DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_NO_IDLE);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800957 p->dma_write(l , OCP_SYSCONFIG, 0);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700958
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800959 l = p->dma_read(CCR, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700960 l &= ~OMAP_DMA_CCR_EN;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800961 p->dma_write(l, CCR, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700962
963 /* Wait for sDMA FIFO drain */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800964 l = p->dma_read(CCR, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700965 while (i < 100 && (l & (OMAP_DMA_CCR_RD_ACTIVE |
966 OMAP_DMA_CCR_WR_ACTIVE))) {
967 udelay(5);
968 i++;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800969 l = p->dma_read(CCR, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700970 }
971 if (i >= 100)
Paul Walmsley7852ec02012-07-26 00:54:26 -0600972 pr_err("DMA drain did not complete on lch %d\n", lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700973 /* Restore OCP_SYSCONFIG */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800974 p->dma_write(sys_cf, OCP_SYSCONFIG, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700975 } else {
976 l &= ~OMAP_DMA_CCR_EN;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -0800977 p->dma_write(l, CCR, lch);
Peter Ujfalusi0e4905c2010-10-11 14:18:56 -0700978 }
Santosh Shilimkar9da65a92009-10-22 14:46:31 -0700979
Russell King35453582012-04-14 18:57:10 +0100980 /*
981 * Ensure that data transferred by DMA is visible to any access
982 * after DMA has been disabled. This is important for coherent
983 * DMA regions.
984 */
985 mb();
986
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000987 if (!omap_dma_in_1510_mode() && dma_chan[lch].next_lch != -1) {
988 int next_lch, cur_lch = lch;
Paul Walmsleybc4d8b52012-04-13 06:34:30 -0600989 char dma_chan_link_map[MAX_LOGICAL_DMA_CH_COUNT];
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000990
991 memset(dma_chan_link_map, 0, sizeof(dma_chan_link_map));
992 do {
993 /* The loop case: we've been here already */
994 if (dma_chan_link_map[cur_lch])
995 break;
996 /* Mark the current channel */
997 dma_chan_link_map[cur_lch] = 1;
998
999 disable_lnk(cur_lch);
1000
1001 next_lch = dma_chan[cur_lch].next_lch;
1002 cur_lch = next_lch;
1003 } while (next_lch != -1);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001004 }
1005
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001006 dma_chan[lch].flags &= ~OMAP_DMA_ACTIVE;
1007}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001008EXPORT_SYMBOL(omap_stop_dma);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001009
1010/*
Tony Lindgren709eb3e52006-09-25 12:45:45 +03001011 * Allows changing the DMA callback function or data. This may be needed if
1012 * the driver shares a single DMA channel for multiple dma triggers.
1013 */
1014int omap_set_dma_callback(int lch,
Tony Lindgren97b7f712008-07-03 12:24:37 +03001015 void (*callback)(int lch, u16 ch_status, void *data),
Tony Lindgren709eb3e52006-09-25 12:45:45 +03001016 void *data)
1017{
1018 unsigned long flags;
1019
1020 if (lch < 0)
1021 return -ENODEV;
1022
1023 spin_lock_irqsave(&dma_chan_lock, flags);
1024 if (dma_chan[lch].dev_id == -1) {
1025 printk(KERN_ERR "DMA callback for not set for free channel\n");
1026 spin_unlock_irqrestore(&dma_chan_lock, flags);
1027 return -EINVAL;
1028 }
1029 dma_chan[lch].callback = callback;
1030 dma_chan[lch].data = data;
1031 spin_unlock_irqrestore(&dma_chan_lock, flags);
1032
1033 return 0;
1034}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001035EXPORT_SYMBOL(omap_set_dma_callback);
Tony Lindgren709eb3e52006-09-25 12:45:45 +03001036
1037/*
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001038 * Returns current physical source address for the given DMA channel.
1039 * If the channel is running the caller must disable interrupts prior calling
1040 * this function and process the returned value before re-enabling interrupt to
1041 * prevent races with the interrupt handler. Note that in continuous mode there
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001042 * is a chance for CSSA_L register overflow between the two reads resulting
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001043 * in incorrect return value.
1044 */
1045dma_addr_t omap_get_dma_src_pos(int lch)
1046{
Tony Lindgren0695de32007-05-07 18:24:14 -07001047 dma_addr_t offset = 0;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001048
Tony Lindgren0499bde2008-07-03 12:24:36 +03001049 if (cpu_is_omap15xx())
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001050 offset = p->dma_read(CPC, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001051 else
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001052 offset = p->dma_read(CSAC, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001053
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001054 if (IS_DMA_ERRATA(DMA_ERRATA_3_3) && offset == 0)
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001055 offset = p->dma_read(CSAC, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001056
Peter Ujfalusi7ba96682011-12-09 13:38:00 -08001057 if (!cpu_is_omap15xx()) {
1058 /*
1059 * CDAC == 0 indicates that the DMA transfer on the channel has
1060 * not been started (no data has been transferred so far).
1061 * Return the programmed source start address in this case.
1062 */
1063 if (likely(p->dma_read(CDAC, lch)))
1064 offset = p->dma_read(CSAC, lch);
1065 else
1066 offset = p->dma_read(CSSA, lch);
1067 }
1068
Tony Lindgren0499bde2008-07-03 12:24:36 +03001069 if (cpu_class_is_omap1())
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001070 offset |= (p->dma_read(CSSA, lch) & 0xFFFF0000);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001071
1072 return offset;
1073}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001074EXPORT_SYMBOL(omap_get_dma_src_pos);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001075
1076/*
1077 * Returns current physical destination address for the given DMA channel.
1078 * If the channel is running the caller must disable interrupts prior calling
1079 * this function and process the returned value before re-enabling interrupt to
1080 * prevent races with the interrupt handler. Note that in continuous mode there
Lucas De Marchi25985ed2011-03-30 22:57:33 -03001081 * is a chance for CDSA_L register overflow between the two reads resulting
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001082 * in incorrect return value.
1083 */
1084dma_addr_t omap_get_dma_dst_pos(int lch)
1085{
Tony Lindgren0695de32007-05-07 18:24:14 -07001086 dma_addr_t offset = 0;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001087
Tony Lindgren0499bde2008-07-03 12:24:36 +03001088 if (cpu_is_omap15xx())
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001089 offset = p->dma_read(CPC, lch);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001090 else
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001091 offset = p->dma_read(CDAC, lch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001092
Tony Lindgren0499bde2008-07-03 12:24:36 +03001093 /*
1094 * omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
1095 * read before the DMA controller finished disabling the channel.
1096 */
Peter Ujfalusi06e80772011-12-09 13:38:00 -08001097 if (!cpu_is_omap15xx() && offset == 0) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001098 offset = p->dma_read(CDAC, lch);
Peter Ujfalusi06e80772011-12-09 13:38:00 -08001099 /*
1100 * CDAC == 0 indicates that the DMA transfer on the channel has
1101 * not been started (no data has been transferred so far).
1102 * Return the programmed destination start address in this case.
1103 */
1104 if (unlikely(!offset))
1105 offset = p->dma_read(CDSA, lch);
1106 }
Tony Lindgren0499bde2008-07-03 12:24:36 +03001107
1108 if (cpu_class_is_omap1())
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001109 offset |= (p->dma_read(CDSA, lch) & 0xFFFF0000);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001110
1111 return offset;
1112}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001113EXPORT_SYMBOL(omap_get_dma_dst_pos);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001114
Tony Lindgren0499bde2008-07-03 12:24:36 +03001115int omap_get_dma_active_status(int lch)
1116{
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001117 return (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN) != 0;
Tony Lindgren0499bde2008-07-03 12:24:36 +03001118}
1119EXPORT_SYMBOL(omap_get_dma_active_status);
1120
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001121int omap_dma_running(void)
1122{
1123 int lch;
1124
Janusz Krzysztofikf8e9e982009-12-11 16:16:33 -08001125 if (cpu_class_is_omap1())
1126 if (omap_lcd_dma_running())
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001127 return 1;
1128
1129 for (lch = 0; lch < dma_chan_count; lch++)
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001130 if (p->dma_read(CCR, lch) & OMAP_DMA_CCR_EN)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001131 return 1;
1132
1133 return 0;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001134}
1135
1136/*
1137 * lch_queue DMA will start right after lch_head one is finished.
1138 * For this DMA link to start, you still need to start (see omap_start_dma)
1139 * the first one. That will fire up the entire queue.
1140 */
Tony Lindgren97b7f712008-07-03 12:24:37 +03001141void omap_dma_link_lch(int lch_head, int lch_queue)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001142{
1143 if (omap_dma_in_1510_mode()) {
Janusz Krzysztofik9f0f4ae2009-08-23 17:56:12 +02001144 if (lch_head == lch_queue) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001145 p->dma_write(p->dma_read(CCR, lch_head) | (3 << 8),
G, Manjunath Kondaiaha4c537c2010-12-20 18:27:17 -08001146 CCR, lch_head);
Janusz Krzysztofik9f0f4ae2009-08-23 17:56:12 +02001147 return;
1148 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001149 printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
1150 BUG();
1151 return;
1152 }
1153
1154 if ((dma_chan[lch_head].dev_id == -1) ||
1155 (dma_chan[lch_queue].dev_id == -1)) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06001156 pr_err("omap_dma: trying to link non requested channels\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001157 dump_stack();
1158 }
1159
1160 dma_chan[lch_head].next_lch = lch_queue;
1161}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001162EXPORT_SYMBOL(omap_dma_link_lch);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001163
1164/*
1165 * Once the DMA queue is stopped, we can destroy it.
1166 */
Tony Lindgren97b7f712008-07-03 12:24:37 +03001167void omap_dma_unlink_lch(int lch_head, int lch_queue)
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001168{
1169 if (omap_dma_in_1510_mode()) {
Janusz Krzysztofik9f0f4ae2009-08-23 17:56:12 +02001170 if (lch_head == lch_queue) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001171 p->dma_write(p->dma_read(CCR, lch_head) & ~(3 << 8),
G, Manjunath Kondaiaha4c537c2010-12-20 18:27:17 -08001172 CCR, lch_head);
Janusz Krzysztofik9f0f4ae2009-08-23 17:56:12 +02001173 return;
1174 }
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001175 printk(KERN_ERR "DMA linking is not supported in 1510 mode\n");
1176 BUG();
1177 return;
1178 }
1179
1180 if (dma_chan[lch_head].next_lch != lch_queue ||
1181 dma_chan[lch_head].next_lch == -1) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06001182 pr_err("omap_dma: trying to unlink non linked channels\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001183 dump_stack();
1184 }
1185
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001186 if ((dma_chan[lch_head].flags & OMAP_DMA_ACTIVE) ||
Roel Kluin247421f2010-01-13 18:10:29 -08001187 (dma_chan[lch_queue].flags & OMAP_DMA_ACTIVE)) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06001188 pr_err("omap_dma: You need to stop the DMA channels before unlinking\n");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001189 dump_stack();
1190 }
1191
1192 dma_chan[lch_head].next_lch = -1;
1193}
Tony Lindgren97b7f712008-07-03 12:24:37 +03001194EXPORT_SYMBOL(omap_dma_unlink_lch);
1195
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001196#ifndef CONFIG_ARCH_OMAP1
1197/* Create chain of DMA channesls */
1198static void create_dma_lch_chain(int lch_head, int lch_queue)
1199{
Tony Lindgren0499bde2008-07-03 12:24:36 +03001200 u32 l;
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001201
1202 /* Check if this is the first link in chain */
1203 if (dma_chan[lch_head].next_linked_ch == -1) {
1204 dma_chan[lch_head].next_linked_ch = lch_queue;
1205 dma_chan[lch_head].prev_linked_ch = lch_queue;
1206 dma_chan[lch_queue].next_linked_ch = lch_head;
1207 dma_chan[lch_queue].prev_linked_ch = lch_head;
1208 }
1209
1210 /* a link exists, link the new channel in circular chain */
1211 else {
1212 dma_chan[lch_queue].next_linked_ch =
1213 dma_chan[lch_head].next_linked_ch;
1214 dma_chan[lch_queue].prev_linked_ch = lch_head;
1215 dma_chan[lch_head].next_linked_ch = lch_queue;
1216 dma_chan[dma_chan[lch_queue].next_linked_ch].prev_linked_ch =
1217 lch_queue;
1218 }
1219
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001220 l = p->dma_read(CLNK_CTRL, lch_head);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001221 l &= ~(0x1f);
1222 l |= lch_queue;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001223 p->dma_write(l, CLNK_CTRL, lch_head);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001224
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001225 l = p->dma_read(CLNK_CTRL, lch_queue);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001226 l &= ~(0x1f);
1227 l |= (dma_chan[lch_queue].next_linked_ch);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001228 p->dma_write(l, CLNK_CTRL, lch_queue);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001229}
1230
1231/**
1232 * @brief omap_request_dma_chain : Request a chain of DMA channels
1233 *
1234 * @param dev_id - Device id using the dma channel
1235 * @param dev_name - Device name
1236 * @param callback - Call back function
1237 * @chain_id -
1238 * @no_of_chans - Number of channels requested
1239 * @chain_mode - Dynamic or static chaining : OMAP_DMA_STATIC_CHAIN
1240 * OMAP_DMA_DYNAMIC_CHAIN
1241 * @params - Channel parameters
1242 *
André Goddard Rosaaf901ca2009-11-14 13:09:05 -02001243 * @return - Success : 0
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001244 * Failure: -EINVAL/-ENOMEM
1245 */
1246int omap_request_dma_chain(int dev_id, const char *dev_name,
Santosh Shilimkar279b9182009-05-28 13:23:52 -07001247 void (*callback) (int lch, u16 ch_status,
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001248 void *data),
1249 int *chain_id, int no_of_chans, int chain_mode,
1250 struct omap_dma_channel_params params)
1251{
1252 int *channels;
1253 int i, err;
1254
1255 /* Is the chain mode valid ? */
1256 if (chain_mode != OMAP_DMA_STATIC_CHAIN
1257 && chain_mode != OMAP_DMA_DYNAMIC_CHAIN) {
1258 printk(KERN_ERR "Invalid chain mode requested\n");
1259 return -EINVAL;
1260 }
1261
1262 if (unlikely((no_of_chans < 1
Tony Lindgren4d963722008-07-03 12:24:31 +03001263 || no_of_chans > dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001264 printk(KERN_ERR "Invalid Number of channels requested\n");
1265 return -EINVAL;
1266 }
1267
manjugk manjugkea221a62010-05-14 12:05:25 -07001268 /*
1269 * Allocate a queue to maintain the status of the channels
1270 * in the chain
1271 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001272 channels = kmalloc(sizeof(*channels) * no_of_chans, GFP_KERNEL);
1273 if (channels == NULL) {
1274 printk(KERN_ERR "omap_dma: No memory for channel queue\n");
1275 return -ENOMEM;
1276 }
1277
1278 /* request and reserve DMA channels for the chain */
1279 for (i = 0; i < no_of_chans; i++) {
1280 err = omap_request_dma(dev_id, dev_name,
Russell Kingc0fc18c52008-09-05 15:10:27 +01001281 callback, NULL, &channels[i]);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001282 if (err < 0) {
1283 int j;
1284 for (j = 0; j < i; j++)
1285 omap_free_dma(channels[j]);
1286 kfree(channels);
1287 printk(KERN_ERR "omap_dma: Request failed %d\n", err);
1288 return err;
1289 }
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001290 dma_chan[channels[i]].prev_linked_ch = -1;
1291 dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
1292
1293 /*
1294 * Allowing client drivers to set common parameters now,
1295 * so that later only relevant (src_start, dest_start
1296 * and element count) can be set
1297 */
1298 omap_set_dma_params(channels[i], &params);
1299 }
1300
1301 *chain_id = channels[0];
1302 dma_linked_lch[*chain_id].linked_dmach_q = channels;
1303 dma_linked_lch[*chain_id].chain_mode = chain_mode;
1304 dma_linked_lch[*chain_id].chain_state = DMA_CHAIN_NOTSTARTED;
1305 dma_linked_lch[*chain_id].no_of_lchs_linked = no_of_chans;
1306
1307 for (i = 0; i < no_of_chans; i++)
1308 dma_chan[channels[i]].chain_id = *chain_id;
1309
1310 /* Reset the Queue pointers */
1311 OMAP_DMA_CHAIN_QINIT(*chain_id);
1312
1313 /* Set up the chain */
1314 if (no_of_chans == 1)
1315 create_dma_lch_chain(channels[0], channels[0]);
1316 else {
1317 for (i = 0; i < (no_of_chans - 1); i++)
1318 create_dma_lch_chain(channels[i], channels[i + 1]);
1319 }
Tony Lindgren97b7f712008-07-03 12:24:37 +03001320
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001321 return 0;
1322}
1323EXPORT_SYMBOL(omap_request_dma_chain);
1324
1325/**
1326 * @brief omap_modify_dma_chain_param : Modify the chain's params - Modify the
1327 * params after setting it. Dont do this while dma is running!!
1328 *
1329 * @param chain_id - Chained logical channel id.
1330 * @param params
1331 *
1332 * @return - Success : 0
1333 * Failure : -EINVAL
1334 */
1335int omap_modify_dma_chain_params(int chain_id,
1336 struct omap_dma_channel_params params)
1337{
1338 int *channels;
1339 u32 i;
1340
1341 /* Check for input params */
1342 if (unlikely((chain_id < 0
Tony Lindgren4d963722008-07-03 12:24:31 +03001343 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001344 printk(KERN_ERR "Invalid chain id\n");
1345 return -EINVAL;
1346 }
1347
1348 /* Check if the chain exists */
1349 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1350 printk(KERN_ERR "Chain doesn't exists\n");
1351 return -EINVAL;
1352 }
1353 channels = dma_linked_lch[chain_id].linked_dmach_q;
1354
1355 for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
1356 /*
1357 * Allowing client drivers to set common parameters now,
1358 * so that later only relevant (src_start, dest_start
1359 * and element count) can be set
1360 */
1361 omap_set_dma_params(channels[i], &params);
1362 }
Tony Lindgren97b7f712008-07-03 12:24:37 +03001363
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001364 return 0;
1365}
1366EXPORT_SYMBOL(omap_modify_dma_chain_params);
1367
1368/**
1369 * @brief omap_free_dma_chain - Free all the logical channels in a chain.
1370 *
1371 * @param chain_id
1372 *
1373 * @return - Success : 0
1374 * Failure : -EINVAL
1375 */
1376int omap_free_dma_chain(int chain_id)
1377{
1378 int *channels;
1379 u32 i;
1380
1381 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001382 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001383 printk(KERN_ERR "Invalid chain id\n");
1384 return -EINVAL;
1385 }
1386
1387 /* Check if the chain exists */
1388 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1389 printk(KERN_ERR "Chain doesn't exists\n");
1390 return -EINVAL;
1391 }
1392
1393 channels = dma_linked_lch[chain_id].linked_dmach_q;
1394 for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
1395 dma_chan[channels[i]].next_linked_ch = -1;
1396 dma_chan[channels[i]].prev_linked_ch = -1;
1397 dma_chan[channels[i]].chain_id = -1;
1398 dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
1399 omap_free_dma(channels[i]);
1400 }
1401
1402 kfree(channels);
1403
1404 dma_linked_lch[chain_id].linked_dmach_q = NULL;
1405 dma_linked_lch[chain_id].chain_mode = -1;
1406 dma_linked_lch[chain_id].chain_state = -1;
Tony Lindgren97b7f712008-07-03 12:24:37 +03001407
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001408 return (0);
1409}
1410EXPORT_SYMBOL(omap_free_dma_chain);
1411
1412/**
1413 * @brief omap_dma_chain_status - Check if the chain is in
1414 * active / inactive state.
1415 * @param chain_id
1416 *
1417 * @return - Success : OMAP_DMA_CHAIN_ACTIVE/OMAP_DMA_CHAIN_INACTIVE
1418 * Failure : -EINVAL
1419 */
1420int omap_dma_chain_status(int chain_id)
1421{
1422 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001423 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001424 printk(KERN_ERR "Invalid chain id\n");
1425 return -EINVAL;
1426 }
1427
1428 /* Check if the chain exists */
1429 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1430 printk(KERN_ERR "Chain doesn't exists\n");
1431 return -EINVAL;
1432 }
1433 pr_debug("CHAINID=%d, qcnt=%d\n", chain_id,
1434 dma_linked_lch[chain_id].q_count);
1435
1436 if (OMAP_DMA_CHAIN_QEMPTY(chain_id))
1437 return OMAP_DMA_CHAIN_INACTIVE;
Tony Lindgren97b7f712008-07-03 12:24:37 +03001438
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001439 return OMAP_DMA_CHAIN_ACTIVE;
1440}
1441EXPORT_SYMBOL(omap_dma_chain_status);
1442
1443/**
1444 * @brief omap_dma_chain_a_transfer - Get a free channel from a chain,
1445 * set the params and start the transfer.
1446 *
1447 * @param chain_id
1448 * @param src_start - buffer start address
1449 * @param dest_start - Dest address
1450 * @param elem_count
1451 * @param frame_count
1452 * @param callbk_data - channel callback parameter data.
1453 *
Anand Gadiyarf4b6a7e2008-03-11 01:10:35 +05301454 * @return - Success : 0
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001455 * Failure: -EINVAL/-EBUSY
1456 */
1457int omap_dma_chain_a_transfer(int chain_id, int src_start, int dest_start,
1458 int elem_count, int frame_count, void *callbk_data)
1459{
1460 int *channels;
Tony Lindgren0499bde2008-07-03 12:24:36 +03001461 u32 l, lch;
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001462 int start_dma = 0;
1463
Tony Lindgren97b7f712008-07-03 12:24:37 +03001464 /*
1465 * if buffer size is less than 1 then there is
1466 * no use of starting the chain
1467 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001468 if (elem_count < 1) {
1469 printk(KERN_ERR "Invalid buffer size\n");
1470 return -EINVAL;
1471 }
1472
1473 /* Check for input params */
1474 if (unlikely((chain_id < 0
Tony Lindgren4d963722008-07-03 12:24:31 +03001475 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001476 printk(KERN_ERR "Invalid chain id\n");
1477 return -EINVAL;
1478 }
1479
1480 /* Check if the chain exists */
1481 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1482 printk(KERN_ERR "Chain doesn't exist\n");
1483 return -EINVAL;
1484 }
1485
1486 /* Check if all the channels in chain are in use */
1487 if (OMAP_DMA_CHAIN_QFULL(chain_id))
1488 return -EBUSY;
1489
1490 /* Frame count may be negative in case of indexed transfers */
1491 channels = dma_linked_lch[chain_id].linked_dmach_q;
1492
1493 /* Get a free channel */
1494 lch = channels[dma_linked_lch[chain_id].q_tail];
1495
1496 /* Store the callback data */
1497 dma_chan[lch].data = callbk_data;
1498
1499 /* Increment the q_tail */
1500 OMAP_DMA_CHAIN_INCQTAIL(chain_id);
1501
1502 /* Set the params to the free channel */
1503 if (src_start != 0)
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001504 p->dma_write(src_start, CSSA, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001505 if (dest_start != 0)
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001506 p->dma_write(dest_start, CDSA, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001507
1508 /* Write the buffer size */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001509 p->dma_write(elem_count, CEN, lch);
1510 p->dma_write(frame_count, CFN, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001511
Tony Lindgren97b7f712008-07-03 12:24:37 +03001512 /*
1513 * If the chain is dynamically linked,
1514 * then we may have to start the chain if its not active
1515 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001516 if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_DYNAMIC_CHAIN) {
1517
Tony Lindgren97b7f712008-07-03 12:24:37 +03001518 /*
1519 * In Dynamic chain, if the chain is not started,
1520 * queue the channel
1521 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001522 if (dma_linked_lch[chain_id].chain_state ==
1523 DMA_CHAIN_NOTSTARTED) {
1524 /* Enable the link in previous channel */
1525 if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
1526 DMA_CH_QUEUED)
1527 enable_lnk(dma_chan[lch].prev_linked_ch);
1528 dma_chan[lch].state = DMA_CH_QUEUED;
1529 }
1530
Tony Lindgren97b7f712008-07-03 12:24:37 +03001531 /*
1532 * Chain is already started, make sure its active,
1533 * if not then start the chain
1534 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001535 else {
1536 start_dma = 1;
1537
1538 if (dma_chan[dma_chan[lch].prev_linked_ch].state ==
1539 DMA_CH_STARTED) {
1540 enable_lnk(dma_chan[lch].prev_linked_ch);
1541 dma_chan[lch].state = DMA_CH_QUEUED;
1542 start_dma = 0;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001543 if (0 == ((1 << 7) & p->dma_read(
G, Manjunath Kondaiaha4c537c2010-12-20 18:27:17 -08001544 CCR, dma_chan[lch].prev_linked_ch))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001545 disable_lnk(dma_chan[lch].
1546 prev_linked_ch);
1547 pr_debug("\n prev ch is stopped\n");
1548 start_dma = 1;
1549 }
1550 }
1551
1552 else if (dma_chan[dma_chan[lch].prev_linked_ch].state
1553 == DMA_CH_QUEUED) {
1554 enable_lnk(dma_chan[lch].prev_linked_ch);
1555 dma_chan[lch].state = DMA_CH_QUEUED;
1556 start_dma = 0;
1557 }
1558 omap_enable_channel_irq(lch);
1559
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001560 l = p->dma_read(CCR, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001561
Tony Lindgren0499bde2008-07-03 12:24:36 +03001562 if ((0 == (l & (1 << 24))))
1563 l &= ~(1 << 25);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001564 else
Tony Lindgren0499bde2008-07-03 12:24:36 +03001565 l |= (1 << 25);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001566 if (start_dma == 1) {
Tony Lindgren0499bde2008-07-03 12:24:36 +03001567 if (0 == (l & (1 << 7))) {
1568 l |= (1 << 7);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001569 dma_chan[lch].state = DMA_CH_STARTED;
1570 pr_debug("starting %d\n", lch);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001571 p->dma_write(l, CCR, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001572 } else
1573 start_dma = 0;
1574 } else {
Tony Lindgren0499bde2008-07-03 12:24:36 +03001575 if (0 == (l & (1 << 7)))
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001576 p->dma_write(l, CCR, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001577 }
1578 dma_chan[lch].flags |= OMAP_DMA_ACTIVE;
1579 }
1580 }
Tony Lindgren97b7f712008-07-03 12:24:37 +03001581
Anand Gadiyarf4b6a7e2008-03-11 01:10:35 +05301582 return 0;
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001583}
1584EXPORT_SYMBOL(omap_dma_chain_a_transfer);
1585
1586/**
1587 * @brief omap_start_dma_chain_transfers - Start the chain
1588 *
1589 * @param chain_id
1590 *
1591 * @return - Success : 0
1592 * Failure : -EINVAL/-EBUSY
1593 */
1594int omap_start_dma_chain_transfers(int chain_id)
1595{
1596 int *channels;
Tony Lindgren0499bde2008-07-03 12:24:36 +03001597 u32 l, i;
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001598
Tony Lindgren4d963722008-07-03 12:24:31 +03001599 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001600 printk(KERN_ERR "Invalid chain id\n");
1601 return -EINVAL;
1602 }
1603
1604 channels = dma_linked_lch[chain_id].linked_dmach_q;
1605
1606 if (dma_linked_lch[channels[0]].chain_state == DMA_CHAIN_STARTED) {
1607 printk(KERN_ERR "Chain is already started\n");
1608 return -EBUSY;
1609 }
1610
1611 if (dma_linked_lch[chain_id].chain_mode == OMAP_DMA_STATIC_CHAIN) {
1612 for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked;
1613 i++) {
1614 enable_lnk(channels[i]);
1615 omap_enable_channel_irq(channels[i]);
1616 }
1617 } else {
1618 omap_enable_channel_irq(channels[0]);
1619 }
1620
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001621 l = p->dma_read(CCR, channels[0]);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001622 l |= (1 << 7);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001623 dma_linked_lch[chain_id].chain_state = DMA_CHAIN_STARTED;
1624 dma_chan[channels[0]].state = DMA_CH_STARTED;
1625
Tony Lindgren0499bde2008-07-03 12:24:36 +03001626 if ((0 == (l & (1 << 24))))
1627 l &= ~(1 << 25);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001628 else
Tony Lindgren0499bde2008-07-03 12:24:36 +03001629 l |= (1 << 25);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001630 p->dma_write(l, CCR, channels[0]);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001631
1632 dma_chan[channels[0]].flags |= OMAP_DMA_ACTIVE;
Tony Lindgren97b7f712008-07-03 12:24:37 +03001633
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001634 return 0;
1635}
1636EXPORT_SYMBOL(omap_start_dma_chain_transfers);
1637
1638/**
1639 * @brief omap_stop_dma_chain_transfers - Stop the dma transfer of a chain.
1640 *
1641 * @param chain_id
1642 *
1643 * @return - Success : 0
1644 * Failure : EINVAL
1645 */
1646int omap_stop_dma_chain_transfers(int chain_id)
1647{
1648 int *channels;
Tony Lindgren0499bde2008-07-03 12:24:36 +03001649 u32 l, i;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001650 u32 sys_cf = 0;
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001651
1652 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001653 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001654 printk(KERN_ERR "Invalid chain id\n");
1655 return -EINVAL;
1656 }
1657
1658 /* Check if the chain exists */
1659 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1660 printk(KERN_ERR "Chain doesn't exists\n");
1661 return -EINVAL;
1662 }
1663 channels = dma_linked_lch[chain_id].linked_dmach_q;
1664
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001665 if (IS_DMA_ERRATA(DMA_ERRATA_i88)) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001666 sys_cf = p->dma_read(OCP_SYSCONFIG, 0);
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001667 l = sys_cf;
1668 /* Middle mode reg set no Standby */
1669 l &= ~((1 << 12)|(1 << 13));
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001670 p->dma_write(l, OCP_SYSCONFIG, 0);
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001671 }
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001672
1673 for (i = 0; i < dma_linked_lch[chain_id].no_of_lchs_linked; i++) {
1674
1675 /* Stop the Channel transmission */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001676 l = p->dma_read(CCR, channels[i]);
Tony Lindgren0499bde2008-07-03 12:24:36 +03001677 l &= ~(1 << 7);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001678 p->dma_write(l, CCR, channels[i]);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001679
1680 /* Disable the link in all the channels */
1681 disable_lnk(channels[i]);
1682 dma_chan[channels[i]].state = DMA_CH_NOTSTARTED;
1683
1684 }
1685 dma_linked_lch[chain_id].chain_state = DMA_CHAIN_NOTSTARTED;
1686
1687 /* Reset the Queue pointers */
1688 OMAP_DMA_CHAIN_QINIT(chain_id);
1689
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001690 if (IS_DMA_ERRATA(DMA_ERRATA_i88))
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001691 p->dma_write(sys_cf, OCP_SYSCONFIG, 0);
Tony Lindgren97b7f712008-07-03 12:24:37 +03001692
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001693 return 0;
1694}
1695EXPORT_SYMBOL(omap_stop_dma_chain_transfers);
1696
1697/* Get the index of the ongoing DMA in chain */
1698/**
1699 * @brief omap_get_dma_chain_index - Get the element and frame index
1700 * of the ongoing DMA in chain
1701 *
1702 * @param chain_id
1703 * @param ei - Element index
1704 * @param fi - Frame index
1705 *
1706 * @return - Success : 0
1707 * Failure : -EINVAL
1708 */
1709int omap_get_dma_chain_index(int chain_id, int *ei, int *fi)
1710{
1711 int lch;
1712 int *channels;
1713
1714 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001715 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001716 printk(KERN_ERR "Invalid chain id\n");
1717 return -EINVAL;
1718 }
1719
1720 /* Check if the chain exists */
1721 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1722 printk(KERN_ERR "Chain doesn't exists\n");
1723 return -EINVAL;
1724 }
1725 if ((!ei) || (!fi))
1726 return -EINVAL;
1727
1728 channels = dma_linked_lch[chain_id].linked_dmach_q;
1729
1730 /* Get the current channel */
1731 lch = channels[dma_linked_lch[chain_id].q_head];
1732
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001733 *ei = p->dma_read(CCEN, lch);
1734 *fi = p->dma_read(CCFN, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001735
1736 return 0;
1737}
1738EXPORT_SYMBOL(omap_get_dma_chain_index);
1739
1740/**
1741 * @brief omap_get_dma_chain_dst_pos - Get the destination position of the
1742 * ongoing DMA in chain
1743 *
1744 * @param chain_id
1745 *
1746 * @return - Success : Destination position
1747 * Failure : -EINVAL
1748 */
1749int omap_get_dma_chain_dst_pos(int chain_id)
1750{
1751 int lch;
1752 int *channels;
1753
1754 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001755 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001756 printk(KERN_ERR "Invalid chain id\n");
1757 return -EINVAL;
1758 }
1759
1760 /* Check if the chain exists */
1761 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1762 printk(KERN_ERR "Chain doesn't exists\n");
1763 return -EINVAL;
1764 }
1765
1766 channels = dma_linked_lch[chain_id].linked_dmach_q;
1767
1768 /* Get the current channel */
1769 lch = channels[dma_linked_lch[chain_id].q_head];
1770
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001771 return p->dma_read(CDAC, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001772}
1773EXPORT_SYMBOL(omap_get_dma_chain_dst_pos);
1774
1775/**
1776 * @brief omap_get_dma_chain_src_pos - Get the source position
1777 * of the ongoing DMA in chain
1778 * @param chain_id
1779 *
1780 * @return - Success : Destination position
1781 * Failure : -EINVAL
1782 */
1783int omap_get_dma_chain_src_pos(int chain_id)
1784{
1785 int lch;
1786 int *channels;
1787
1788 /* Check for input params */
Tony Lindgren4d963722008-07-03 12:24:31 +03001789 if (unlikely((chain_id < 0 || chain_id >= dma_lch_count))) {
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001790 printk(KERN_ERR "Invalid chain id\n");
1791 return -EINVAL;
1792 }
1793
1794 /* Check if the chain exists */
1795 if (dma_linked_lch[chain_id].linked_dmach_q == NULL) {
1796 printk(KERN_ERR "Chain doesn't exists\n");
1797 return -EINVAL;
1798 }
1799
1800 channels = dma_linked_lch[chain_id].linked_dmach_q;
1801
1802 /* Get the current channel */
1803 lch = channels[dma_linked_lch[chain_id].q_head];
1804
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001805 return p->dma_read(CSAC, lch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001806}
1807EXPORT_SYMBOL(omap_get_dma_chain_src_pos);
Tony Lindgren97b7f712008-07-03 12:24:37 +03001808#endif /* ifndef CONFIG_ARCH_OMAP1 */
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001809
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001810/*----------------------------------------------------------------------------*/
1811
1812#ifdef CONFIG_ARCH_OMAP1
1813
1814static int omap1_dma_handle_ch(int ch)
1815{
Tony Lindgren0499bde2008-07-03 12:24:36 +03001816 u32 csr;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001817
1818 if (enable_1510_mode && ch >= 6) {
1819 csr = dma_chan[ch].saved_csr;
1820 dma_chan[ch].saved_csr = 0;
1821 } else
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001822 csr = p->dma_read(CSR, ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001823 if (enable_1510_mode && ch <= 2 && (csr >> 7) != 0) {
1824 dma_chan[ch + 6].saved_csr = csr >> 7;
1825 csr &= 0x7f;
1826 }
1827 if ((csr & 0x3f) == 0)
1828 return 0;
1829 if (unlikely(dma_chan[ch].dev_id == -1)) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06001830 pr_warn("Spurious interrupt from DMA channel %d (CSR %04x)\n",
1831 ch, csr);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001832 return 0;
1833 }
Tony Lindgren7ff879d2006-06-26 16:16:15 -07001834 if (unlikely(csr & OMAP1_DMA_TOUT_IRQ))
Paul Walmsley7852ec02012-07-26 00:54:26 -06001835 pr_warn("DMA timeout with device %d\n", dma_chan[ch].dev_id);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001836 if (unlikely(csr & OMAP_DMA_DROP_IRQ))
Paul Walmsley7852ec02012-07-26 00:54:26 -06001837 pr_warn("DMA synchronization event drop occurred with device %d\n",
1838 dma_chan[ch].dev_id);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001839 if (likely(csr & OMAP_DMA_BLOCK_IRQ))
1840 dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
1841 if (likely(dma_chan[ch].callback != NULL))
1842 dma_chan[ch].callback(ch, csr, dma_chan[ch].data);
Tony Lindgren97b7f712008-07-03 12:24:37 +03001843
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001844 return 1;
1845}
1846
Linus Torvalds0cd61b62006-10-06 10:53:39 -07001847static irqreturn_t omap1_dma_irq_handler(int irq, void *dev_id)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001848{
1849 int ch = ((int) dev_id) - 1;
1850 int handled = 0;
1851
1852 for (;;) {
1853 int handled_now = 0;
1854
1855 handled_now += omap1_dma_handle_ch(ch);
1856 if (enable_1510_mode && dma_chan[ch + 6].saved_csr)
1857 handled_now += omap1_dma_handle_ch(ch + 6);
1858 if (!handled_now)
1859 break;
1860 handled += handled_now;
1861 }
1862
1863 return handled ? IRQ_HANDLED : IRQ_NONE;
1864}
1865
1866#else
1867#define omap1_dma_irq_handler NULL
1868#endif
1869
Tony Lindgren140455f2010-02-12 12:26:48 -08001870#ifdef CONFIG_ARCH_OMAP2PLUS
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001871
1872static int omap2_dma_handle_ch(int ch)
1873{
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001874 u32 status = p->dma_read(CSR, ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001875
Juha Yrjola31513692006-12-06 17:13:47 -08001876 if (!status) {
1877 if (printk_ratelimit())
Paul Walmsley7852ec02012-07-26 00:54:26 -06001878 pr_warn("Spurious DMA IRQ for lch %d\n", ch);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001879 p->dma_write(1 << ch, IRQSTATUS_L0, ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001880 return 0;
Juha Yrjola31513692006-12-06 17:13:47 -08001881 }
1882 if (unlikely(dma_chan[ch].dev_id == -1)) {
1883 if (printk_ratelimit())
Paul Walmsley7852ec02012-07-26 00:54:26 -06001884 pr_warn("IRQ %04x for non-allocated DMA channel %d\n",
1885 status, ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001886 return 0;
Juha Yrjola31513692006-12-06 17:13:47 -08001887 }
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001888 if (unlikely(status & OMAP_DMA_DROP_IRQ))
Paul Walmsley7852ec02012-07-26 00:54:26 -06001889 pr_info("DMA synchronization event drop occurred with device %d\n",
1890 dma_chan[ch].dev_id);
Santosh Shilimkara50f18c2008-12-10 17:36:53 -08001891 if (unlikely(status & OMAP2_DMA_TRANS_ERR_IRQ)) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001892 printk(KERN_INFO "DMA transaction error with device %d\n",
1893 dma_chan[ch].dev_id);
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001894 if (IS_DMA_ERRATA(DMA_ERRATA_i378)) {
Santosh Shilimkara50f18c2008-12-10 17:36:53 -08001895 u32 ccr;
1896
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001897 ccr = p->dma_read(CCR, ch);
Santosh Shilimkara50f18c2008-12-10 17:36:53 -08001898 ccr &= ~OMAP_DMA_CCR_EN;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001899 p->dma_write(ccr, CCR, ch);
Santosh Shilimkara50f18c2008-12-10 17:36:53 -08001900 dma_chan[ch].flags &= ~OMAP_DMA_ACTIVE;
1901 }
1902 }
Tony Lindgren7ff879d2006-06-26 16:16:15 -07001903 if (unlikely(status & OMAP2_DMA_SECURE_ERR_IRQ))
1904 printk(KERN_INFO "DMA secure error with device %d\n",
1905 dma_chan[ch].dev_id);
1906 if (unlikely(status & OMAP2_DMA_MISALIGNED_ERR_IRQ))
1907 printk(KERN_INFO "DMA misaligned error with device %d\n",
1908 dma_chan[ch].dev_id);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001909
Adrian Hunter4fb699b2010-11-24 13:23:21 +02001910 p->dma_write(status, CSR, ch);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001911 p->dma_write(1 << ch, IRQSTATUS_L0, ch);
Mathias Nymane860e6d2010-10-25 14:35:24 +00001912 /* read back the register to flush the write */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001913 p->dma_read(IRQSTATUS_L0, ch);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001914
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001915 /* If the ch is not chained then chain_id will be -1 */
1916 if (dma_chan[ch].chain_id != -1) {
1917 int chain_id = dma_chan[ch].chain_id;
1918 dma_chan[ch].state = DMA_CH_NOTSTARTED;
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001919 if (p->dma_read(CLNK_CTRL, ch) & (1 << 15))
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001920 dma_chan[dma_chan[ch].next_linked_ch].state =
1921 DMA_CH_STARTED;
1922 if (dma_linked_lch[chain_id].chain_mode ==
1923 OMAP_DMA_DYNAMIC_CHAIN)
1924 disable_lnk(ch);
1925
1926 if (!OMAP_DMA_CHAIN_QEMPTY(chain_id))
1927 OMAP_DMA_CHAIN_INCQHEAD(chain_id);
1928
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001929 status = p->dma_read(CSR, ch);
Adrian Hunter4fb699b2010-11-24 13:23:21 +02001930 p->dma_write(status, CSR, ch);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001931 }
1932
Jarkko Nikula538528d2008-02-13 11:47:29 +02001933 if (likely(dma_chan[ch].callback != NULL))
1934 dma_chan[ch].callback(ch, status, dma_chan[ch].data);
Anand Gadiyarf8151e52007-12-01 12:14:11 -08001935
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001936 return 0;
1937}
1938
1939/* STATUS register count is from 1-32 while our is 0-31 */
Linus Torvalds0cd61b62006-10-06 10:53:39 -07001940static irqreturn_t omap2_dma_irq_handler(int irq, void *dev_id)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001941{
Santosh Shilimkar52176e72009-03-23 18:07:49 -07001942 u32 val, enable_reg;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001943 int i;
1944
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001945 val = p->dma_read(IRQSTATUS_L0, 0);
Juha Yrjola31513692006-12-06 17:13:47 -08001946 if (val == 0) {
1947 if (printk_ratelimit())
1948 printk(KERN_WARNING "Spurious DMA IRQ\n");
1949 return IRQ_HANDLED;
1950 }
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001951 enable_reg = p->dma_read(IRQENABLE_L0, 0);
Santosh Shilimkar52176e72009-03-23 18:07:49 -07001952 val &= enable_reg; /* Dispatch only relevant interrupts */
Tony Lindgren4d963722008-07-03 12:24:31 +03001953 for (i = 0; i < dma_lch_count && val != 0; i++) {
Juha Yrjola31513692006-12-06 17:13:47 -08001954 if (val & 1)
1955 omap2_dma_handle_ch(i);
1956 val >>= 1;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001957 }
1958
1959 return IRQ_HANDLED;
1960}
1961
1962static struct irqaction omap24xx_dma_irq = {
1963 .name = "DMA",
1964 .handler = omap2_dma_irq_handler,
Thomas Gleixner52e405e2006-07-03 02:20:05 +02001965 .flags = IRQF_DISABLED
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00001966};
1967
1968#else
1969static struct irqaction omap24xx_dma_irq;
1970#endif
1971
1972/*----------------------------------------------------------------------------*/
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01001973
Tero Kristof2d11852008-08-28 13:13:31 +00001974void omap_dma_global_context_save(void)
1975{
1976 omap_dma_global_context.dma_irqenable_l0 =
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001977 p->dma_read(IRQENABLE_L0, 0);
Tero Kristof2d11852008-08-28 13:13:31 +00001978 omap_dma_global_context.dma_ocp_sysconfig =
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001979 p->dma_read(OCP_SYSCONFIG, 0);
1980 omap_dma_global_context.dma_gcr = p->dma_read(GCR, 0);
Tero Kristof2d11852008-08-28 13:13:31 +00001981}
1982
1983void omap_dma_global_context_restore(void)
1984{
Aaro Koskinenbf07c9f2009-05-20 16:58:30 +03001985 int ch;
1986
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001987 p->dma_write(omap_dma_global_context.dma_gcr, GCR, 0);
1988 p->dma_write(omap_dma_global_context.dma_ocp_sysconfig,
G, Manjunath Kondaiaha4c537c2010-12-20 18:27:17 -08001989 OCP_SYSCONFIG, 0);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001990 p->dma_write(omap_dma_global_context.dma_irqenable_l0,
G, Manjunath Kondaiaha4c537c2010-12-20 18:27:17 -08001991 IRQENABLE_L0, 0);
Tero Kristof2d11852008-08-28 13:13:31 +00001992
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08001993 if (IS_DMA_ERRATA(DMA_ROMCODE_BUG))
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08001994 p->dma_write(0x3 , IRQSTATUS_L0, 0);
Aaro Koskinenbf07c9f2009-05-20 16:58:30 +03001995
1996 for (ch = 0; ch < dma_chan_count; ch++)
1997 if (dma_chan[ch].dev_id != -1)
1998 omap_clear_dma(ch);
Tero Kristof2d11852008-08-28 13:13:31 +00001999}
2000
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002001static int __devinit omap_system_dma_probe(struct platform_device *pdev)
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08002002{
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002003 int ch, ret = 0;
2004 int dma_irq;
2005 char irq_name[4];
2006 int irq_rel;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08002007
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002008 p = pdev->dev.platform_data;
2009 if (!p) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06002010 dev_err(&pdev->dev,
2011 "%s: System DMA initialized without platform data\n",
2012 __func__);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002013 return -EINVAL;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08002014 }
2015
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002016 d = p->dma_attr;
2017 errata = p->errata;
G, Manjunath Kondaiahd3c9be22010-12-20 18:27:18 -08002018
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002019 if ((d->dev_caps & RESERVE_CHANNEL) && omap_dma_reserve_channels
Santosh Shilimkar2263f022009-03-23 18:07:48 -07002020 && (omap_dma_reserve_channels <= dma_lch_count))
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002021 d->lch_count = omap_dma_reserve_channels;
Santosh Shilimkar2263f022009-03-23 18:07:48 -07002022
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002023 dma_lch_count = d->lch_count;
2024 dma_chan_count = dma_lch_count;
2025 dma_chan = d->chan;
2026 enable_1510_mode = d->dev_caps & ENABLE_1510_MODE;
Tony Lindgren4d963722008-07-03 12:24:31 +03002027
2028 if (cpu_class_is_omap2()) {
2029 dma_linked_lch = kzalloc(sizeof(struct dma_link_info) *
2030 dma_lch_count, GFP_KERNEL);
2031 if (!dma_linked_lch) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002032 ret = -ENOMEM;
2033 goto exit_dma_lch_fail;
Tony Lindgren4d963722008-07-03 12:24:31 +03002034 }
2035 }
2036
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002037 spin_lock_init(&dma_chan_lock);
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002038 for (ch = 0; ch < dma_chan_count; ch++) {
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00002039 omap_clear_dma(ch);
Mika Westerbergada8d4a2010-05-14 12:05:25 -07002040 if (cpu_class_is_omap2())
2041 omap2_disable_irq_lch(ch);
2042
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002043 dma_chan[ch].dev_id = -1;
2044 dma_chan[ch].next_lch = -1;
2045
2046 if (ch >= 6 && enable_1510_mode)
2047 continue;
2048
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00002049 if (cpu_class_is_omap1()) {
Tony Lindgren97b7f712008-07-03 12:24:37 +03002050 /*
2051 * request_irq() doesn't like dev_id (ie. ch) being
2052 * zero, so we have to kludge around this.
2053 */
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002054 sprintf(&irq_name[0], "%d", ch);
2055 dma_irq = platform_get_irq_byname(pdev, irq_name);
2056
2057 if (dma_irq < 0) {
2058 ret = dma_irq;
2059 goto exit_dma_irq_fail;
2060 }
2061
2062 /* INT_DMA_LCD is handled in lcd_dma.c */
2063 if (dma_irq == INT_DMA_LCD)
2064 continue;
2065
2066 ret = request_irq(dma_irq,
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00002067 omap1_dma_irq_handler, 0, "DMA",
2068 (void *) (ch + 1));
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002069 if (ret != 0)
2070 goto exit_dma_irq_fail;
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00002071 }
2072 }
2073
Santosh Shilimkard07c3df2012-04-28 20:19:10 +05302074 if (cpu_class_is_omap2() && !cpu_is_omap242x())
Anand Gadiyarf8151e52007-12-01 12:14:11 -08002075 omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE,
2076 DMA_DEFAULT_FIFO_DEPTH, 0);
2077
Santosh Shilimkar44169072009-05-28 14:16:04 -07002078 if (cpu_class_is_omap2()) {
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002079 strcpy(irq_name, "0");
2080 dma_irq = platform_get_irq_byname(pdev, irq_name);
2081 if (dma_irq < 0) {
2082 dev_err(&pdev->dev, "failed: request IRQ %d", dma_irq);
2083 goto exit_dma_lch_fail;
2084 }
2085 ret = setup_irq(dma_irq, &omap24xx_dma_irq);
2086 if (ret) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06002087 dev_err(&pdev->dev, "set_up failed for IRQ %d for DMA (error %d)\n",
2088 dma_irq, ret);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002089 goto exit_dma_lch_fail;
Kalle Jokiniemiba50ea72009-03-26 15:59:00 +02002090 }
Kalle Jokiniemiaecedb92009-06-23 13:30:24 +03002091 }
2092
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002093 /* reserve dma channels 0 and 1 in high security devices */
2094 if (cpu_is_omap34xx() &&
2095 (omap_type() != OMAP2_DEVICE_TYPE_GP)) {
Paul Walmsley7852ec02012-07-26 00:54:26 -06002096 pr_info("Reserving DMA channels 0 and 1 for HS ROM code\n");
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002097 dma_chan[0].dev_id = 0;
2098 dma_chan[1].dev_id = 1;
2099 }
2100 p->show_dma_caps();
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002101 return 0;
Tony Lindgren7e9bf842009-10-19 15:25:15 -07002102
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002103exit_dma_irq_fail:
Paul Walmsley7852ec02012-07-26 00:54:26 -06002104 dev_err(&pdev->dev, "unable to request IRQ %d for DMA (error %d)\n",
2105 dma_irq, ret);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002106 for (irq_rel = 0; irq_rel < ch; irq_rel++) {
2107 dma_irq = platform_get_irq(pdev, irq_rel);
2108 free_irq(dma_irq, (void *)(irq_rel + 1));
2109 }
2110
2111exit_dma_lch_fail:
2112 kfree(p);
2113 kfree(d);
Tony Lindgren7e9bf842009-10-19 15:25:15 -07002114 kfree(dma_chan);
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002115 return ret;
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002116}
2117
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002118static int __devexit omap_system_dma_remove(struct platform_device *pdev)
2119{
2120 int dma_irq;
2121
2122 if (cpu_class_is_omap2()) {
2123 char irq_name[4];
2124 strcpy(irq_name, "0");
2125 dma_irq = platform_get_irq_byname(pdev, irq_name);
2126 remove_irq(dma_irq, &omap24xx_dma_irq);
2127 } else {
2128 int irq_rel = 0;
2129 for ( ; irq_rel < dma_chan_count; irq_rel++) {
2130 dma_irq = platform_get_irq(pdev, irq_rel);
2131 free_irq(dma_irq, (void *)(irq_rel + 1));
2132 }
2133 }
2134 kfree(p);
2135 kfree(d);
2136 kfree(dma_chan);
2137 return 0;
2138}
2139
2140static struct platform_driver omap_system_dma_driver = {
2141 .probe = omap_system_dma_probe,
Tony Lindgren3e2e6132012-02-23 14:58:08 -08002142 .remove = __devexit_p(omap_system_dma_remove),
G, Manjunath Kondaiahf31cc962010-12-20 18:27:19 -08002143 .driver = {
2144 .name = "omap_dma_system"
2145 },
2146};
2147
2148static int __init omap_system_dma_init(void)
2149{
2150 return platform_driver_register(&omap_system_dma_driver);
2151}
2152arch_initcall(omap_system_dma_init);
2153
2154static void __exit omap_system_dma_exit(void)
2155{
2156 platform_driver_unregister(&omap_system_dma_driver);
2157}
2158
2159MODULE_DESCRIPTION("OMAP SYSTEM DMA DRIVER");
2160MODULE_LICENSE("GPL");
2161MODULE_ALIAS("platform:" DRIVER_NAME);
2162MODULE_AUTHOR("Texas Instruments Inc");
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002163
Santosh Shilimkar2263f022009-03-23 18:07:48 -07002164/*
2165 * Reserve the omap SDMA channels using cmdline bootarg
2166 * "omap_dma_reserve_ch=". The valid range is 1 to 32
2167 */
2168static int __init omap_dma_cmdline_reserve_ch(char *str)
2169{
2170 if (get_option(&str, &omap_dma_reserve_channels) != 1)
2171 omap_dma_reserve_channels = 0;
2172 return 1;
2173}
2174
2175__setup("omap_dma_reserve_ch=", omap_dma_cmdline_reserve_ch);
2176
Tony Lindgren5e1c5ff2005-07-10 19:58:15 +01002177