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
34int 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
52static 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
69void 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}
77EXPORT_SYMBOL(omap_set_lcd_dma_b1);
78
79void omap_set_lcd_dma_ext_controller(int external)
80{
81 lcd_dma.ext_ctrl = external;
82}
83EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller);
84
85void omap_set_lcd_dma_single_transfer(int single)
86{
87 lcd_dma.single_transfer = single;
88}
89EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer);
90
91void 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}
100EXPORT_SYMBOL(omap_set_lcd_dma_b1_rotation);
101
102void 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}
110EXPORT_SYMBOL(omap_set_lcd_dma_b1_mirror);
111
112void 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}
120EXPORT_SYMBOL(omap_set_lcd_dma_b1_vxres);
121
122void 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}
131EXPORT_SYMBOL(omap_set_lcd_dma_b1_scale);
132
133static 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
284static 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
303int 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}
329EXPORT_SYMBOL(omap_request_lcd_dma);
330
331void 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}
346EXPORT_SYMBOL(omap_free_lcd_dma);
347
348void 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}
370EXPORT_SYMBOL(omap_enable_lcd_dma);
371
372void 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}
397EXPORT_SYMBOL(omap_setup_lcd_dma);
398
399void 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}
415EXPORT_SYMBOL(omap_stop_lcd_dma);
416
417static 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
443arch_initcall(omap_init_lcd_dma);
444
445

source code of linux/drivers/video/fbdev/omap/lcd_dma.c