Commit | Line | Data |
---|---|---|
f8e9e984 JK |
1 | /* |
2 | * linux/arch/arm/mach-omap1/lcd_dma.c | |
3 | * | |
4 | * Extracted from arch/arm/plat-omap/dma.c | |
5 | * Copyright (C) 2003 - 2008 Nokia Corporation | |
6 | * Author: Juha Yrjölä <juha.yrjola@nokia.com> | |
7 | * DMA channel linking for 1610 by Samuel Ortiz <samuel.ortiz@nokia.com> | |
8 | * Graphics DMA and LCD DMA graphics tranformations | |
9 | * by Imre Deak <imre.deak@nokia.com> | |
10 | * OMAP2/3 support Copyright (C) 2004-2007 Texas Instruments, Inc. | |
11 | * Merged to support both OMAP1 and OMAP2 by Tony Lindgren <tony@atomide.com> | |
12 | * Some functions based on earlier dma-omap.c Copyright (C) 2001 RidgeRun, Inc. | |
13 | * | |
14 | * Copyright (C) 2009 Texas Instruments | |
15 | * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> | |
16 | * | |
17 | * Support functions for the OMAP internal DMA channels. | |
18 | * | |
19 | * This program is free software; you can redistribute it and/or modify | |
20 | * it under the terms of the GNU General Public License version 2 as | |
21 | * published by the Free Software Foundation. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <linux/module.h> | |
26 | #include <linux/spinlock.h> | |
27 | #include <linux/interrupt.h> | |
28 | #include <linux/io.h> | |
29 | ||
45c3eb7d | 30 | #include <linux/omap-dma.h> |
2e3ee9f4 | 31 | |
f8e9e984 | 32 | #include <mach/hardware.h> |
8d72c796 | 33 | #include <mach/lcdc.h> |
f8e9e984 JK |
34 | |
35 | int omap_lcd_dma_running(void) | |
36 | { | |
37 | /* | |
38 | * On OMAP1510, internal LCD controller will start the transfer | |
39 | * when it gets enabled, so assume DMA running if LCD enabled. | |
40 | */ | |
719078a6 | 41 | if (cpu_is_omap15xx()) |
8d72c796 | 42 | if (omap_readw(OMAP_LCDC_CONTROL) & OMAP_LCDC_CTRL_LCD_EN) |
f8e9e984 JK |
43 | return 1; |
44 | ||
45 | /* Check if LCD DMA is running */ | |
46 | if (cpu_is_omap16xx()) | |
47 | if (omap_readw(OMAP1610_DMA_LCD_CCR) & OMAP_DMA_CCR_EN) | |
48 | return 1; | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
53 | static struct lcd_dma_info { | |
54 | spinlock_t lock; | |
55 | int reserved; | |
56 | void (*callback)(u16 status, void *data); | |
57 | void *cb_data; | |
58 | ||
59 | int active; | |
eeb3711b | 60 | unsigned long addr; |
f8e9e984 JK |
61 | int rotate, data_type, xres, yres; |
62 | int vxres; | |
63 | int mirror; | |
64 | int xscale, yscale; | |
65 | int ext_ctrl; | |
66 | int src_port; | |
67 | int single_transfer; | |
68 | } lcd_dma; | |
69 | ||
70 | void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, | |
71 | int data_type) | |
72 | { | |
73 | lcd_dma.addr = addr; | |
74 | lcd_dma.data_type = data_type; | |
75 | lcd_dma.xres = fb_xres; | |
76 | lcd_dma.yres = fb_yres; | |
77 | } | |
78 | EXPORT_SYMBOL(omap_set_lcd_dma_b1); | |
79 | ||
f8e9e984 JK |
80 | void omap_set_lcd_dma_ext_controller(int external) |
81 | { | |
82 | lcd_dma.ext_ctrl = external; | |
83 | } | |
84 | EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); | |
85 | ||
86 | void omap_set_lcd_dma_single_transfer(int single) | |
87 | { | |
88 | lcd_dma.single_transfer = single; | |
89 | } | |
90 | EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); | |
91 | ||
92 | void omap_set_lcd_dma_b1_rotation(int rotate) | |
93 | { | |
719078a6 | 94 | if (cpu_is_omap15xx()) { |
f8e9e984 JK |
95 | printk(KERN_ERR "DMA rotation is not supported in 1510 mode\n"); |
96 | BUG(); | |
97 | return; | |
98 | } | |
99 | lcd_dma.rotate = rotate; | |
100 | } | |
101 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation); | |
102 | ||
103 | void omap_set_lcd_dma_b1_mirror(int mirror) | |
104 | { | |
719078a6 | 105 | if (cpu_is_omap15xx()) { |
f8e9e984 JK |
106 | printk(KERN_ERR "DMA mirror is not supported in 1510 mode\n"); |
107 | BUG(); | |
108 | } | |
109 | lcd_dma.mirror = mirror; | |
110 | } | |
111 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror); | |
112 | ||
113 | void omap_set_lcd_dma_b1_vxres(unsigned long vxres) | |
114 | { | |
719078a6 | 115 | if (cpu_is_omap15xx()) { |
7852ec05 | 116 | pr_err("DMA virtual resolution is not supported in 1510 mode\n"); |
f8e9e984 JK |
117 | BUG(); |
118 | } | |
119 | lcd_dma.vxres = vxres; | |
120 | } | |
121 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres); | |
122 | ||
123 | void omap_set_lcd_dma_b1_scale(unsigned int xscale, unsigned int yscale) | |
124 | { | |
719078a6 | 125 | if (cpu_is_omap15xx()) { |
f8e9e984 JK |
126 | printk(KERN_ERR "DMA scale is not supported in 1510 mode\n"); |
127 | BUG(); | |
128 | } | |
129 | lcd_dma.xscale = xscale; | |
130 | lcd_dma.yscale = yscale; | |
131 | } | |
132 | EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale); | |
133 | ||
134 | static void set_b1_regs(void) | |
135 | { | |
136 | unsigned long top, bottom; | |
137 | int es; | |
138 | u16 w; | |
139 | unsigned long en, fn; | |
140 | long ei, fi; | |
141 | unsigned long vxres; | |
142 | unsigned int xscale, yscale; | |
143 | ||
144 | switch (lcd_dma.data_type) { | |
145 | case OMAP_DMA_DATA_TYPE_S8: | |
146 | es = 1; | |
147 | break; | |
148 | case OMAP_DMA_DATA_TYPE_S16: | |
149 | es = 2; | |
150 | break; | |
151 | case OMAP_DMA_DATA_TYPE_S32: | |
152 | es = 4; | |
153 | break; | |
154 | default: | |
155 | BUG(); | |
156 | return; | |
157 | } | |
158 | ||
159 | vxres = lcd_dma.vxres ? lcd_dma.vxres : lcd_dma.xres; | |
160 | xscale = lcd_dma.xscale ? lcd_dma.xscale : 1; | |
161 | yscale = lcd_dma.yscale ? lcd_dma.yscale : 1; | |
162 | BUG_ON(vxres < lcd_dma.xres); | |
163 | ||
164 | #define PIXADDR(x, y) (lcd_dma.addr + \ | |
165 | ((y) * vxres * yscale + (x) * xscale) * es) | |
166 | #define PIXSTEP(sx, sy, dx, dy) (PIXADDR(dx, dy) - PIXADDR(sx, sy) - es + 1) | |
167 | ||
168 | switch (lcd_dma.rotate) { | |
169 | case 0: | |
170 | if (!lcd_dma.mirror) { | |
171 | top = PIXADDR(0, 0); | |
172 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | |
173 | /* 1510 DMA requires the bottom address to be 2 more | |
174 | * than the actual last memory access location. */ | |
719078a6 | 175 | if (cpu_is_omap15xx() && |
f8e9e984 JK |
176 | lcd_dma.data_type == OMAP_DMA_DATA_TYPE_S32) |
177 | bottom += 2; | |
178 | ei = PIXSTEP(0, 0, 1, 0); | |
179 | fi = PIXSTEP(lcd_dma.xres - 1, 0, 0, 1); | |
180 | } else { | |
181 | top = PIXADDR(lcd_dma.xres - 1, 0); | |
182 | bottom = PIXADDR(0, lcd_dma.yres - 1); | |
183 | ei = PIXSTEP(1, 0, 0, 0); | |
184 | fi = PIXSTEP(0, 0, lcd_dma.xres - 1, 1); | |
185 | } | |
186 | en = lcd_dma.xres; | |
187 | fn = lcd_dma.yres; | |
188 | break; | |
189 | case 90: | |
190 | if (!lcd_dma.mirror) { | |
191 | top = PIXADDR(0, lcd_dma.yres - 1); | |
192 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | |
193 | ei = PIXSTEP(0, 1, 0, 0); | |
194 | fi = PIXSTEP(0, 0, 1, lcd_dma.yres - 1); | |
195 | } else { | |
196 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | |
197 | bottom = PIXADDR(0, 0); | |
198 | ei = PIXSTEP(0, 1, 0, 0); | |
199 | fi = PIXSTEP(1, 0, 0, lcd_dma.yres - 1); | |
200 | } | |
201 | en = lcd_dma.yres; | |
202 | fn = lcd_dma.xres; | |
203 | break; | |
204 | case 180: | |
205 | if (!lcd_dma.mirror) { | |
206 | top = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | |
207 | bottom = PIXADDR(0, 0); | |
208 | ei = PIXSTEP(1, 0, 0, 0); | |
209 | fi = PIXSTEP(0, 1, lcd_dma.xres - 1, 0); | |
210 | } else { | |
211 | top = PIXADDR(0, lcd_dma.yres - 1); | |
212 | bottom = PIXADDR(lcd_dma.xres - 1, 0); | |
213 | ei = PIXSTEP(0, 0, 1, 0); | |
214 | fi = PIXSTEP(lcd_dma.xres - 1, 1, 0, 0); | |
215 | } | |
216 | en = lcd_dma.xres; | |
217 | fn = lcd_dma.yres; | |
218 | break; | |
219 | case 270: | |
220 | if (!lcd_dma.mirror) { | |
221 | top = PIXADDR(lcd_dma.xres - 1, 0); | |
222 | bottom = PIXADDR(0, lcd_dma.yres - 1); | |
223 | ei = PIXSTEP(0, 0, 0, 1); | |
224 | fi = PIXSTEP(1, lcd_dma.yres - 1, 0, 0); | |
225 | } else { | |
226 | top = PIXADDR(0, 0); | |
227 | bottom = PIXADDR(lcd_dma.xres - 1, lcd_dma.yres - 1); | |
228 | ei = PIXSTEP(0, 0, 0, 1); | |
229 | fi = PIXSTEP(0, lcd_dma.yres - 1, 1, 0); | |
230 | } | |
231 | en = lcd_dma.yres; | |
232 | fn = lcd_dma.xres; | |
233 | break; | |
234 | default: | |
235 | BUG(); | |
236 | return; /* Suppress warning about uninitialized vars */ | |
237 | } | |
238 | ||
719078a6 | 239 | if (cpu_is_omap15xx()) { |
f8e9e984 JK |
240 | omap_writew(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U); |
241 | omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L); | |
242 | omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U); | |
243 | omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L); | |
244 | ||
245 | return; | |
246 | } | |
247 | ||
248 | /* 1610 regs */ | |
249 | omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U); | |
250 | omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L); | |
251 | omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U); | |
252 | omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L); | |
253 | ||
254 | omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1); | |
255 | omap_writew(fn, OMAP1610_DMA_LCD_SRC_FN_B1); | |
256 | ||
257 | w = omap_readw(OMAP1610_DMA_LCD_CSDP); | |
258 | w &= ~0x03; | |
259 | w |= lcd_dma.data_type; | |
260 | omap_writew(w, OMAP1610_DMA_LCD_CSDP); | |
261 | ||
262 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | |
263 | /* Always set the source port as SDRAM for now*/ | |
264 | w &= ~(0x03 << 6); | |
265 | if (lcd_dma.callback != NULL) | |
266 | w |= 1 << 1; /* Block interrupt enable */ | |
267 | else | |
268 | w &= ~(1 << 1); | |
269 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | |
270 | ||
271 | if (!(lcd_dma.rotate || lcd_dma.mirror || | |
272 | lcd_dma.vxres || lcd_dma.xscale || lcd_dma.yscale)) | |
273 | return; | |
274 | ||
275 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | |
276 | /* Set the double-indexed addressing mode */ | |
277 | w |= (0x03 << 12); | |
278 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | |
279 | ||
280 | omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1); | |
281 | omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U); | |
282 | omap_writew(fi, OMAP1610_DMA_LCD_SRC_FI_B1_L); | |
283 | } | |
284 | ||
285 | static irqreturn_t lcd_dma_irq_handler(int irq, void *dev_id) | |
286 | { | |
287 | u16 w; | |
288 | ||
289 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | |
290 | if (unlikely(!(w & (1 << 3)))) { | |
291 | printk(KERN_WARNING "Spurious LCD DMA IRQ\n"); | |
292 | return IRQ_NONE; | |
293 | } | |
294 | /* Ack the IRQ */ | |
295 | w |= (1 << 3); | |
296 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | |
297 | lcd_dma.active = 0; | |
298 | if (lcd_dma.callback != NULL) | |
299 | lcd_dma.callback(w, lcd_dma.cb_data); | |
300 | ||
301 | return IRQ_HANDLED; | |
302 | } | |
303 | ||
304 | int omap_request_lcd_dma(void (*callback)(u16 status, void *data), | |
305 | void *data) | |
306 | { | |
307 | spin_lock_irq(&lcd_dma.lock); | |
308 | if (lcd_dma.reserved) { | |
309 | spin_unlock_irq(&lcd_dma.lock); | |
310 | printk(KERN_ERR "LCD DMA channel already reserved\n"); | |
311 | BUG(); | |
312 | return -EBUSY; | |
313 | } | |
314 | lcd_dma.reserved = 1; | |
315 | spin_unlock_irq(&lcd_dma.lock); | |
316 | lcd_dma.callback = callback; | |
317 | lcd_dma.cb_data = data; | |
318 | lcd_dma.active = 0; | |
319 | lcd_dma.single_transfer = 0; | |
320 | lcd_dma.rotate = 0; | |
321 | lcd_dma.vxres = 0; | |
322 | lcd_dma.mirror = 0; | |
323 | lcd_dma.xscale = 0; | |
324 | lcd_dma.yscale = 0; | |
325 | lcd_dma.ext_ctrl = 0; | |
326 | lcd_dma.src_port = 0; | |
327 | ||
328 | return 0; | |
329 | } | |
330 | EXPORT_SYMBOL(omap_request_lcd_dma); | |
331 | ||
332 | void omap_free_lcd_dma(void) | |
333 | { | |
334 | spin_lock(&lcd_dma.lock); | |
335 | if (!lcd_dma.reserved) { | |
336 | spin_unlock(&lcd_dma.lock); | |
337 | printk(KERN_ERR "LCD DMA is not reserved\n"); | |
338 | BUG(); | |
339 | return; | |
340 | } | |
719078a6 | 341 | if (!cpu_is_omap15xx()) |
f8e9e984 JK |
342 | omap_writew(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1, |
343 | OMAP1610_DMA_LCD_CCR); | |
344 | lcd_dma.reserved = 0; | |
345 | spin_unlock(&lcd_dma.lock); | |
346 | } | |
347 | EXPORT_SYMBOL(omap_free_lcd_dma); | |
348 | ||
349 | void omap_enable_lcd_dma(void) | |
350 | { | |
351 | u16 w; | |
352 | ||
353 | /* | |
354 | * Set the Enable bit only if an external controller is | |
355 | * connected. Otherwise the OMAP internal controller will | |
356 | * start the transfer when it gets enabled. | |
357 | */ | |
719078a6 | 358 | if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl) |
f8e9e984 JK |
359 | return; |
360 | ||
361 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | |
362 | w |= 1 << 8; | |
363 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | |
364 | ||
365 | lcd_dma.active = 1; | |
366 | ||
367 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | |
368 | w |= 1 << 7; | |
369 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | |
370 | } | |
371 | EXPORT_SYMBOL(omap_enable_lcd_dma); | |
372 | ||
373 | void omap_setup_lcd_dma(void) | |
374 | { | |
375 | BUG_ON(lcd_dma.active); | |
719078a6 | 376 | if (!cpu_is_omap15xx()) { |
f8e9e984 JK |
377 | /* Set some reasonable defaults */ |
378 | omap_writew(0x5440, OMAP1610_DMA_LCD_CCR); | |
379 | omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP); | |
380 | omap_writew(0x0004, OMAP1610_DMA_LCD_LCH_CTRL); | |
381 | } | |
382 | set_b1_regs(); | |
719078a6 | 383 | if (!cpu_is_omap15xx()) { |
f8e9e984 JK |
384 | u16 w; |
385 | ||
386 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | |
387 | /* | |
388 | * If DMA was already active set the end_prog bit to have | |
389 | * the programmed register set loaded into the active | |
390 | * register set. | |
391 | */ | |
392 | w |= 1 << 11; /* End_prog */ | |
393 | if (!lcd_dma.single_transfer) | |
394 | w |= (3 << 8); /* Auto_init, repeat */ | |
395 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | |
396 | } | |
397 | } | |
398 | EXPORT_SYMBOL(omap_setup_lcd_dma); | |
399 | ||
400 | void omap_stop_lcd_dma(void) | |
401 | { | |
402 | u16 w; | |
403 | ||
404 | lcd_dma.active = 0; | |
719078a6 | 405 | if (cpu_is_omap15xx() || !lcd_dma.ext_ctrl) |
f8e9e984 JK |
406 | return; |
407 | ||
408 | w = omap_readw(OMAP1610_DMA_LCD_CCR); | |
409 | w &= ~(1 << 7); | |
410 | omap_writew(w, OMAP1610_DMA_LCD_CCR); | |
411 | ||
412 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | |
413 | w &= ~(1 << 8); | |
414 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | |
415 | } | |
416 | EXPORT_SYMBOL(omap_stop_lcd_dma); | |
417 | ||
418 | static int __init omap_init_lcd_dma(void) | |
419 | { | |
420 | int r; | |
421 | ||
7f9187c2 TL |
422 | if (!cpu_class_is_omap1()) |
423 | return -ENODEV; | |
424 | ||
f8e9e984 JK |
425 | if (cpu_is_omap16xx()) { |
426 | u16 w; | |
427 | ||
428 | /* this would prevent OMAP sleep */ | |
429 | w = omap_readw(OMAP1610_DMA_LCD_CTRL); | |
430 | w &= ~(1 << 8); | |
431 | omap_writew(w, OMAP1610_DMA_LCD_CTRL); | |
432 | } | |
433 | ||
434 | spin_lock_init(&lcd_dma.lock); | |
435 | ||
436 | r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0, | |
437 | "LCD DMA", NULL); | |
438 | if (r != 0) | |
7852ec05 | 439 | pr_err("unable to request IRQ for LCD DMA (error %d)\n", r); |
f8e9e984 JK |
440 | |
441 | return r; | |
442 | } | |
443 | ||
444 | arch_initcall(omap_init_lcd_dma); | |
445 |