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