xref: /linux/drivers/video/fbdev/omap/lcdc.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2f7018c21STomi Valkeinen /*
3f7018c21STomi Valkeinen  * OMAP1 internal LCD controller
4f7018c21STomi Valkeinen  *
5f7018c21STomi Valkeinen  * Copyright (C) 2004 Nokia Corporation
6f7018c21STomi Valkeinen  * Author: Imre Deak <imre.deak@nokia.com>
7f7018c21STomi Valkeinen  */
8f7018c21STomi Valkeinen #include <linux/module.h>
9f7018c21STomi Valkeinen #include <linux/device.h>
10f7018c21STomi Valkeinen #include <linux/interrupt.h>
11f7018c21STomi Valkeinen #include <linux/spinlock.h>
12f7018c21STomi Valkeinen #include <linux/err.h>
13f7018c21STomi Valkeinen #include <linux/mm.h>
14f7018c21STomi Valkeinen #include <linux/fb.h>
15f7018c21STomi Valkeinen #include <linux/dma-mapping.h>
16f7018c21STomi Valkeinen #include <linux/vmalloc.h>
17f7018c21STomi Valkeinen #include <linux/clk.h>
18f7018c21STomi Valkeinen #include <linux/gfp.h>
19f7018c21STomi Valkeinen 
20*804f7f19SArnd Bergmann #include <linux/soc/ti/omap1-io.h>
21*804f7f19SArnd Bergmann #include <linux/soc/ti/omap1-soc.h>
22f7018c21STomi Valkeinen #include <linux/omap-dma.h>
23f7018c21STomi Valkeinen 
24f7018c21STomi Valkeinen #include <asm/mach-types.h>
25f7018c21STomi Valkeinen 
26f7018c21STomi Valkeinen #include "omapfb.h"
27f7018c21STomi Valkeinen 
28f7018c21STomi Valkeinen #include "lcdc.h"
29042c4884SArnd Bergmann #include "lcd_dma.h"
30f7018c21STomi Valkeinen 
31f7018c21STomi Valkeinen #define MODULE_NAME			"lcdc"
32f7018c21STomi Valkeinen 
33f7018c21STomi Valkeinen #define MAX_PALETTE_SIZE		PAGE_SIZE
34f7018c21STomi Valkeinen 
35f7018c21STomi Valkeinen enum lcdc_load_mode {
36f7018c21STomi Valkeinen 	OMAP_LCDC_LOAD_PALETTE,
37f7018c21STomi Valkeinen 	OMAP_LCDC_LOAD_FRAME,
38f7018c21STomi Valkeinen 	OMAP_LCDC_LOAD_PALETTE_AND_FRAME
39f7018c21STomi Valkeinen };
40f7018c21STomi Valkeinen 
41f7018c21STomi Valkeinen static struct omap_lcd_controller {
42f7018c21STomi Valkeinen 	enum omapfb_update_mode	update_mode;
43f7018c21STomi Valkeinen 	int			ext_mode;
44f7018c21STomi Valkeinen 
45f7018c21STomi Valkeinen 	unsigned long		frame_offset;
46f7018c21STomi Valkeinen 	int			screen_width;
47f7018c21STomi Valkeinen 	int			xres;
48f7018c21STomi Valkeinen 	int			yres;
49f7018c21STomi Valkeinen 
50f7018c21STomi Valkeinen 	enum omapfb_color_format	color_mode;
51f7018c21STomi Valkeinen 	int			bpp;
52f7018c21STomi Valkeinen 	void			*palette_virt;
53f7018c21STomi Valkeinen 	dma_addr_t		palette_phys;
54f7018c21STomi Valkeinen 	int			palette_code;
55f7018c21STomi Valkeinen 	int			palette_size;
56f7018c21STomi Valkeinen 
57f7018c21STomi Valkeinen 	unsigned int		irq_mask;
58f7018c21STomi Valkeinen 	struct completion	last_frame_complete;
59f7018c21STomi Valkeinen 	struct completion	palette_load_complete;
60f7018c21STomi Valkeinen 	struct clk		*lcd_ck;
61f7018c21STomi Valkeinen 	struct omapfb_device	*fbdev;
62f7018c21STomi Valkeinen 
63f7018c21STomi Valkeinen 	void			(*dma_callback)(void *data);
64f7018c21STomi Valkeinen 	void			*dma_callback_data;
65f7018c21STomi Valkeinen 
66f7018c21STomi Valkeinen 	dma_addr_t		vram_phys;
67f7018c21STomi Valkeinen 	void			*vram_virt;
68f7018c21STomi Valkeinen 	unsigned long		vram_size;
69f7018c21STomi Valkeinen } lcdc;
70f7018c21STomi Valkeinen 
enable_irqs(int mask)71ba168a46SJoe Perches static inline void enable_irqs(int mask)
72f7018c21STomi Valkeinen {
73f7018c21STomi Valkeinen 	lcdc.irq_mask |= mask;
74f7018c21STomi Valkeinen }
75f7018c21STomi Valkeinen 
disable_irqs(int mask)76ba168a46SJoe Perches static inline void disable_irqs(int mask)
77f7018c21STomi Valkeinen {
78f7018c21STomi Valkeinen 	lcdc.irq_mask &= ~mask;
79f7018c21STomi Valkeinen }
80f7018c21STomi Valkeinen 
set_load_mode(enum lcdc_load_mode mode)81f7018c21STomi Valkeinen static void set_load_mode(enum lcdc_load_mode mode)
82f7018c21STomi Valkeinen {
83f7018c21STomi Valkeinen 	u32 l;
84f7018c21STomi Valkeinen 
85f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_CONTROL);
86f7018c21STomi Valkeinen 	l &= ~(3 << 20);
87f7018c21STomi Valkeinen 	switch (mode) {
88f7018c21STomi Valkeinen 	case OMAP_LCDC_LOAD_PALETTE:
89f7018c21STomi Valkeinen 		l |= 1 << 20;
90f7018c21STomi Valkeinen 		break;
91f7018c21STomi Valkeinen 	case OMAP_LCDC_LOAD_FRAME:
92f7018c21STomi Valkeinen 		l |= 2 << 20;
93f7018c21STomi Valkeinen 		break;
94f7018c21STomi Valkeinen 	case OMAP_LCDC_LOAD_PALETTE_AND_FRAME:
95f7018c21STomi Valkeinen 		break;
96f7018c21STomi Valkeinen 	default:
97f7018c21STomi Valkeinen 		BUG();
98f7018c21STomi Valkeinen 	}
99f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_CONTROL);
100f7018c21STomi Valkeinen }
101f7018c21STomi Valkeinen 
enable_controller(void)102f7018c21STomi Valkeinen static void enable_controller(void)
103f7018c21STomi Valkeinen {
104f7018c21STomi Valkeinen 	u32 l;
105f7018c21STomi Valkeinen 
106f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_CONTROL);
107f7018c21STomi Valkeinen 	l |= OMAP_LCDC_CTRL_LCD_EN;
108f7018c21STomi Valkeinen 	l &= ~OMAP_LCDC_IRQ_MASK;
109f7018c21STomi Valkeinen 	l |= lcdc.irq_mask | OMAP_LCDC_IRQ_DONE;	/* enabled IRQs */
110f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_CONTROL);
111f7018c21STomi Valkeinen }
112f7018c21STomi Valkeinen 
disable_controller_async(void)113f7018c21STomi Valkeinen static void disable_controller_async(void)
114f7018c21STomi Valkeinen {
115f7018c21STomi Valkeinen 	u32 l;
116f7018c21STomi Valkeinen 	u32 mask;
117f7018c21STomi Valkeinen 
118f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_CONTROL);
119f7018c21STomi Valkeinen 	mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK;
120f7018c21STomi Valkeinen 	/*
121f7018c21STomi Valkeinen 	 * Preserve the DONE mask, since we still want to get the
122f7018c21STomi Valkeinen 	 * final DONE irq. It will be disabled in the IRQ handler.
123f7018c21STomi Valkeinen 	 */
124f7018c21STomi Valkeinen 	mask &= ~OMAP_LCDC_IRQ_DONE;
125f7018c21STomi Valkeinen 	l &= ~mask;
126f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_CONTROL);
127f7018c21STomi Valkeinen }
128f7018c21STomi Valkeinen 
disable_controller(void)129f7018c21STomi Valkeinen static void disable_controller(void)
130f7018c21STomi Valkeinen {
131f7018c21STomi Valkeinen 	init_completion(&lcdc.last_frame_complete);
132f7018c21STomi Valkeinen 	disable_controller_async();
133f7018c21STomi Valkeinen 	if (!wait_for_completion_timeout(&lcdc.last_frame_complete,
134f7018c21STomi Valkeinen 				msecs_to_jiffies(500)))
135f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
136f7018c21STomi Valkeinen }
137f7018c21STomi Valkeinen 
reset_controller(u32 status)138f7018c21STomi Valkeinen static void reset_controller(u32 status)
139f7018c21STomi Valkeinen {
140f7018c21STomi Valkeinen 	static unsigned long reset_count;
141f7018c21STomi Valkeinen 	static unsigned long last_jiffies;
142f7018c21STomi Valkeinen 
143f7018c21STomi Valkeinen 	disable_controller_async();
144f7018c21STomi Valkeinen 	reset_count++;
145f7018c21STomi Valkeinen 	if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) {
146f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev,
147f7018c21STomi Valkeinen 			  "resetting (status %#010x,reset count %lu)\n",
148f7018c21STomi Valkeinen 			  status, reset_count);
149f7018c21STomi Valkeinen 		last_jiffies = jiffies;
150f7018c21STomi Valkeinen 	}
151f7018c21STomi Valkeinen 	if (reset_count < 100) {
152f7018c21STomi Valkeinen 		enable_controller();
153f7018c21STomi Valkeinen 	} else {
154f7018c21STomi Valkeinen 		reset_count = 0;
155f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev,
156f7018c21STomi Valkeinen 			"too many reset attempts, giving up.\n");
157f7018c21STomi Valkeinen 	}
158f7018c21STomi Valkeinen }
159f7018c21STomi Valkeinen 
160f7018c21STomi Valkeinen /*
161f7018c21STomi Valkeinen  * Configure the LCD DMA according to the current mode specified by parameters
162f7018c21STomi Valkeinen  * in lcdc.fbdev and fbdev->var.
163f7018c21STomi Valkeinen  */
setup_lcd_dma(void)164f7018c21STomi Valkeinen static void setup_lcd_dma(void)
165f7018c21STomi Valkeinen {
166f7018c21STomi Valkeinen 	static const int dma_elem_type[] = {
167f7018c21STomi Valkeinen 		0,
168f7018c21STomi Valkeinen 		OMAP_DMA_DATA_TYPE_S8,
169f7018c21STomi Valkeinen 		OMAP_DMA_DATA_TYPE_S16,
170f7018c21STomi Valkeinen 		0,
171f7018c21STomi Valkeinen 		OMAP_DMA_DATA_TYPE_S32,
172f7018c21STomi Valkeinen 	};
173f7018c21STomi Valkeinen 	struct omapfb_plane_struct *plane = lcdc.fbdev->fb_info[0]->par;
174f7018c21STomi Valkeinen 	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
175f7018c21STomi Valkeinen 	unsigned long	src;
176f7018c21STomi Valkeinen 	int		esize, xelem, yelem;
177f7018c21STomi Valkeinen 
178f7018c21STomi Valkeinen 	src = lcdc.vram_phys + lcdc.frame_offset;
179f7018c21STomi Valkeinen 
180f7018c21STomi Valkeinen 	switch (var->rotate) {
181f7018c21STomi Valkeinen 	case 0:
182f7018c21STomi Valkeinen 		if (plane->info.mirror || (src & 3) ||
183f7018c21STomi Valkeinen 		    lcdc.color_mode == OMAPFB_COLOR_YUV420 ||
184f7018c21STomi Valkeinen 		    (lcdc.xres & 1))
185f7018c21STomi Valkeinen 			esize = 2;
186f7018c21STomi Valkeinen 		else
187f7018c21STomi Valkeinen 			esize = 4;
188f7018c21STomi Valkeinen 		xelem = lcdc.xres * lcdc.bpp / 8 / esize;
189f7018c21STomi Valkeinen 		yelem = lcdc.yres;
190f7018c21STomi Valkeinen 		break;
191f7018c21STomi Valkeinen 	case 90:
192f7018c21STomi Valkeinen 	case 180:
193f7018c21STomi Valkeinen 	case 270:
194f7018c21STomi Valkeinen 		if (cpu_is_omap15xx()) {
195f7018c21STomi Valkeinen 			BUG();
196f7018c21STomi Valkeinen 		}
197f7018c21STomi Valkeinen 		esize = 2;
198f7018c21STomi Valkeinen 		xelem = lcdc.yres * lcdc.bpp / 16;
199f7018c21STomi Valkeinen 		yelem = lcdc.xres;
200f7018c21STomi Valkeinen 		break;
201f7018c21STomi Valkeinen 	default:
202f7018c21STomi Valkeinen 		BUG();
203f7018c21STomi Valkeinen 		return;
204f7018c21STomi Valkeinen 	}
205f7018c21STomi Valkeinen #ifdef VERBOSE
206f7018c21STomi Valkeinen 	dev_dbg(lcdc.fbdev->dev,
207f7018c21STomi Valkeinen 		 "setup_dma: src %#010lx esize %d xelem %d yelem %d\n",
208f7018c21STomi Valkeinen 		 src, esize, xelem, yelem);
209f7018c21STomi Valkeinen #endif
210f7018c21STomi Valkeinen 	omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]);
211f7018c21STomi Valkeinen 	if (!cpu_is_omap15xx()) {
212f7018c21STomi Valkeinen 		int bpp = lcdc.bpp;
213f7018c21STomi Valkeinen 
214f7018c21STomi Valkeinen 		/*
215f7018c21STomi Valkeinen 		 * YUV support is only for external mode when we have the
216f7018c21STomi Valkeinen 		 * YUV window embedded in a 16bpp frame buffer.
217f7018c21STomi Valkeinen 		 */
218f7018c21STomi Valkeinen 		if (lcdc.color_mode == OMAPFB_COLOR_YUV420)
219f7018c21STomi Valkeinen 			bpp = 16;
220f7018c21STomi Valkeinen 		/* Set virtual xres elem size */
221f7018c21STomi Valkeinen 		omap_set_lcd_dma_b1_vxres(
222f7018c21STomi Valkeinen 			lcdc.screen_width * bpp / 8 / esize);
223f7018c21STomi Valkeinen 		/* Setup transformations */
224f7018c21STomi Valkeinen 		omap_set_lcd_dma_b1_rotation(var->rotate);
225f7018c21STomi Valkeinen 		omap_set_lcd_dma_b1_mirror(plane->info.mirror);
226f7018c21STomi Valkeinen 	}
227f7018c21STomi Valkeinen 	omap_setup_lcd_dma();
228f7018c21STomi Valkeinen }
229f7018c21STomi Valkeinen 
lcdc_irq_handler(int irq,void * dev_id)230f7018c21STomi Valkeinen static irqreturn_t lcdc_irq_handler(int irq, void *dev_id)
231f7018c21STomi Valkeinen {
232f7018c21STomi Valkeinen 	u32 status;
233f7018c21STomi Valkeinen 
234f7018c21STomi Valkeinen 	status = omap_readl(OMAP_LCDC_STATUS);
235f7018c21STomi Valkeinen 
236f7018c21STomi Valkeinen 	if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST))
237f7018c21STomi Valkeinen 		reset_controller(status);
238f7018c21STomi Valkeinen 	else {
239f7018c21STomi Valkeinen 		if (status & OMAP_LCDC_STAT_DONE) {
240f7018c21STomi Valkeinen 			u32 l;
241f7018c21STomi Valkeinen 
242f7018c21STomi Valkeinen 			/*
243f7018c21STomi Valkeinen 			 * Disable IRQ_DONE. The status bit will be cleared
244f7018c21STomi Valkeinen 			 * only when the controller is reenabled and we don't
245f7018c21STomi Valkeinen 			 * want to get more interrupts.
246f7018c21STomi Valkeinen 			 */
247f7018c21STomi Valkeinen 			l = omap_readl(OMAP_LCDC_CONTROL);
248f7018c21STomi Valkeinen 			l &= ~OMAP_LCDC_IRQ_DONE;
249f7018c21STomi Valkeinen 			omap_writel(l, OMAP_LCDC_CONTROL);
250f7018c21STomi Valkeinen 			complete(&lcdc.last_frame_complete);
251f7018c21STomi Valkeinen 		}
252f7018c21STomi Valkeinen 		if (status & OMAP_LCDC_STAT_LOADED_PALETTE) {
253f7018c21STomi Valkeinen 			disable_controller_async();
254f7018c21STomi Valkeinen 			complete(&lcdc.palette_load_complete);
255f7018c21STomi Valkeinen 		}
256f7018c21STomi Valkeinen 	}
257f7018c21STomi Valkeinen 
258f7018c21STomi Valkeinen 	/*
259f7018c21STomi Valkeinen 	 * Clear these interrupt status bits.
260f7018c21STomi Valkeinen 	 * Sync_lost, FUF bits were cleared by disabling the LCD controller
261f7018c21STomi Valkeinen 	 * LOADED_PALETTE can be cleared this way only in palette only
262f7018c21STomi Valkeinen 	 * load mode. In other load modes it's cleared by disabling the
263f7018c21STomi Valkeinen 	 * controller.
264f7018c21STomi Valkeinen 	 */
265f7018c21STomi Valkeinen 	status &= ~(OMAP_LCDC_STAT_VSYNC |
266f7018c21STomi Valkeinen 		    OMAP_LCDC_STAT_LOADED_PALETTE |
267f7018c21STomi Valkeinen 		    OMAP_LCDC_STAT_ABC |
268f7018c21STomi Valkeinen 		    OMAP_LCDC_STAT_LINE_INT);
269f7018c21STomi Valkeinen 	omap_writel(status, OMAP_LCDC_STATUS);
270f7018c21STomi Valkeinen 	return IRQ_HANDLED;
271f7018c21STomi Valkeinen }
272f7018c21STomi Valkeinen 
273f7018c21STomi Valkeinen /*
274f7018c21STomi Valkeinen  * Change to a new video mode. We defer this to a later time to avoid any
275f7018c21STomi Valkeinen  * flicker and not to mess up the current LCD DMA context. For this we disable
276f7018c21STomi Valkeinen  * the LCD controller, which will generate a DONE irq after the last frame has
277f7018c21STomi Valkeinen  * been transferred. Then it'll be safe to reconfigure both the LCD controller
278f7018c21STomi Valkeinen  * as well as the LCD DMA.
279f7018c21STomi Valkeinen  */
omap_lcdc_setup_plane(int plane,int channel_out,unsigned long offset,int screen_width,int pos_x,int pos_y,int width,int height,int color_mode)280f7018c21STomi Valkeinen static int omap_lcdc_setup_plane(int plane, int channel_out,
281f7018c21STomi Valkeinen 				 unsigned long offset, int screen_width,
282f7018c21STomi Valkeinen 				 int pos_x, int pos_y, int width, int height,
283f7018c21STomi Valkeinen 				 int color_mode)
284f7018c21STomi Valkeinen {
285f7018c21STomi Valkeinen 	struct fb_var_screeninfo *var = &lcdc.fbdev->fb_info[0]->var;
286f7018c21STomi Valkeinen 	struct lcd_panel *panel = lcdc.fbdev->panel;
287f7018c21STomi Valkeinen 	int rot_x, rot_y;
288f7018c21STomi Valkeinen 
289f7018c21STomi Valkeinen 	if (var->rotate == 0) {
290f7018c21STomi Valkeinen 		rot_x = panel->x_res;
291f7018c21STomi Valkeinen 		rot_y = panel->y_res;
292f7018c21STomi Valkeinen 	} else {
293f7018c21STomi Valkeinen 		rot_x = panel->y_res;
294f7018c21STomi Valkeinen 		rot_y = panel->x_res;
295f7018c21STomi Valkeinen 	}
296f7018c21STomi Valkeinen 	if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 ||
297f7018c21STomi Valkeinen 	    width > rot_x || height > rot_y) {
298f7018c21STomi Valkeinen #ifdef VERBOSE
299f7018c21STomi Valkeinen 		dev_dbg(lcdc.fbdev->dev,
300f7018c21STomi Valkeinen 			"invalid plane params plane %d pos_x %d pos_y %d "
301f7018c21STomi Valkeinen 			"w %d h %d\n", plane, pos_x, pos_y, width, height);
302f7018c21STomi Valkeinen #endif
303f7018c21STomi Valkeinen 		return -EINVAL;
304f7018c21STomi Valkeinen 	}
305f7018c21STomi Valkeinen 
306f7018c21STomi Valkeinen 	lcdc.frame_offset = offset;
307f7018c21STomi Valkeinen 	lcdc.xres = width;
308f7018c21STomi Valkeinen 	lcdc.yres = height;
309f7018c21STomi Valkeinen 	lcdc.screen_width = screen_width;
310f7018c21STomi Valkeinen 	lcdc.color_mode = color_mode;
311f7018c21STomi Valkeinen 
312f7018c21STomi Valkeinen 	switch (color_mode) {
313f7018c21STomi Valkeinen 	case OMAPFB_COLOR_CLUT_8BPP:
314f7018c21STomi Valkeinen 		lcdc.bpp = 8;
315f7018c21STomi Valkeinen 		lcdc.palette_code = 0x3000;
316f7018c21STomi Valkeinen 		lcdc.palette_size = 512;
317f7018c21STomi Valkeinen 		break;
318f7018c21STomi Valkeinen 	case OMAPFB_COLOR_RGB565:
319f7018c21STomi Valkeinen 		lcdc.bpp = 16;
320f7018c21STomi Valkeinen 		lcdc.palette_code = 0x4000;
321f7018c21STomi Valkeinen 		lcdc.palette_size = 32;
322f7018c21STomi Valkeinen 		break;
323f7018c21STomi Valkeinen 	case OMAPFB_COLOR_RGB444:
324f7018c21STomi Valkeinen 		lcdc.bpp = 16;
325f7018c21STomi Valkeinen 		lcdc.palette_code = 0x4000;
326f7018c21STomi Valkeinen 		lcdc.palette_size = 32;
327f7018c21STomi Valkeinen 		break;
328f7018c21STomi Valkeinen 	case OMAPFB_COLOR_YUV420:
329f7018c21STomi Valkeinen 		if (lcdc.ext_mode) {
330f7018c21STomi Valkeinen 			lcdc.bpp = 12;
331f7018c21STomi Valkeinen 			break;
332f7018c21STomi Valkeinen 		}
333df561f66SGustavo A. R. Silva 		fallthrough;
334f7018c21STomi Valkeinen 	case OMAPFB_COLOR_YUV422:
335f7018c21STomi Valkeinen 		if (lcdc.ext_mode) {
336f7018c21STomi Valkeinen 			lcdc.bpp = 16;
337f7018c21STomi Valkeinen 			break;
338f7018c21STomi Valkeinen 		}
339df561f66SGustavo A. R. Silva 		fallthrough;
340f7018c21STomi Valkeinen 	default:
341f7018c21STomi Valkeinen 		/* FIXME: other BPPs.
342f7018c21STomi Valkeinen 		 * bpp1: code  0,     size 256
343f7018c21STomi Valkeinen 		 * bpp2: code  0x1000 size 256
344f7018c21STomi Valkeinen 		 * bpp4: code  0x2000 size 256
345f7018c21STomi Valkeinen 		 * bpp12: code 0x4000 size 32
346f7018c21STomi Valkeinen 		 */
347f7018c21STomi Valkeinen 		dev_dbg(lcdc.fbdev->dev, "invalid color mode %d\n", color_mode);
348f7018c21STomi Valkeinen 		BUG();
349f7018c21STomi Valkeinen 		return -1;
350f7018c21STomi Valkeinen 	}
351f7018c21STomi Valkeinen 
352f7018c21STomi Valkeinen 	if (lcdc.ext_mode) {
353f7018c21STomi Valkeinen 		setup_lcd_dma();
354f7018c21STomi Valkeinen 		return 0;
355f7018c21STomi Valkeinen 	}
356f7018c21STomi Valkeinen 
357f7018c21STomi Valkeinen 	if (lcdc.update_mode == OMAPFB_AUTO_UPDATE) {
358f7018c21STomi Valkeinen 		disable_controller();
359f7018c21STomi Valkeinen 		omap_stop_lcd_dma();
360f7018c21STomi Valkeinen 		setup_lcd_dma();
361f7018c21STomi Valkeinen 		enable_controller();
362f7018c21STomi Valkeinen 	}
363f7018c21STomi Valkeinen 
364f7018c21STomi Valkeinen 	return 0;
365f7018c21STomi Valkeinen }
366f7018c21STomi Valkeinen 
omap_lcdc_enable_plane(int plane,int enable)367f7018c21STomi Valkeinen static int omap_lcdc_enable_plane(int plane, int enable)
368f7018c21STomi Valkeinen {
369f7018c21STomi Valkeinen 	dev_dbg(lcdc.fbdev->dev,
370f7018c21STomi Valkeinen 		"plane %d enable %d update_mode %d ext_mode %d\n",
371f7018c21STomi Valkeinen 		plane, enable, lcdc.update_mode, lcdc.ext_mode);
372f7018c21STomi Valkeinen 	if (plane != OMAPFB_PLANE_GFX)
373f7018c21STomi Valkeinen 		return -EINVAL;
374f7018c21STomi Valkeinen 
375f7018c21STomi Valkeinen 	return 0;
376f7018c21STomi Valkeinen }
377f7018c21STomi Valkeinen 
378f7018c21STomi Valkeinen /*
379f7018c21STomi Valkeinen  * Configure the LCD DMA for a palette load operation and do the palette
380f7018c21STomi Valkeinen  * downloading synchronously. We don't use the frame+palette load mode of
381f7018c21STomi Valkeinen  * the controller, since the palette can always be downloaded separately.
382f7018c21STomi Valkeinen  */
load_palette(void)383f7018c21STomi Valkeinen static void load_palette(void)
384f7018c21STomi Valkeinen {
385f7018c21STomi Valkeinen 	u16	*palette;
386f7018c21STomi Valkeinen 
387f7018c21STomi Valkeinen 	palette = (u16 *)lcdc.palette_virt;
388f7018c21STomi Valkeinen 
389f7018c21STomi Valkeinen 	*(u16 *)palette &= 0x0fff;
390f7018c21STomi Valkeinen 	*(u16 *)palette |= lcdc.palette_code;
391f7018c21STomi Valkeinen 
392f7018c21STomi Valkeinen 	omap_set_lcd_dma_b1(lcdc.palette_phys,
393f7018c21STomi Valkeinen 		lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32);
394f7018c21STomi Valkeinen 
395f7018c21STomi Valkeinen 	omap_set_lcd_dma_single_transfer(1);
396f7018c21STomi Valkeinen 	omap_setup_lcd_dma();
397f7018c21STomi Valkeinen 
398f7018c21STomi Valkeinen 	init_completion(&lcdc.palette_load_complete);
399f7018c21STomi Valkeinen 	enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
400f7018c21STomi Valkeinen 	set_load_mode(OMAP_LCDC_LOAD_PALETTE);
401f7018c21STomi Valkeinen 	enable_controller();
402f7018c21STomi Valkeinen 	if (!wait_for_completion_timeout(&lcdc.palette_load_complete,
403f7018c21STomi Valkeinen 				msecs_to_jiffies(500)))
404f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "timeout waiting for FRAME DONE\n");
405f7018c21STomi Valkeinen 	/* The controller gets disabled in the irq handler */
406f7018c21STomi Valkeinen 	disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE);
407f7018c21STomi Valkeinen 	omap_stop_lcd_dma();
408f7018c21STomi Valkeinen 
409f7018c21STomi Valkeinen 	omap_set_lcd_dma_single_transfer(lcdc.ext_mode);
410f7018c21STomi Valkeinen }
411f7018c21STomi Valkeinen 
412f7018c21STomi Valkeinen /* Used only in internal controller mode */
omap_lcdc_setcolreg(u_int regno,u16 red,u16 green,u16 blue,u16 transp,int update_hw_pal)413f7018c21STomi Valkeinen static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue,
414f7018c21STomi Valkeinen 			       u16 transp, int update_hw_pal)
415f7018c21STomi Valkeinen {
416f7018c21STomi Valkeinen 	u16 *palette;
417f7018c21STomi Valkeinen 
418f7018c21STomi Valkeinen 	if (lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255)
419f7018c21STomi Valkeinen 		return -EINVAL;
420f7018c21STomi Valkeinen 
421f7018c21STomi Valkeinen 	palette = (u16 *)lcdc.palette_virt;
422f7018c21STomi Valkeinen 
423f7018c21STomi Valkeinen 	palette[regno] &= ~0x0fff;
424f7018c21STomi Valkeinen 	palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) |
425f7018c21STomi Valkeinen 			   (blue >> 12);
426f7018c21STomi Valkeinen 
427f7018c21STomi Valkeinen 	if (update_hw_pal) {
428f7018c21STomi Valkeinen 		disable_controller();
429f7018c21STomi Valkeinen 		omap_stop_lcd_dma();
430f7018c21STomi Valkeinen 		load_palette();
431f7018c21STomi Valkeinen 		setup_lcd_dma();
432f7018c21STomi Valkeinen 		set_load_mode(OMAP_LCDC_LOAD_FRAME);
433f7018c21STomi Valkeinen 		enable_controller();
434f7018c21STomi Valkeinen 	}
435f7018c21STomi Valkeinen 
436f7018c21STomi Valkeinen 	return 0;
437f7018c21STomi Valkeinen }
438f7018c21STomi Valkeinen 
calc_ck_div(int is_tft,int pck,int * pck_div)439f7018c21STomi Valkeinen static void calc_ck_div(int is_tft, int pck, int *pck_div)
440f7018c21STomi Valkeinen {
441f7018c21STomi Valkeinen 	unsigned long lck;
442f7018c21STomi Valkeinen 
443f7018c21STomi Valkeinen 	pck = max(1, pck);
444f7018c21STomi Valkeinen 	lck = clk_get_rate(lcdc.lcd_ck);
445f7018c21STomi Valkeinen 	*pck_div = (lck + pck - 1) / pck;
446f7018c21STomi Valkeinen 	if (is_tft)
447f7018c21STomi Valkeinen 		*pck_div = max(2, *pck_div);
448f7018c21STomi Valkeinen 	else
449f7018c21STomi Valkeinen 		*pck_div = max(3, *pck_div);
450f7018c21STomi Valkeinen 	if (*pck_div > 255) {
451f7018c21STomi Valkeinen 		/* FIXME: try to adjust logic clock divider as well */
452f7018c21STomi Valkeinen 		*pck_div = 255;
453f7018c21STomi Valkeinen 		dev_warn(lcdc.fbdev->dev, "pixclock %d kHz too low.\n",
454f7018c21STomi Valkeinen 			 pck / 1000);
455f7018c21STomi Valkeinen 	}
456f7018c21STomi Valkeinen }
457f7018c21STomi Valkeinen 
setup_regs(void)458ba168a46SJoe Perches static inline void setup_regs(void)
459f7018c21STomi Valkeinen {
460f7018c21STomi Valkeinen 	u32 l;
461f7018c21STomi Valkeinen 	struct lcd_panel *panel = lcdc.fbdev->panel;
462f7018c21STomi Valkeinen 	int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
463f7018c21STomi Valkeinen 	unsigned long lck;
464f7018c21STomi Valkeinen 	int pcd;
465f7018c21STomi Valkeinen 
466f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_CONTROL);
467f7018c21STomi Valkeinen 	l &= ~OMAP_LCDC_CTRL_LCD_TFT;
468f7018c21STomi Valkeinen 	l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0;
469f7018c21STomi Valkeinen #ifdef CONFIG_MACH_OMAP_PALMTE
470f7018c21STomi Valkeinen /* FIXME:if (machine_is_omap_palmte()) { */
471f7018c21STomi Valkeinen 		/* PalmTE uses alternate TFT setting in 8BPP mode */
472f7018c21STomi Valkeinen 		l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0;
473f7018c21STomi Valkeinen /*	} */
474f7018c21STomi Valkeinen #endif
475f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_CONTROL);
476f7018c21STomi Valkeinen 
477f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_TIMING2);
478f7018c21STomi Valkeinen 	l &= ~(((1 << 6) - 1) << 20);
479f7018c21STomi Valkeinen 	l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20;
480f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_TIMING2);
481f7018c21STomi Valkeinen 
482f7018c21STomi Valkeinen 	l = panel->x_res - 1;
483f7018c21STomi Valkeinen 	l |= (panel->hsw - 1) << 10;
484f7018c21STomi Valkeinen 	l |= (panel->hfp - 1) << 16;
485f7018c21STomi Valkeinen 	l |= (panel->hbp - 1) << 24;
486f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_TIMING0);
487f7018c21STomi Valkeinen 
488f7018c21STomi Valkeinen 	l = panel->y_res - 1;
489f7018c21STomi Valkeinen 	l |= (panel->vsw - 1) << 10;
490f7018c21STomi Valkeinen 	l |= panel->vfp << 16;
491f7018c21STomi Valkeinen 	l |= panel->vbp << 24;
492f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_TIMING1);
493f7018c21STomi Valkeinen 
494f7018c21STomi Valkeinen 	l = omap_readl(OMAP_LCDC_TIMING2);
495f7018c21STomi Valkeinen 	l &= ~0xff;
496f7018c21STomi Valkeinen 
497f7018c21STomi Valkeinen 	lck = clk_get_rate(lcdc.lcd_ck);
498f7018c21STomi Valkeinen 
499f7018c21STomi Valkeinen 	if (!panel->pcd)
500f7018c21STomi Valkeinen 		calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd);
501f7018c21STomi Valkeinen 	else {
502f7018c21STomi Valkeinen 		dev_warn(lcdc.fbdev->dev,
503f7018c21STomi Valkeinen 		    "Pixel clock divider value is obsolete.\n"
504f7018c21STomi Valkeinen 		    "Try to set pixel_clock to %lu and pcd to 0 "
505f7018c21STomi Valkeinen 		    "in drivers/video/omap/lcd_%s.c and submit a patch.\n",
506f7018c21STomi Valkeinen 			lck / panel->pcd / 1000, panel->name);
507f7018c21STomi Valkeinen 
508f7018c21STomi Valkeinen 		pcd = panel->pcd;
509f7018c21STomi Valkeinen 	}
510f7018c21STomi Valkeinen 	l |= pcd & 0xff;
511f7018c21STomi Valkeinen 	l |= panel->acb << 8;
512f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_TIMING2);
513f7018c21STomi Valkeinen 
514f7018c21STomi Valkeinen 	/* update panel info with the exact clock */
515f7018c21STomi Valkeinen 	panel->pixel_clock = lck / pcd / 1000;
516f7018c21STomi Valkeinen }
517f7018c21STomi Valkeinen 
518f7018c21STomi Valkeinen /*
519f7018c21STomi Valkeinen  * Configure the LCD controller, download the color palette and start a looped
520f7018c21STomi Valkeinen  * DMA transfer of the frame image data. Called only in internal
521f7018c21STomi Valkeinen  * controller mode.
522f7018c21STomi Valkeinen  */
omap_lcdc_set_update_mode(enum omapfb_update_mode mode)523f7018c21STomi Valkeinen static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode)
524f7018c21STomi Valkeinen {
525f7018c21STomi Valkeinen 	int r = 0;
526f7018c21STomi Valkeinen 
527f7018c21STomi Valkeinen 	if (mode != lcdc.update_mode) {
528f7018c21STomi Valkeinen 		switch (mode) {
529f7018c21STomi Valkeinen 		case OMAPFB_AUTO_UPDATE:
530f7018c21STomi Valkeinen 			setup_regs();
531f7018c21STomi Valkeinen 			load_palette();
532f7018c21STomi Valkeinen 
533f7018c21STomi Valkeinen 			/* Setup and start LCD DMA */
534f7018c21STomi Valkeinen 			setup_lcd_dma();
535f7018c21STomi Valkeinen 
536f7018c21STomi Valkeinen 			set_load_mode(OMAP_LCDC_LOAD_FRAME);
537f7018c21STomi Valkeinen 			enable_irqs(OMAP_LCDC_IRQ_DONE);
538f7018c21STomi Valkeinen 			/* This will start the actual DMA transfer */
539f7018c21STomi Valkeinen 			enable_controller();
540f7018c21STomi Valkeinen 			lcdc.update_mode = mode;
541f7018c21STomi Valkeinen 			break;
542f7018c21STomi Valkeinen 		case OMAPFB_UPDATE_DISABLED:
543f7018c21STomi Valkeinen 			disable_controller();
544f7018c21STomi Valkeinen 			omap_stop_lcd_dma();
545f7018c21STomi Valkeinen 			lcdc.update_mode = mode;
546f7018c21STomi Valkeinen 			break;
547f7018c21STomi Valkeinen 		default:
548f7018c21STomi Valkeinen 			r = -EINVAL;
549f7018c21STomi Valkeinen 		}
550f7018c21STomi Valkeinen 	}
551f7018c21STomi Valkeinen 
552f7018c21STomi Valkeinen 	return r;
553f7018c21STomi Valkeinen }
554f7018c21STomi Valkeinen 
omap_lcdc_get_update_mode(void)555f7018c21STomi Valkeinen static enum omapfb_update_mode omap_lcdc_get_update_mode(void)
556f7018c21STomi Valkeinen {
557f7018c21STomi Valkeinen 	return lcdc.update_mode;
558f7018c21STomi Valkeinen }
559f7018c21STomi Valkeinen 
560f7018c21STomi Valkeinen /* PM code called only in internal controller mode */
omap_lcdc_suspend(void)561f7018c21STomi Valkeinen static void omap_lcdc_suspend(void)
562f7018c21STomi Valkeinen {
563f7018c21STomi Valkeinen 	omap_lcdc_set_update_mode(OMAPFB_UPDATE_DISABLED);
564f7018c21STomi Valkeinen }
565f7018c21STomi Valkeinen 
omap_lcdc_resume(void)566f7018c21STomi Valkeinen static void omap_lcdc_resume(void)
567f7018c21STomi Valkeinen {
568f7018c21STomi Valkeinen 	omap_lcdc_set_update_mode(OMAPFB_AUTO_UPDATE);
569f7018c21STomi Valkeinen }
570f7018c21STomi Valkeinen 
omap_lcdc_get_caps(int plane,struct omapfb_caps * caps)571f7018c21STomi Valkeinen static void omap_lcdc_get_caps(int plane, struct omapfb_caps *caps)
572f7018c21STomi Valkeinen {
573f7018c21STomi Valkeinen 	return;
574f7018c21STomi Valkeinen }
575f7018c21STomi Valkeinen 
omap_lcdc_set_dma_callback(void (* callback)(void * data),void * data)576f7018c21STomi Valkeinen int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data)
577f7018c21STomi Valkeinen {
578f7018c21STomi Valkeinen 	BUG_ON(callback == NULL);
579f7018c21STomi Valkeinen 
580f7018c21STomi Valkeinen 	if (lcdc.dma_callback)
581f7018c21STomi Valkeinen 		return -EBUSY;
582f7018c21STomi Valkeinen 	else {
583f7018c21STomi Valkeinen 		lcdc.dma_callback = callback;
584f7018c21STomi Valkeinen 		lcdc.dma_callback_data = data;
585f7018c21STomi Valkeinen 	}
586f7018c21STomi Valkeinen 	return 0;
587f7018c21STomi Valkeinen }
588f7018c21STomi Valkeinen EXPORT_SYMBOL(omap_lcdc_set_dma_callback);
589f7018c21STomi Valkeinen 
omap_lcdc_free_dma_callback(void)590f7018c21STomi Valkeinen void omap_lcdc_free_dma_callback(void)
591f7018c21STomi Valkeinen {
592f7018c21STomi Valkeinen 	lcdc.dma_callback = NULL;
593f7018c21STomi Valkeinen }
594f7018c21STomi Valkeinen EXPORT_SYMBOL(omap_lcdc_free_dma_callback);
595f7018c21STomi Valkeinen 
lcdc_dma_handler(u16 status,void * data)596f7018c21STomi Valkeinen static void lcdc_dma_handler(u16 status, void *data)
597f7018c21STomi Valkeinen {
598f7018c21STomi Valkeinen 	if (lcdc.dma_callback)
599f7018c21STomi Valkeinen 		lcdc.dma_callback(lcdc.dma_callback_data);
600f7018c21STomi Valkeinen }
601f7018c21STomi Valkeinen 
alloc_palette_ram(void)602f7018c21STomi Valkeinen static int alloc_palette_ram(void)
603f7018c21STomi Valkeinen {
604f6e45661SLuis R. Rodriguez 	lcdc.palette_virt = dma_alloc_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE,
605f6e45661SLuis R. Rodriguez 					 &lcdc.palette_phys, GFP_KERNEL);
606f7018c21STomi Valkeinen 	if (lcdc.palette_virt == NULL) {
607f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "failed to alloc palette memory\n");
608f7018c21STomi Valkeinen 		return -ENOMEM;
609f7018c21STomi Valkeinen 	}
610f7018c21STomi Valkeinen 	memset(lcdc.palette_virt, 0, MAX_PALETTE_SIZE);
611f7018c21STomi Valkeinen 
612f7018c21STomi Valkeinen 	return 0;
613f7018c21STomi Valkeinen }
614f7018c21STomi Valkeinen 
free_palette_ram(void)615f7018c21STomi Valkeinen static void free_palette_ram(void)
616f7018c21STomi Valkeinen {
617f6e45661SLuis R. Rodriguez 	dma_free_wc(lcdc.fbdev->dev, MAX_PALETTE_SIZE, lcdc.palette_virt,
618f6e45661SLuis R. Rodriguez 		    lcdc.palette_phys);
619f7018c21STomi Valkeinen }
620f7018c21STomi Valkeinen 
alloc_fbmem(struct omapfb_mem_region * region)621f7018c21STomi Valkeinen static int alloc_fbmem(struct omapfb_mem_region *region)
622f7018c21STomi Valkeinen {
623f7018c21STomi Valkeinen 	int bpp;
624f7018c21STomi Valkeinen 	int frame_size;
625f7018c21STomi Valkeinen 	struct lcd_panel *panel = lcdc.fbdev->panel;
626f7018c21STomi Valkeinen 
627f7018c21STomi Valkeinen 	bpp = panel->bpp;
628f7018c21STomi Valkeinen 	if (bpp == 12)
629f7018c21STomi Valkeinen 		bpp = 16;
630f7018c21STomi Valkeinen 	frame_size = PAGE_ALIGN(panel->x_res * bpp / 8 * panel->y_res);
631f7018c21STomi Valkeinen 	if (region->size > frame_size)
632f7018c21STomi Valkeinen 		frame_size = region->size;
633f7018c21STomi Valkeinen 	lcdc.vram_size = frame_size;
634f6e45661SLuis R. Rodriguez 	lcdc.vram_virt = dma_alloc_wc(lcdc.fbdev->dev, lcdc.vram_size,
635f6e45661SLuis R. Rodriguez 				      &lcdc.vram_phys, GFP_KERNEL);
636f7018c21STomi Valkeinen 	if (lcdc.vram_virt == NULL) {
637f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "unable to allocate FB DMA memory\n");
638f7018c21STomi Valkeinen 		return -ENOMEM;
639f7018c21STomi Valkeinen 	}
640f7018c21STomi Valkeinen 	region->size = frame_size;
641f7018c21STomi Valkeinen 	region->paddr = lcdc.vram_phys;
642f7018c21STomi Valkeinen 	region->vaddr = lcdc.vram_virt;
643f7018c21STomi Valkeinen 	region->alloc = 1;
644f7018c21STomi Valkeinen 
645f7018c21STomi Valkeinen 	memset(lcdc.vram_virt, 0, lcdc.vram_size);
646f7018c21STomi Valkeinen 
647f7018c21STomi Valkeinen 	return 0;
648f7018c21STomi Valkeinen }
649f7018c21STomi Valkeinen 
free_fbmem(void)650f7018c21STomi Valkeinen static void free_fbmem(void)
651f7018c21STomi Valkeinen {
652f6e45661SLuis R. Rodriguez 	dma_free_wc(lcdc.fbdev->dev, lcdc.vram_size, lcdc.vram_virt,
653f6e45661SLuis R. Rodriguez 		    lcdc.vram_phys);
654f7018c21STomi Valkeinen }
655f7018c21STomi Valkeinen 
setup_fbmem(struct omapfb_mem_desc * req_md)656f7018c21STomi Valkeinen static int setup_fbmem(struct omapfb_mem_desc *req_md)
657f7018c21STomi Valkeinen {
658f7018c21STomi Valkeinen 	if (!req_md->region_cnt) {
659f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "no memory regions defined\n");
660f7018c21STomi Valkeinen 		return -EINVAL;
661f7018c21STomi Valkeinen 	}
662f7018c21STomi Valkeinen 
663f7018c21STomi Valkeinen 	if (req_md->region_cnt > 1) {
664f7018c21STomi Valkeinen 		dev_err(lcdc.fbdev->dev, "only one plane is supported\n");
665f7018c21STomi Valkeinen 		req_md->region_cnt = 1;
666f7018c21STomi Valkeinen 	}
667f7018c21STomi Valkeinen 
6687ceb1892SAaro Koskinen 	return alloc_fbmem(&req_md->region[0]);
669f7018c21STomi Valkeinen }
670f7018c21STomi Valkeinen 
omap_lcdc_init(struct omapfb_device * fbdev,int ext_mode,struct omapfb_mem_desc * req_vram)671f7018c21STomi Valkeinen static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode,
672f7018c21STomi Valkeinen 			  struct omapfb_mem_desc *req_vram)
673f7018c21STomi Valkeinen {
674f7018c21STomi Valkeinen 	int r;
675f7018c21STomi Valkeinen 	u32 l;
676f7018c21STomi Valkeinen 	int rate;
677f7018c21STomi Valkeinen 	struct clk *tc_ck;
678f7018c21STomi Valkeinen 
679f7018c21STomi Valkeinen 	lcdc.irq_mask = 0;
680f7018c21STomi Valkeinen 
681f7018c21STomi Valkeinen 	lcdc.fbdev = fbdev;
682f7018c21STomi Valkeinen 	lcdc.ext_mode = ext_mode;
683f7018c21STomi Valkeinen 
684f7018c21STomi Valkeinen 	l = 0;
685f7018c21STomi Valkeinen 	omap_writel(l, OMAP_LCDC_CONTROL);
686f7018c21STomi Valkeinen 
687f7018c21STomi Valkeinen 	/* FIXME:
688f7018c21STomi Valkeinen 	 * According to errata some platforms have a clock rate limitiation
689f7018c21STomi Valkeinen 	 */
690f7018c21STomi Valkeinen 	lcdc.lcd_ck = clk_get(fbdev->dev, "lcd_ck");
691f7018c21STomi Valkeinen 	if (IS_ERR(lcdc.lcd_ck)) {
692f7018c21STomi Valkeinen 		dev_err(fbdev->dev, "unable to access LCD clock\n");
693f7018c21STomi Valkeinen 		r = PTR_ERR(lcdc.lcd_ck);
694f7018c21STomi Valkeinen 		goto fail0;
695f7018c21STomi Valkeinen 	}
696f7018c21STomi Valkeinen 
697f7018c21STomi Valkeinen 	tc_ck = clk_get(fbdev->dev, "tc_ck");
698f7018c21STomi Valkeinen 	if (IS_ERR(tc_ck)) {
699f7018c21STomi Valkeinen 		dev_err(fbdev->dev, "unable to access TC clock\n");
700f7018c21STomi Valkeinen 		r = PTR_ERR(tc_ck);
701f7018c21STomi Valkeinen 		goto fail1;
702f7018c21STomi Valkeinen 	}
703f7018c21STomi Valkeinen 
704f7018c21STomi Valkeinen 	rate = clk_get_rate(tc_ck);
705f7018c21STomi Valkeinen 	clk_put(tc_ck);
706f7018c21STomi Valkeinen 
707f7018c21STomi Valkeinen 	if (machine_is_ams_delta())
708f7018c21STomi Valkeinen 		rate /= 4;
709f7018c21STomi Valkeinen 	r = clk_set_rate(lcdc.lcd_ck, rate);
710f7018c21STomi Valkeinen 	if (r) {
711f7018c21STomi Valkeinen 		dev_err(fbdev->dev, "failed to adjust LCD rate\n");
712f7018c21STomi Valkeinen 		goto fail1;
713f7018c21STomi Valkeinen 	}
7147e4920bfSJanusz Krzysztofik 	clk_prepare_enable(lcdc.lcd_ck);
715f7018c21STomi Valkeinen 
71681ad0f5bSArnd Bergmann 	r = request_irq(fbdev->int_irq, lcdc_irq_handler, 0, MODULE_NAME, fbdev);
717f7018c21STomi Valkeinen 	if (r) {
718f7018c21STomi Valkeinen 		dev_err(fbdev->dev, "unable to get IRQ\n");
719f7018c21STomi Valkeinen 		goto fail2;
720f7018c21STomi Valkeinen 	}
721f7018c21STomi Valkeinen 
722f7018c21STomi Valkeinen 	r = omap_request_lcd_dma(lcdc_dma_handler, NULL);
723f7018c21STomi Valkeinen 	if (r) {
724f7018c21STomi Valkeinen 		dev_err(fbdev->dev, "unable to get LCD DMA\n");
725f7018c21STomi Valkeinen 		goto fail3;
726f7018c21STomi Valkeinen 	}
727f7018c21STomi Valkeinen 
728f7018c21STomi Valkeinen 	omap_set_lcd_dma_single_transfer(ext_mode);
729f7018c21STomi Valkeinen 	omap_set_lcd_dma_ext_controller(ext_mode);
730f7018c21STomi Valkeinen 
731f7018c21STomi Valkeinen 	if (!ext_mode)
732f7018c21STomi Valkeinen 		if ((r = alloc_palette_ram()) < 0)
733f7018c21STomi Valkeinen 			goto fail4;
734f7018c21STomi Valkeinen 
735f7018c21STomi Valkeinen 	if ((r = setup_fbmem(req_vram)) < 0)
736f7018c21STomi Valkeinen 		goto fail5;
737f7018c21STomi Valkeinen 
738f7018c21STomi Valkeinen 	pr_info("omapfb: LCDC initialized\n");
739f7018c21STomi Valkeinen 
740f7018c21STomi Valkeinen 	return 0;
741f7018c21STomi Valkeinen fail5:
742f7018c21STomi Valkeinen 	if (!ext_mode)
743f7018c21STomi Valkeinen 		free_palette_ram();
744f7018c21STomi Valkeinen fail4:
745f7018c21STomi Valkeinen 	omap_free_lcd_dma();
746f7018c21STomi Valkeinen fail3:
74781ad0f5bSArnd Bergmann 	free_irq(fbdev->int_irq, lcdc.fbdev);
748f7018c21STomi Valkeinen fail2:
7497e4920bfSJanusz Krzysztofik 	clk_disable_unprepare(lcdc.lcd_ck);
750f7018c21STomi Valkeinen fail1:
751f7018c21STomi Valkeinen 	clk_put(lcdc.lcd_ck);
752f7018c21STomi Valkeinen fail0:
753f7018c21STomi Valkeinen 	return r;
754f7018c21STomi Valkeinen }
755f7018c21STomi Valkeinen 
omap_lcdc_cleanup(void)756f7018c21STomi Valkeinen static void omap_lcdc_cleanup(void)
757f7018c21STomi Valkeinen {
758f7018c21STomi Valkeinen 	if (!lcdc.ext_mode)
759f7018c21STomi Valkeinen 		free_palette_ram();
7607ceb1892SAaro Koskinen 	free_fbmem();
761f7018c21STomi Valkeinen 	omap_free_lcd_dma();
76281ad0f5bSArnd Bergmann 	free_irq(lcdc.fbdev->int_irq, lcdc.fbdev);
7637e4920bfSJanusz Krzysztofik 	clk_disable_unprepare(lcdc.lcd_ck);
764f7018c21STomi Valkeinen 	clk_put(lcdc.lcd_ck);
765f7018c21STomi Valkeinen }
766f7018c21STomi Valkeinen 
767f7018c21STomi Valkeinen const struct lcd_ctrl omap1_int_ctrl = {
768f7018c21STomi Valkeinen 	.name			= "internal",
769f7018c21STomi Valkeinen 	.init			= omap_lcdc_init,
770f7018c21STomi Valkeinen 	.cleanup		= omap_lcdc_cleanup,
771f7018c21STomi Valkeinen 	.get_caps		= omap_lcdc_get_caps,
772f7018c21STomi Valkeinen 	.set_update_mode	= omap_lcdc_set_update_mode,
773f7018c21STomi Valkeinen 	.get_update_mode	= omap_lcdc_get_update_mode,
774f7018c21STomi Valkeinen 	.update_window		= NULL,
775f7018c21STomi Valkeinen 	.suspend		= omap_lcdc_suspend,
776f7018c21STomi Valkeinen 	.resume			= omap_lcdc_resume,
777f7018c21STomi Valkeinen 	.setup_plane		= omap_lcdc_setup_plane,
778f7018c21STomi Valkeinen 	.enable_plane		= omap_lcdc_enable_plane,
779f7018c21STomi Valkeinen 	.setcolreg		= omap_lcdc_setcolreg,
780f7018c21STomi Valkeinen };
781