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