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