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(top >> 16, OMAP1510_DMA_LCD_TOP_F1_U);
240		omap_writew(top, OMAP1510_DMA_LCD_TOP_F1_L);
241		omap_writew(bottom >> 16, OMAP1510_DMA_LCD_BOT_F1_U);
242		omap_writew(bottom, OMAP1510_DMA_LCD_BOT_F1_L);
243
244		return;
245	}
246
247	/* 1610 regs */
248	omap_writew(top >> 16, OMAP1610_DMA_LCD_TOP_B1_U);
249	omap_writew(top, OMAP1610_DMA_LCD_TOP_B1_L);
250	omap_writew(bottom >> 16, OMAP1610_DMA_LCD_BOT_B1_U);
251	omap_writew(bottom, OMAP1610_DMA_LCD_BOT_B1_L);
252
253	omap_writew(en, OMAP1610_DMA_LCD_SRC_EN_B1);
254	omap_writew(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(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(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(w, OMAP1610_DMA_LCD_CCR);
278
279	omap_writew(ei, OMAP1610_DMA_LCD_SRC_EI_B1);
280	omap_writew(fi >> 16, OMAP1610_DMA_LCD_SRC_FI_B1_U);
281	omap_writew(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(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(&lcd_dma.lock);
307	if (lcd_dma.reserved) {
308		spin_unlock_irq(&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(&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(&lcd_dma.lock);
334	if (!lcd_dma.reserved) {
335		spin_unlock(&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(omap_readw(OMAP1610_DMA_LCD_CCR) & ~1,
342			    OMAP1610_DMA_LCD_CCR);
343	lcd_dma.reserved = 0;
344	spin_unlock(&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(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(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(0x5440, OMAP1610_DMA_LCD_CCR);
378		omap_writew(0x9102, OMAP1610_DMA_LCD_CSDP);
379		omap_writew(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(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(w, OMAP1610_DMA_LCD_CCR);
410
411	w = omap_readw(OMAP1610_DMA_LCD_CTRL);
412	w &= ~(1 << 8);
413	omap_writew(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(w, OMAP1610_DMA_LCD_CTRL);
431	}
432
433	spin_lock_init(&lcd_dma.lock);
434
435	r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0,
436			"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