aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/fsl-diu-fb.c
diff options
context:
space:
mode:
authorYork Sun <yorksun@freescale.com>2008-04-28 05:15:34 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-28 11:58:40 -0400
commit9b53a9e28a34ed82516191dc46ae018e0e899546 (patch)
tree3e3addb97efae2ecb66435f41da62ae786d26adf /drivers/video/fsl-diu-fb.c
parent7d345b2253f92804948d66f4db17a49c1932b9a3 (diff)
fbdev: powerpc: driver for Freescale 8610 and 5121 DIU
The following features are supported: plane 0 works as a regular frame buffer, can be accessed by /dev/fb0 plane 1 has two AOIs (area of interest), can be accessed by /dev/fb1 and /dev/fb2 plane 2 has two AOIs, can be accessed by /dev/fb3 and /dev/fb4 Special ioctls support AOIs All /dev/fb* can be used as regular frame buffer devices, except hardware change can only be made through /dev/fb0. Changing pixel clock has no effect on other fbs. Limitation of usage of AOIs: AOIs on the same plane can not be horizonally overlapped AOIs have horizonal order, i.e. AOI0 should be always on top of AOI1 AOIs can not beyond phisical display area. Application should check AOI geometry before changing physical resolution on /dev/fb0 required command line parameters to preallocate memory for frame buffer diufb. optional command line parameters to set modes and monitor video=fslfb:[resolution][,bpp][,monitor] Syntax: Resolution xres x yres-bpp@refresh_rate, the -bpp and @refresh_rate are optional eg, 1024x768, 1280x1024, 1280x1024-32, 1280x1024@60, 1280x1024-32@60, 1280x480-32@60 Bpp bpp=32, bpp=24, or bpp=16 Monitor monitor=0, monitor=1, monitor=2 0 is DVI 1 is Single link LVDS 2 is Double link LVDS Note: switching monitor is a board feather, not DIU feather. MPC8610HPCD has three monitor ports to swtich to. MPC5121ADS doesn't have additional monitor port. So switching monirot port for MPC5121ADS has no effect. If compiled as a module, it takes pamameters mode, bpp, monitor with the same syntax above. Signed-off-by: York Sun <yorksun@freescale.com> Signed-off-by: Timur Tabi <timur@freescale.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: "Antonino A. Daplas" <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/fsl-diu-fb.c')
-rw-r--r--drivers/video/fsl-diu-fb.c1721
1 files changed, 1721 insertions, 0 deletions
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
new file mode 100644
index 000000000000..b50bb03cb5ab
--- /dev/null
+++ b/drivers/video/fsl-diu-fb.c
@@ -0,0 +1,1721 @@
1/*
2 * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
3 *
4 * Freescale DIU Frame Buffer device driver
5 *
6 * Authors: Hongjun Chen <hong-jun.chen@freescale.com>
7 * Paul Widmer <paul.widmer@freescale.com>
8 * Srikanth Srinivasan <srikanth.srinivasan@freescale.com>
9 * York Sun <yorksun@freescale.com>
10 *
11 * Based on imxfb.c Copyright (C) 2004 S.Hauer, Pengutronix
12 *
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 2 of the License, or (at your
16 * option) any later version.
17 *
18 */
19
20#include <linux/module.h>
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/string.h>
24#include <linux/slab.h>
25#include <linux/fb.h>
26#include <linux/init.h>
27#include <linux/dma-mapping.h>
28#include <linux/platform_device.h>
29#include <linux/interrupt.h>
30#include <linux/clk.h>
31#include <linux/uaccess.h>
32#include <linux/vmalloc.h>
33
34#include <linux/of_platform.h>
35
36#include <sysdev/fsl_soc.h>
37#include "fsl-diu-fb.h"
38
39/*
40 * These parameters give default parameters
41 * for video output 1024x768,
42 * FIXME - change timing to proper amounts
43 * hsync 31.5kHz, vsync 60Hz
44 */
45static struct fb_videomode __devinitdata fsl_diu_default_mode = {
46 .refresh = 60,
47 .xres = 1024,
48 .yres = 768,
49 .pixclock = 15385,
50 .left_margin = 160,
51 .right_margin = 24,
52 .upper_margin = 29,
53 .lower_margin = 3,
54 .hsync_len = 136,
55 .vsync_len = 6,
56 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
57 .vmode = FB_VMODE_NONINTERLACED
58};
59
60static struct fb_videomode __devinitdata fsl_diu_mode_db[] = {
61 {
62 .name = "1024x768-60",
63 .refresh = 60,
64 .xres = 1024,
65 .yres = 768,
66 .pixclock = 15385,
67 .left_margin = 160,
68 .right_margin = 24,
69 .upper_margin = 29,
70 .lower_margin = 3,
71 .hsync_len = 136,
72 .vsync_len = 6,
73 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
74 .vmode = FB_VMODE_NONINTERLACED
75 },
76 {
77 .name = "1024x768-70",
78 .refresh = 70,
79 .xres = 1024,
80 .yres = 768,
81 .pixclock = 16886,
82 .left_margin = 3,
83 .right_margin = 3,
84 .upper_margin = 2,
85 .lower_margin = 2,
86 .hsync_len = 40,
87 .vsync_len = 18,
88 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
89 .vmode = FB_VMODE_NONINTERLACED
90 },
91 {
92 .name = "1024x768-75",
93 .refresh = 75,
94 .xres = 1024,
95 .yres = 768,
96 .pixclock = 15009,
97 .left_margin = 3,
98 .right_margin = 3,
99 .upper_margin = 2,
100 .lower_margin = 2,
101 .hsync_len = 80,
102 .vsync_len = 32,
103 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
104 .vmode = FB_VMODE_NONINTERLACED
105 },
106 {
107 .name = "1280x1024-60",
108 .refresh = 60,
109 .xres = 1280,
110 .yres = 1024,
111 .pixclock = 9375,
112 .left_margin = 38,
113 .right_margin = 128,
114 .upper_margin = 2,
115 .lower_margin = 7,
116 .hsync_len = 216,
117 .vsync_len = 37,
118 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
119 .vmode = FB_VMODE_NONINTERLACED
120 },
121 {
122 .name = "1280x1024-70",
123 .refresh = 70,
124 .xres = 1280,
125 .yres = 1024,
126 .pixclock = 9380,
127 .left_margin = 6,
128 .right_margin = 6,
129 .upper_margin = 4,
130 .lower_margin = 4,
131 .hsync_len = 60,
132 .vsync_len = 94,
133 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
134 .vmode = FB_VMODE_NONINTERLACED
135 },
136 {
137 .name = "1280x1024-75",
138 .refresh = 75,
139 .xres = 1280,
140 .yres = 1024,
141 .pixclock = 9380,
142 .left_margin = 6,
143 .right_margin = 6,
144 .upper_margin = 4,
145 .lower_margin = 4,
146 .hsync_len = 60,
147 .vsync_len = 15,
148 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
149 .vmode = FB_VMODE_NONINTERLACED
150 },
151 {
152 .name = "320x240", /* for AOI only */
153 .refresh = 60,
154 .xres = 320,
155 .yres = 240,
156 .pixclock = 15385,
157 .left_margin = 0,
158 .right_margin = 0,
159 .upper_margin = 0,
160 .lower_margin = 0,
161 .hsync_len = 0,
162 .vsync_len = 0,
163 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
164 .vmode = FB_VMODE_NONINTERLACED
165 },
166 {
167 .name = "1280x480-60",
168 .refresh = 60,
169 .xres = 1280,
170 .yres = 480,
171 .pixclock = 18939,
172 .left_margin = 353,
173 .right_margin = 47,
174 .upper_margin = 39,
175 .lower_margin = 4,
176 .hsync_len = 8,
177 .vsync_len = 2,
178 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
179 .vmode = FB_VMODE_NONINTERLACED
180 },
181};
182
183static char *fb_mode = "1024x768-32@60";
184static unsigned long default_bpp = 32;
185static int monitor_port;
186
187#if defined(CONFIG_NOT_COHERENT_CACHE)
188static u8 *coherence_data;
189static size_t coherence_data_size;
190static unsigned int d_cache_line_size;
191#endif
192
193static DEFINE_SPINLOCK(diu_lock);
194
195struct fsl_diu_data {
196 struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
197 /*FSL_AOI_NUM has one dummy AOI */
198 struct device_attribute dev_attr;
199 struct diu_ad *dummy_ad;
200 void *dummy_aoi_virt;
201 unsigned int irq;
202 int fb_enabled;
203 int monitor_port;
204};
205
206struct mfb_info {
207 int index;
208 int type;
209 char *id;
210 int registered;
211 int blank;
212 unsigned long pseudo_palette[16];
213 struct diu_ad *ad;
214 int cursor_reset;
215 unsigned char g_alpha;
216 unsigned int count;
217 int x_aoi_d; /* aoi display x offset to physical screen */
218 int y_aoi_d; /* aoi display y offset to physical screen */
219 struct fsl_diu_data *parent;
220};
221
222
223static struct mfb_info mfb_template[] = {
224 { /* AOI 0 for plane 0 */
225 .index = 0,
226 .type = MFB_TYPE_OUTPUT,
227 .id = "Panel0",
228 .registered = 0,
229 .count = 0,
230 .x_aoi_d = 0,
231 .y_aoi_d = 0,
232 },
233 { /* AOI 0 for plane 1 */
234 .index = 1,
235 .type = MFB_TYPE_OUTPUT,
236 .id = "Panel1 AOI0",
237 .registered = 0,
238 .g_alpha = 0xff,
239 .count = 0,
240 .x_aoi_d = 0,
241 .y_aoi_d = 0,
242 },
243 { /* AOI 1 for plane 1 */
244 .index = 2,
245 .type = MFB_TYPE_OUTPUT,
246 .id = "Panel1 AOI1",
247 .registered = 0,
248 .g_alpha = 0xff,
249 .count = 0,
250 .x_aoi_d = 0,
251 .y_aoi_d = 480,
252 },
253 { /* AOI 0 for plane 2 */
254 .index = 3,
255 .type = MFB_TYPE_OUTPUT,
256 .id = "Panel2 AOI0",
257 .registered = 0,
258 .g_alpha = 0xff,
259 .count = 0,
260 .x_aoi_d = 640,
261 .y_aoi_d = 0,
262 },
263 { /* AOI 1 for plane 2 */
264 .index = 4,
265 .type = MFB_TYPE_OUTPUT,
266 .id = "Panel2 AOI1",
267 .registered = 0,
268 .g_alpha = 0xff,
269 .count = 0,
270 .x_aoi_d = 640,
271 .y_aoi_d = 480,
272 },
273};
274
275static struct diu_hw dr = {
276 .mode = MFB_MODE1,
277 .reg_lock = __SPIN_LOCK_UNLOCKED(diu_hw.reg_lock),
278};
279
280static struct diu_pool pool;
281
282/* To allocate memory for framebuffer. First try __get_free_pages(). If it
283 * fails, try rh_alloc. The reason is __get_free_pages() cannot allocate
284 * very large memory (more than 4MB). We don't want to allocate all memory
285 * in rheap since small memory allocation/deallocation will fragment the
286 * rheap and make the furture large allocation fail.
287 */
288
289void *fsl_diu_alloc(unsigned long size, phys_addr_t *phys)
290{
291 void *virt;
292
293 pr_debug("size=%lu\n", size);
294
295 virt = (void *)__get_free_pages(GFP_DMA | __GFP_ZERO, get_order(size));
296 if (virt) {
297 *phys = virt_to_phys(virt);
298 pr_debug("virt %p, phys=%llx\n", virt, (uint64_t) *phys);
299 return virt;
300 }
301 if (!diu_ops.diu_mem) {
302 printk(KERN_INFO "%s: no diu_mem."
303 " To reserve more memory, put 'diufb=15M' "
304 "in the command line\n", __func__);
305 return NULL;
306 }
307
308 virt = (void *)rh_alloc(&diu_ops.diu_rh_info, size, "DIU");
309 if (virt) {
310 *phys = virt_to_bus(virt);
311 memset(virt, 0, size);
312 }
313
314 pr_debug("rh virt=%p phys=%lx\n", virt, *phys);
315
316 return virt;
317}
318
319void fsl_diu_free(void *p, unsigned long size)
320{
321 pr_debug("p=%p size=%lu\n", p, size);
322
323 if (!p)
324 return;
325
326 if ((p >= diu_ops.diu_mem) &&
327 (p < (diu_ops.diu_mem + diu_ops.diu_size))) {
328 pr_debug("rh\n");
329 rh_free(&diu_ops.diu_rh_info, (unsigned long) p);
330 } else {
331 pr_debug("dma\n");
332 free_pages((unsigned long)p, get_order(size));
333 }
334}
335
336static int fsl_diu_enable_panel(struct fb_info *info)
337{
338 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
339 struct diu *hw = dr.diu_reg;
340 struct diu_ad *ad = mfbi->ad;
341 struct fsl_diu_data *machine_data = mfbi->parent;
342 int res = 0;
343
344 pr_debug("enable_panel index %d\n", mfbi->index);
345 if (mfbi->type != MFB_TYPE_OFF) {
346 switch (mfbi->index) {
347 case 0: /* plane 0 */
348 if (hw->desc[0] != ad->paddr)
349 out_be32(&hw->desc[0], ad->paddr);
350 break;
351 case 1: /* plane 1 AOI 0 */
352 cmfbi = machine_data->fsl_diu_info[2]->par;
353 if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
354 if (cmfbi->count > 0) /* AOI1 open */
355 ad->next_ad =
356 cpu_to_le32(cmfbi->ad->paddr);
357 else
358 ad->next_ad = 0;
359 out_be32(&hw->desc[1], ad->paddr);
360 }
361 break;
362 case 3: /* plane 2 AOI 0 */
363 cmfbi = machine_data->fsl_diu_info[4]->par;
364 if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
365 if (cmfbi->count > 0) /* AOI1 open */
366 ad->next_ad =
367 cpu_to_le32(cmfbi->ad->paddr);
368 else
369 ad->next_ad = 0;
370 out_be32(&hw->desc[2], ad->paddr);
371 }
372 break;
373 case 2: /* plane 1 AOI 1 */
374 pmfbi = machine_data->fsl_diu_info[1]->par;
375 ad->next_ad = 0;
376 if (hw->desc[1] == machine_data->dummy_ad->paddr)
377 out_be32(&hw->desc[1], ad->paddr);
378 else /* AOI0 open */
379 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
380 break;
381 case 4: /* plane 2 AOI 1 */
382 pmfbi = machine_data->fsl_diu_info[3]->par;
383 ad->next_ad = 0;
384 if (hw->desc[2] == machine_data->dummy_ad->paddr)
385 out_be32(&hw->desc[2], ad->paddr);
386 else /* AOI0 was open */
387 pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
388 break;
389 default:
390 res = -EINVAL;
391 break;
392 }
393 } else
394 res = -EINVAL;
395 return res;
396}
397
398static int fsl_diu_disable_panel(struct fb_info *info)
399{
400 struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
401 struct diu *hw = dr.diu_reg;
402 struct diu_ad *ad = mfbi->ad;
403 struct fsl_diu_data *machine_data = mfbi->parent;
404 int res = 0;
405
406 switch (mfbi->index) {
407 case 0: /* plane 0 */
408 if (hw->desc[0] != machine_data->dummy_ad->paddr)
409 out_be32(&hw->desc[0],
410 machine_data->dummy_ad->paddr);
411 break;
412 case 1: /* plane 1 AOI 0 */
413 cmfbi = machine_data->fsl_diu_info[2]->par;
414 if (cmfbi->count > 0) /* AOI1 is open */
415 out_be32(&hw->desc[1], cmfbi->ad->paddr);
416 /* move AOI1 to the first */
417 else /* AOI1 was closed */
418 out_be32(&hw->desc[1],
419 machine_data->dummy_ad->paddr);
420 /* close AOI 0 */
421 break;
422 case 3: /* plane 2 AOI 0 */
423 cmfbi = machine_data->fsl_diu_info[4]->par;
424 if (cmfbi->count > 0) /* AOI1 is open */
425 out_be32(&hw->desc[2], cmfbi->ad->paddr);
426 /* move AOI1 to the first */
427 else /* AOI1 was closed */
428 out_be32(&hw->desc[2],
429 machine_data->dummy_ad->paddr);
430 /* close AOI 0 */
431 break;
432 case 2: /* plane 1 AOI 1 */
433 pmfbi = machine_data->fsl_diu_info[1]->par;
434 if (hw->desc[1] != ad->paddr) {
435 /* AOI1 is not the first in the chain */
436 if (pmfbi->count > 0)
437 /* AOI0 is open, must be the first */
438 pmfbi->ad->next_ad = 0;
439 } else /* AOI1 is the first in the chain */
440 out_be32(&hw->desc[1], machine_data->dummy_ad->paddr);
441 /* close AOI 1 */
442 break;
443 case 4: /* plane 2 AOI 1 */
444 pmfbi = machine_data->fsl_diu_info[3]->par;
445 if (hw->desc[2] != ad->paddr) {
446 /* AOI1 is not the first in the chain */
447 if (pmfbi->count > 0)
448 /* AOI0 is open, must be the first */
449 pmfbi->ad->next_ad = 0;
450 } else /* AOI1 is the first in the chain */
451 out_be32(&hw->desc[2], machine_data->dummy_ad->paddr);
452 /* close AOI 1 */
453 break;
454 default:
455 res = -EINVAL;
456 break;
457 }
458
459 return res;
460}
461
462static void enable_lcdc(struct fb_info *info)
463{
464 struct diu *hw = dr.diu_reg;
465 struct mfb_info *mfbi = info->par;
466 struct fsl_diu_data *machine_data = mfbi->parent;
467
468 if (!machine_data->fb_enabled) {
469 out_be32(&hw->diu_mode, dr.mode);
470 machine_data->fb_enabled++;
471 }
472}
473
474static void disable_lcdc(struct fb_info *info)
475{
476 struct diu *hw = dr.diu_reg;
477 struct mfb_info *mfbi = info->par;
478 struct fsl_diu_data *machine_data = mfbi->parent;
479
480 if (machine_data->fb_enabled) {
481 out_be32(&hw->diu_mode, 0);
482 machine_data->fb_enabled = 0;
483 }
484}
485
486static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
487 struct fb_info *info)
488{
489 struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
490 struct fsl_diu_data *machine_data = mfbi->parent;
491 int available_height, upper_aoi_bottom, index = mfbi->index;
492 int lower_aoi_is_open, upper_aoi_is_open;
493 __u32 base_plane_width, base_plane_height, upper_aoi_height;
494
495 base_plane_width = machine_data->fsl_diu_info[0]->var.xres;
496 base_plane_height = machine_data->fsl_diu_info[0]->var.yres;
497
498 switch (index) {
499 case 0:
500 if (mfbi->x_aoi_d != 0)
501 mfbi->x_aoi_d = 0;
502 if (mfbi->y_aoi_d != 0)
503 mfbi->y_aoi_d = 0;
504 break;
505 case 1: /* AOI 0 */
506 case 3:
507 lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par;
508 lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
509 if (var->xres > base_plane_width)
510 var->xres = base_plane_width;
511 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
512 mfbi->x_aoi_d = base_plane_width - var->xres;
513
514 if (lower_aoi_is_open)
515 available_height = lower_aoi_mfbi->y_aoi_d;
516 else
517 available_height = base_plane_height;
518 if (var->yres > available_height)
519 var->yres = available_height;
520 if ((mfbi->y_aoi_d + var->yres) > available_height)
521 mfbi->y_aoi_d = available_height - var->yres;
522 break;
523 case 2: /* AOI 1 */
524 case 4:
525 upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par;
526 upper_aoi_height =
527 machine_data->fsl_diu_info[index-1]->var.yres;
528 upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
529 upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
530 if (var->xres > base_plane_width)
531 var->xres = base_plane_width;
532 if ((mfbi->x_aoi_d + var->xres) > base_plane_width)
533 mfbi->x_aoi_d = base_plane_width - var->xres;
534 if (mfbi->y_aoi_d < 0)
535 mfbi->y_aoi_d = 0;
536 if (upper_aoi_is_open) {
537 if (mfbi->y_aoi_d < upper_aoi_bottom)
538 mfbi->y_aoi_d = upper_aoi_bottom;
539 available_height = base_plane_height
540 - upper_aoi_bottom;
541 } else
542 available_height = base_plane_height;
543 if (var->yres > available_height)
544 var->yres = available_height;
545 if ((mfbi->y_aoi_d + var->yres) > base_plane_height)
546 mfbi->y_aoi_d = base_plane_height - var->yres;
547 break;
548 }
549}
550/*
551 * Checks to see if the hardware supports the state requested by var passed
552 * in. This function does not alter the hardware state! If the var passed in
553 * is slightly off by what the hardware can support then we alter the var
554 * PASSED in to what we can do. If the hardware doesn't support mode change
555 * a -EINVAL will be returned by the upper layers.
556 */
557static int fsl_diu_check_var(struct fb_var_screeninfo *var,
558 struct fb_info *info)
559{
560 unsigned long htotal, vtotal;
561
562 pr_debug("check_var xres: %d\n", var->xres);
563 pr_debug("check_var yres: %d\n", var->yres);
564
565 if (var->xres_virtual < var->xres)
566 var->xres_virtual = var->xres;
567 if (var->yres_virtual < var->yres)
568 var->yres_virtual = var->yres;
569
570 if (var->xoffset < 0)
571 var->xoffset = 0;
572
573 if (var->yoffset < 0)
574 var->yoffset = 0;
575
576 if (var->xoffset + info->var.xres > info->var.xres_virtual)
577 var->xoffset = info->var.xres_virtual - info->var.xres;
578
579 if (var->yoffset + info->var.yres > info->var.yres_virtual)
580 var->yoffset = info->var.yres_virtual - info->var.yres;
581
582 if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) &&
583 (var->bits_per_pixel != 16))
584 var->bits_per_pixel = default_bpp;
585
586 switch (var->bits_per_pixel) {
587 case 16:
588 var->red.length = 5;
589 var->red.offset = 11;
590 var->red.msb_right = 0;
591
592 var->green.length = 6;
593 var->green.offset = 5;
594 var->green.msb_right = 0;
595
596 var->blue.length = 5;
597 var->blue.offset = 0;
598 var->blue.msb_right = 0;
599
600 var->transp.length = 0;
601 var->transp.offset = 0;
602 var->transp.msb_right = 0;
603 break;
604 case 24:
605 var->red.length = 8;
606 var->red.offset = 0;
607 var->red.msb_right = 0;
608
609 var->green.length = 8;
610 var->green.offset = 8;
611 var->green.msb_right = 0;
612
613 var->blue.length = 8;
614 var->blue.offset = 16;
615 var->blue.msb_right = 0;
616
617 var->transp.length = 0;
618 var->transp.offset = 0;
619 var->transp.msb_right = 0;
620 break;
621 case 32:
622 var->red.length = 8;
623 var->red.offset = 16;
624 var->red.msb_right = 0;
625
626 var->green.length = 8;
627 var->green.offset = 8;
628 var->green.msb_right = 0;
629
630 var->blue.length = 8;
631 var->blue.offset = 0;
632 var->blue.msb_right = 0;
633
634 var->transp.length = 8;
635 var->transp.offset = 24;
636 var->transp.msb_right = 0;
637
638 break;
639 }
640 /* If the pixclock is below the minimum spec'd value then set to
641 * refresh rate for 60Hz since this is supported by most monitors.
642 * Refer to Documentation/fb/ for calculations.
643 */
644 if ((var->pixclock < MIN_PIX_CLK) || (var->pixclock > MAX_PIX_CLK)) {
645 htotal = var->xres + var->right_margin + var->hsync_len +
646 var->left_margin;
647 vtotal = var->yres + var->lower_margin + var->vsync_len +
648 var->upper_margin;
649 var->pixclock = (vtotal * htotal * 6UL) / 100UL;
650 var->pixclock = KHZ2PICOS(var->pixclock);
651 pr_debug("pixclock set for 60Hz refresh = %u ps\n",
652 var->pixclock);
653 }
654
655 var->height = -1;
656 var->width = -1;
657 var->grayscale = 0;
658
659 /* Copy nonstd field to/from sync for fbset usage */
660 var->sync |= var->nonstd;
661 var->nonstd |= var->sync;
662
663 adjust_aoi_size_position(var, info);
664 return 0;
665}
666
667static void set_fix(struct fb_info *info)
668{
669 struct fb_fix_screeninfo *fix = &info->fix;
670 struct fb_var_screeninfo *var = &info->var;
671 struct mfb_info *mfbi = info->par;
672
673 strncpy(fix->id, mfbi->id, strlen(mfbi->id));
674 fix->line_length = var->xres_virtual * var->bits_per_pixel / 8;
675 fix->type = FB_TYPE_PACKED_PIXELS;
676 fix->accel = FB_ACCEL_NONE;
677 fix->visual = FB_VISUAL_TRUECOLOR;
678 fix->xpanstep = 1;
679 fix->ypanstep = 1;
680}
681
682static void update_lcdc(struct fb_info *info)
683{
684 struct fb_var_screeninfo *var = &info->var;
685 struct mfb_info *mfbi = info->par;
686 struct fsl_diu_data *machine_data = mfbi->parent;
687 struct diu *hw;
688 int i, j;
689 char __iomem *cursor_base, *gamma_table_base;
690
691 u32 temp;
692
693 hw = dr.diu_reg;
694
695 if (mfbi->type == MFB_TYPE_OFF) {
696 fsl_diu_disable_panel(info);
697 return;
698 }
699
700 diu_ops.set_monitor_port(machine_data->monitor_port);
701 gamma_table_base = pool.gamma.vaddr;
702 cursor_base = pool.cursor.vaddr;
703 /* Prep for DIU init - gamma table, cursor table */
704
705 for (i = 0; i <= 2; i++)
706 for (j = 0; j <= 255; j++)
707 *gamma_table_base++ = j;
708
709 diu_ops.set_gamma_table(machine_data->monitor_port, pool.gamma.vaddr);
710
711 pr_debug("update-lcdc: HW - %p\n Disabling DIU\n", hw);
712 disable_lcdc(info);
713
714 /* Program DIU registers */
715
716 out_be32(&hw->gamma, pool.gamma.paddr);
717 out_be32(&hw->cursor, pool.cursor.paddr);
718
719 out_be32(&hw->bgnd, 0x007F7F7F); /* BGND */
720 out_be32(&hw->bgnd_wb, 0); /* BGND_WB */
721 out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
722 /* DISP SIZE */
723 pr_debug("DIU xres: %d\n", var->xres);
724 pr_debug("DIU yres: %d\n", var->yres);
725
726 out_be32(&hw->wb_size, 0); /* WB SIZE */
727 out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
728
729 /* Horizontal and vertical configuration register */
730 temp = var->left_margin << 22 | /* BP_H */
731 var->hsync_len << 11 | /* PW_H */
732 var->right_margin; /* FP_H */
733
734 out_be32(&hw->hsyn_para, temp);
735
736 temp = var->upper_margin << 22 | /* BP_V */
737 var->vsync_len << 11 | /* PW_V */
738 var->lower_margin; /* FP_V */
739
740 out_be32(&hw->vsyn_para, temp);
741
742 pr_debug("DIU right_margin - %d\n", var->right_margin);
743 pr_debug("DIU left_margin - %d\n", var->left_margin);
744 pr_debug("DIU hsync_len - %d\n", var->hsync_len);
745 pr_debug("DIU upper_margin - %d\n", var->upper_margin);
746 pr_debug("DIU lower_margin - %d\n", var->lower_margin);
747 pr_debug("DIU vsync_len - %d\n", var->vsync_len);
748 pr_debug("DIU HSYNC - 0x%08x\n", hw->hsyn_para);
749 pr_debug("DIU VSYNC - 0x%08x\n", hw->vsyn_para);
750
751 diu_ops.set_pixel_clock(var->pixclock);
752
753 out_be32(&hw->syn_pol, 0); /* SYNC SIGNALS POLARITY */
754 out_be32(&hw->thresholds, 0x00037800); /* The Thresholds */
755 out_be32(&hw->int_status, 0); /* INTERRUPT STATUS */
756 out_be32(&hw->plut, 0x01F5F666);
757
758 /* Enable the DIU */
759 enable_lcdc(info);
760}
761
762static int map_video_memory(struct fb_info *info)
763{
764 phys_addr_t phys;
765
766 pr_debug("info->var.xres_virtual = %d\n", info->var.xres_virtual);
767 pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
768 pr_debug("info->fix.line_length = %d\n", info->fix.line_length);
769
770 info->fix.smem_len = info->fix.line_length * info->var.yres_virtual;
771 pr_debug("MAP_VIDEO_MEMORY: smem_len = %d\n", info->fix.smem_len);
772 info->screen_base = fsl_diu_alloc(info->fix.smem_len, &phys);
773 if (info->screen_base == 0) {
774 printk(KERN_ERR "Unable to allocate fb memory\n");
775 return -ENOMEM;
776 }
777 info->fix.smem_start = (unsigned long) phys;
778 info->screen_size = info->fix.smem_len;
779
780 pr_debug("Allocated fb @ paddr=0x%08lx, size=%d.\n",
781 info->fix.smem_start,
782 info->fix.smem_len);
783 pr_debug("screen base %p\n", info->screen_base);
784
785 return 0;
786}
787
788static void unmap_video_memory(struct fb_info *info)
789{
790 fsl_diu_free(info->screen_base, info->fix.smem_len);
791 info->screen_base = 0;
792 info->fix.smem_start = 0;
793 info->fix.smem_len = 0;
794}
795
796/*
797 * Using the fb_var_screeninfo in fb_info we set the resolution of this
798 * particular framebuffer. This function alters the fb_fix_screeninfo stored
799 * in fb_info. It does not alter var in fb_info since we are using that
800 * data. This means we depend on the data in var inside fb_info to be
801 * supported by the hardware. fsl_diu_check_var is always called before
802 * fsl_diu_set_par to ensure this.
803 */
804static int fsl_diu_set_par(struct fb_info *info)
805{
806 unsigned long len;
807 struct fb_var_screeninfo *var = &info->var;
808 struct mfb_info *mfbi = info->par;
809 struct fsl_diu_data *machine_data = mfbi->parent;
810 struct diu_ad *ad = mfbi->ad;
811 struct diu *hw;
812
813 hw = dr.diu_reg;
814
815 set_fix(info);
816 mfbi->cursor_reset = 1;
817
818 len = info->var.yres_virtual * info->fix.line_length;
819 /* Alloc & dealloc each time resolution/bpp change */
820 if (len != info->fix.smem_len) {
821 if (info->fix.smem_start)
822 unmap_video_memory(info);
823 pr_debug("SET PAR: smem_len = %d\n", info->fix.smem_len);
824
825 /* Memory allocation for framebuffer */
826 if (map_video_memory(info)) {
827 printk(KERN_ERR "Unable to allocate fb memory 1\n");
828 return -ENOMEM;
829 }
830 }
831
832 ad->pix_fmt =
833 diu_ops.get_pixel_format(var->bits_per_pixel,
834 machine_data->monitor_port);
835 ad->addr = cpu_to_le32(info->fix.smem_start);
836 ad->src_size_g_alpha = cpu_to_le32((var->yres << 12) |
837 var->xres) | mfbi->g_alpha;
838 /* fix me. AOI should not be greater than display size */
839 ad->aoi_size = cpu_to_le32((var->yres << 16) | var->xres);
840 ad->offset_xyi = 0;
841 ad->offset_xyd = cpu_to_le32((mfbi->y_aoi_d << 16) | mfbi->x_aoi_d);
842
843 /* Disable chroma keying function */
844 ad->ckmax_r = 0;
845 ad->ckmax_g = 0;
846 ad->ckmax_b = 0;
847
848 ad->ckmin_r = 255;
849 ad->ckmin_g = 255;
850 ad->ckmin_b = 255;
851
852 if (mfbi->index == 0)
853 update_lcdc(info);
854 return 0;
855}
856
857static inline __u32 CNVT_TOHW(__u32 val, __u32 width)
858{
859 return ((val<<width) + 0x7FFF - val)>>16;
860}
861
862/*
863 * Set a single color register. The values supplied have a 16 bit magnitude
864 * which needs to be scaled in this function for the hardware. Things to take
865 * into consideration are how many color registers, if any, are supported with
866 * the current color visual. With truecolor mode no color palettes are
867 * supported. Here a psuedo palette is created which we store the value in
868 * pseudo_palette in struct fb_info. For pseudocolor mode we have a limited
869 * color palette.
870 */
871static int fsl_diu_setcolreg(unsigned regno, unsigned red, unsigned green,
872 unsigned blue, unsigned transp, struct fb_info *info)
873{
874 int ret = 1;
875
876 /*
877 * If greyscale is true, then we convert the RGB value
878 * to greyscale no matter what visual we are using.
879 */
880 if (info->var.grayscale)
881 red = green = blue = (19595 * red + 38470 * green +
882 7471 * blue) >> 16;
883 switch (info->fix.visual) {
884 case FB_VISUAL_TRUECOLOR:
885 /*
886 * 16-bit True Colour. We encode the RGB value
887 * according to the RGB bitfield information.
888 */
889 if (regno < 16) {
890 u32 *pal = info->pseudo_palette;
891 u32 v;
892
893 red = CNVT_TOHW(red, info->var.red.length);
894 green = CNVT_TOHW(green, info->var.green.length);
895 blue = CNVT_TOHW(blue, info->var.blue.length);
896 transp = CNVT_TOHW(transp, info->var.transp.length);
897
898 v = (red << info->var.red.offset) |
899 (green << info->var.green.offset) |
900 (blue << info->var.blue.offset) |
901 (transp << info->var.transp.offset);
902
903 pal[regno] = v;
904 ret = 0;
905 }
906 break;
907 case FB_VISUAL_STATIC_PSEUDOCOLOR:
908 case FB_VISUAL_PSEUDOCOLOR:
909 break;
910 }
911
912 return ret;
913}
914
915/*
916 * Pan (or wrap, depending on the `vmode' field) the display using the
917 * 'xoffset' and 'yoffset' fields of the 'var' structure. If the values
918 * don't fit, return -EINVAL.
919 */
920static int fsl_diu_pan_display(struct fb_var_screeninfo *var,
921 struct fb_info *info)
922{
923 if ((info->var.xoffset == var->xoffset) &&
924 (info->var.yoffset == var->yoffset))
925 return 0; /* No change, do nothing */
926
927 if (var->xoffset < 0 || var->yoffset < 0
928 || var->xoffset + info->var.xres > info->var.xres_virtual
929 || var->yoffset + info->var.yres > info->var.yres_virtual)
930 return -EINVAL;
931
932 info->var.xoffset = var->xoffset;
933 info->var.yoffset = var->yoffset;
934
935 if (var->vmode & FB_VMODE_YWRAP)
936 info->var.vmode |= FB_VMODE_YWRAP;
937 else
938 info->var.vmode &= ~FB_VMODE_YWRAP;
939
940 return 0;
941}
942
943/*
944 * Blank the screen if blank_mode != 0, else unblank. Return 0 if blanking
945 * succeeded, != 0 if un-/blanking failed.
946 * blank_mode == 2: suspend vsync
947 * blank_mode == 3: suspend hsync
948 * blank_mode == 4: powerdown
949 */
950static int fsl_diu_blank(int blank_mode, struct fb_info *info)
951{
952 struct mfb_info *mfbi = info->par;
953
954 mfbi->blank = blank_mode;
955
956 switch (blank_mode) {
957 case FB_BLANK_VSYNC_SUSPEND:
958 case FB_BLANK_HSYNC_SUSPEND:
959 /* FIXME: fixes to enable_panel and enable lcdc needed */
960 case FB_BLANK_NORMAL:
961 /* fsl_diu_disable_panel(info);*/
962 break;
963 case FB_BLANK_POWERDOWN:
964 /* disable_lcdc(info); */
965 break;
966 case FB_BLANK_UNBLANK:
967 /* fsl_diu_enable_panel(info);*/
968 break;
969 }
970
971 return 0;
972}
973
974static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
975 unsigned long arg)
976{
977 struct mfb_info *mfbi = info->par;
978 struct diu_ad *ad = mfbi->ad;
979 struct mfb_chroma_key ck;
980 unsigned char global_alpha;
981 struct aoi_display_offset aoi_d;
982 __u32 pix_fmt;
983 void __user *buf = (void __user *)arg;
984
985 if (!arg)
986 return -EINVAL;
987 switch (cmd) {
988 case MFB_SET_PIXFMT:
989 if (copy_from_user(&pix_fmt, buf, sizeof(pix_fmt)))
990 return -EFAULT;
991 ad->pix_fmt = pix_fmt;
992 pr_debug("Set pixel format to 0x%08x\n", ad->pix_fmt);
993 break;
994 case MFB_GET_PIXFMT:
995 pix_fmt = ad->pix_fmt;
996 if (copy_to_user(buf, &pix_fmt, sizeof(pix_fmt)))
997 return -EFAULT;
998 pr_debug("get pixel format 0x%08x\n", ad->pix_fmt);
999 break;
1000 case MFB_SET_AOID:
1001 if (copy_from_user(&aoi_d, buf, sizeof(aoi_d)))
1002 return -EFAULT;
1003 mfbi->x_aoi_d = aoi_d.x_aoi_d;
1004 mfbi->y_aoi_d = aoi_d.y_aoi_d;
1005 pr_debug("set AOI display offset of index %d to (%d,%d)\n",
1006 mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
1007 fsl_diu_check_var(&info->var, info);
1008 fsl_diu_set_par(info);
1009 break;
1010 case MFB_GET_AOID:
1011 aoi_d.x_aoi_d = mfbi->x_aoi_d;
1012 aoi_d.y_aoi_d = mfbi->y_aoi_d;
1013 if (copy_to_user(buf, &aoi_d, sizeof(aoi_d)))
1014 return -EFAULT;
1015 pr_debug("get AOI display offset of index %d (%d,%d)\n",
1016 mfbi->index, aoi_d.x_aoi_d, aoi_d.y_aoi_d);
1017 break;
1018 case MFB_GET_ALPHA:
1019 global_alpha = mfbi->g_alpha;
1020 if (copy_to_user(buf, &global_alpha, sizeof(global_alpha)))
1021 return -EFAULT;
1022 pr_debug("get global alpha of index %d\n", mfbi->index);
1023 break;
1024 case MFB_SET_ALPHA:
1025 /* set panel information */
1026 if (copy_from_user(&global_alpha, buf, sizeof(global_alpha)))
1027 return -EFAULT;
1028 ad->src_size_g_alpha = (ad->src_size_g_alpha & (~0xff)) |
1029 (global_alpha & 0xff);
1030 mfbi->g_alpha = global_alpha;
1031 pr_debug("set global alpha for index %d\n", mfbi->index);
1032 break;
1033 case MFB_SET_CHROMA_KEY:
1034 /* set panel winformation */
1035 if (copy_from_user(&ck, buf, sizeof(ck)))
1036 return -EFAULT;
1037
1038 if (ck.enable &&
1039 (ck.red_max < ck.red_min ||
1040 ck.green_max < ck.green_min ||
1041 ck.blue_max < ck.blue_min))
1042 return -EINVAL;
1043
1044 if (!ck.enable) {
1045 ad->ckmax_r = 0;
1046 ad->ckmax_g = 0;
1047 ad->ckmax_b = 0;
1048 ad->ckmin_r = 255;
1049 ad->ckmin_g = 255;
1050 ad->ckmin_b = 255;
1051 } else {
1052 ad->ckmax_r = ck.red_max;
1053 ad->ckmax_g = ck.green_max;
1054 ad->ckmax_b = ck.blue_max;
1055 ad->ckmin_r = ck.red_min;
1056 ad->ckmin_g = ck.green_min;
1057 ad->ckmin_b = ck.blue_min;
1058 }
1059 pr_debug("set chroma key\n");
1060 break;
1061 case FBIOGET_GWINFO:
1062 if (mfbi->type == MFB_TYPE_OFF)
1063 return -ENODEV;
1064 /* get graphic window information */
1065 if (copy_to_user(buf, ad, sizeof(*ad)))
1066 return -EFAULT;
1067 break;
1068 case FBIOGET_HWCINFO:
1069 pr_debug("FBIOGET_HWCINFO:0x%08x\n", FBIOGET_HWCINFO);
1070 break;
1071 case FBIOPUT_MODEINFO:
1072 pr_debug("FBIOPUT_MODEINFO:0x%08x\n", FBIOPUT_MODEINFO);
1073 break;
1074 case FBIOGET_DISPINFO:
1075 pr_debug("FBIOGET_DISPINFO:0x%08x\n", FBIOGET_DISPINFO);
1076 break;
1077
1078 default:
1079 printk(KERN_ERR "Unknown ioctl command (0x%08X)\n", cmd);
1080 return -ENOIOCTLCMD;
1081 }
1082
1083 return 0;
1084}
1085
1086/* turn on fb if count == 1
1087 */
1088static int fsl_diu_open(struct fb_info *info, int user)
1089{
1090 struct mfb_info *mfbi = info->par;
1091 int res = 0;
1092
1093 spin_lock(&diu_lock);
1094 mfbi->count++;
1095 if (mfbi->count == 1) {
1096 pr_debug("open plane index %d\n", mfbi->index);
1097 fsl_diu_check_var(&info->var, info);
1098 res = fsl_diu_set_par(info);
1099 if (res < 0)
1100 mfbi->count--;
1101 else {
1102 res = fsl_diu_enable_panel(info);
1103 if (res < 0)
1104 mfbi->count--;
1105 }
1106 }
1107
1108 spin_unlock(&diu_lock);
1109 return res;
1110}
1111
1112/* turn off fb if count == 0
1113 */
1114static int fsl_diu_release(struct fb_info *info, int user)
1115{
1116 struct mfb_info *mfbi = info->par;
1117 int res = 0;
1118
1119 spin_lock(&diu_lock);
1120 mfbi->count--;
1121 if (mfbi->count == 0) {
1122 pr_debug("release plane index %d\n", mfbi->index);
1123 res = fsl_diu_disable_panel(info);
1124 if (res < 0)
1125 mfbi->count++;
1126 }
1127 spin_unlock(&diu_lock);
1128 return res;
1129}
1130
1131static struct fb_ops fsl_diu_ops = {
1132 .owner = THIS_MODULE,
1133 .fb_check_var = fsl_diu_check_var,
1134 .fb_set_par = fsl_diu_set_par,
1135 .fb_setcolreg = fsl_diu_setcolreg,
1136 .fb_blank = fsl_diu_blank,
1137 .fb_pan_display = fsl_diu_pan_display,
1138 .fb_fillrect = cfb_fillrect,
1139 .fb_copyarea = cfb_copyarea,
1140 .fb_imageblit = cfb_imageblit,
1141 .fb_ioctl = fsl_diu_ioctl,
1142 .fb_open = fsl_diu_open,
1143 .fb_release = fsl_diu_release,
1144};
1145
1146static int init_fbinfo(struct fb_info *info)
1147{
1148 struct mfb_info *mfbi = info->par;
1149
1150 info->device = NULL;
1151 info->var.activate = FB_ACTIVATE_NOW;
1152 info->fbops = &fsl_diu_ops;
1153 info->flags = FBINFO_FLAG_DEFAULT;
1154 info->pseudo_palette = &mfbi->pseudo_palette;
1155
1156 /* Allocate colormap */
1157 fb_alloc_cmap(&info->cmap, 16, 0);
1158 return 0;
1159}
1160
1161static int install_fb(struct fb_info *info)
1162{
1163 int rc;
1164 struct mfb_info *mfbi = info->par;
1165 const char *aoi_mode, *init_aoi_mode = "320x240";
1166
1167 if (init_fbinfo(info))
1168 return -EINVAL;
1169
1170 if (mfbi->index == 0) /* plane 0 */
1171 aoi_mode = fb_mode;
1172 else
1173 aoi_mode = init_aoi_mode;
1174 pr_debug("mode used = %s\n", aoi_mode);
1175 rc = fb_find_mode(&info->var, info, aoi_mode, fsl_diu_mode_db,
1176 ARRAY_SIZE(fsl_diu_mode_db), &fsl_diu_default_mode, default_bpp);
1177
1178 switch (rc) {
1179 case 1:
1180 pr_debug("using mode specified in @mode\n");
1181 break;
1182 case 2:
1183 pr_debug("using mode specified in @mode "
1184 "with ignored refresh rate\n");
1185 break;
1186 case 3:
1187 pr_debug("using mode default mode\n");
1188 break;
1189 case 4:
1190 pr_debug("using mode from list\n");
1191 break;
1192 default:
1193 pr_debug("rc = %d\n", rc);
1194 pr_debug("failed to find mode\n");
1195 return -EINVAL;
1196 break;
1197 }
1198
1199 pr_debug("xres_virtual %d\n", info->var.xres_virtual);
1200 pr_debug("bits_per_pixel %d\n", info->var.bits_per_pixel);
1201
1202 pr_debug("info->var.yres_virtual = %d\n", info->var.yres_virtual);
1203 pr_debug("info->fix.line_length = %d\n", info->fix.line_length);
1204
1205 if (mfbi->type == MFB_TYPE_OFF)
1206 mfbi->blank = FB_BLANK_NORMAL;
1207 else
1208 mfbi->blank = FB_BLANK_UNBLANK;
1209
1210 if (fsl_diu_check_var(&info->var, info)) {
1211 printk(KERN_ERR "fb_check_var failed");
1212 fb_dealloc_cmap(&info->cmap);
1213 return -EINVAL;
1214 }
1215
1216 if (fsl_diu_set_par(info)) {
1217 printk(KERN_ERR "fb_set_par failed");
1218 fb_dealloc_cmap(&info->cmap);
1219 return -EINVAL;
1220 }
1221
1222 if (register_framebuffer(info) < 0) {
1223 printk(KERN_ERR "register_framebuffer failed");
1224 unmap_video_memory(info);
1225 fb_dealloc_cmap(&info->cmap);
1226 return -EINVAL;
1227 }
1228
1229 mfbi->registered = 1;
1230 printk(KERN_INFO "fb%d: %s fb device registered successfully.\n",
1231 info->node, info->fix.id);
1232
1233 return 0;
1234}
1235
1236static void __exit uninstall_fb(struct fb_info *info)
1237{
1238 struct mfb_info *mfbi = info->par;
1239
1240 if (!mfbi->registered)
1241 return;
1242
1243 unregister_framebuffer(info);
1244 unmap_video_memory(info);
1245 if (&info->cmap)
1246 fb_dealloc_cmap(&info->cmap);
1247
1248 mfbi->registered = 0;
1249}
1250
1251static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
1252{
1253 struct diu *hw = dr.diu_reg;
1254 unsigned int status = in_be32(&hw->int_status);
1255
1256 if (status) {
1257 /* This is the workaround for underrun */
1258 if (status & INT_UNDRUN) {
1259 out_be32(&hw->diu_mode, 0);
1260 pr_debug("Err: DIU occurs underrun!\n");
1261 udelay(1);
1262 out_be32(&hw->diu_mode, 1);
1263 }
1264#if defined(CONFIG_NOT_COHERENT_CACHE)
1265 else if (status & INT_VSYNC) {
1266 unsigned int i;
1267 for (i = 0; i < coherence_data_size;
1268 i += d_cache_line_size)
1269 __asm__ __volatile__ (
1270 "dcbz 0, %[input]"
1271 ::[input]"r"(&coherence_data[i]));
1272 }
1273#endif
1274 return IRQ_HANDLED;
1275 }
1276 return IRQ_NONE;
1277}
1278
1279static int request_irq_local(int irq)
1280{
1281 unsigned long status, ints;
1282 struct diu *hw;
1283 int ret;
1284
1285 hw = dr.diu_reg;
1286
1287 /* Read to clear the status */
1288 status = in_be32(&hw->int_status);
1289
1290 ret = request_irq(irq, fsl_diu_isr, 0, "diu", 0);
1291 if (ret)
1292 pr_info("Request diu IRQ failed.\n");
1293 else {
1294 ints = INT_PARERR | INT_LS_BF_VS;
1295#if !defined(CONFIG_NOT_COHERENT_CACHE)
1296 ints |= INT_VSYNC;
1297#endif
1298 if (dr.mode == MFB_MODE2 || dr.mode == MFB_MODE3)
1299 ints |= INT_VSYNC_WB;
1300
1301 /* Read to clear the status */
1302 status = in_be32(&hw->int_status);
1303 out_be32(&hw->int_mask, ints);
1304 }
1305 return ret;
1306}
1307
1308static void free_irq_local(int irq)
1309{
1310 struct diu *hw = dr.diu_reg;
1311
1312 /* Disable all LCDC interrupt */
1313 out_be32(&hw->int_mask, 0x1f);
1314
1315 free_irq(irq, 0);
1316}
1317
1318#ifdef CONFIG_PM
1319/*
1320 * Power management hooks. Note that we won't be called from IRQ context,
1321 * unlike the blank functions above, so we may sleep.
1322 */
1323static int fsl_diu_suspend(struct of_device *dev, pm_message_t state)
1324{
1325 struct fsl_diu_data *machine_data;
1326
1327 machine_data = dev_get_drvdata(&ofdev->dev);
1328 disable_lcdc(machine_data->fsl_diu_info[0]);
1329
1330 return 0;
1331}
1332
1333static int fsl_diu_resume(struct of_device *dev)
1334{
1335 struct fsl_diu_data *machine_data;
1336
1337 machine_data = dev_get_drvdata(&ofdev->dev);
1338 enable_lcdc(machine_data->fsl_diu_info[0]);
1339
1340 return 0;
1341}
1342
1343#else
1344#define fsl_diu_suspend NULL
1345#define fsl_diu_resume NULL
1346#endif /* CONFIG_PM */
1347
1348/* Align to 64-bit(8-byte), 32-byte, etc. */
1349static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
1350{
1351 u32 offset, ssize;
1352 u32 mask;
1353 dma_addr_t paddr = 0;
1354
1355 ssize = size + bytes_align;
1356 buf->vaddr = dma_alloc_coherent(0, ssize, &paddr, GFP_DMA | __GFP_ZERO);
1357 if (!buf->vaddr)
1358 return -ENOMEM;
1359
1360 buf->paddr = (__u32) paddr;
1361
1362 mask = bytes_align - 1;
1363 offset = (u32)buf->paddr & mask;
1364 if (offset) {
1365 buf->offset = bytes_align - offset;
1366 buf->paddr = (u32)buf->paddr + offset;
1367 } else
1368 buf->offset = 0;
1369 return 0;
1370}
1371
1372static void free_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
1373{
1374 dma_free_coherent(0, size + bytes_align,
1375 buf->vaddr, (buf->paddr - buf->offset));
1376 return;
1377}
1378
1379static ssize_t store_monitor(struct device *device,
1380 struct device_attribute *attr, const char *buf, size_t count)
1381{
1382 int old_monitor_port;
1383 unsigned long val;
1384 struct fsl_diu_data *machine_data =
1385 container_of(attr, struct fsl_diu_data, dev_attr);
1386
1387 if (strict_strtoul(buf, 10, &val))
1388 return 0;
1389
1390 old_monitor_port = machine_data->monitor_port;
1391 machine_data->monitor_port = diu_ops.set_sysfs_monitor_port(val);
1392
1393 if (old_monitor_port != machine_data->monitor_port) {
1394 /* All AOIs need adjust pixel format
1395 * fsl_diu_set_par only change the pixsel format here
1396 * unlikely to fail. */
1397 fsl_diu_set_par(machine_data->fsl_diu_info[0]);
1398 fsl_diu_set_par(machine_data->fsl_diu_info[1]);
1399 fsl_diu_set_par(machine_data->fsl_diu_info[2]);
1400 fsl_diu_set_par(machine_data->fsl_diu_info[3]);
1401 fsl_diu_set_par(machine_data->fsl_diu_info[4]);
1402 }
1403 return count;
1404}
1405
1406static ssize_t show_monitor(struct device *device,
1407 struct device_attribute *attr, char *buf)
1408{
1409 struct fsl_diu_data *machine_data =
1410 container_of(attr, struct fsl_diu_data, dev_attr);
1411 return diu_ops.show_monitor_port(machine_data->monitor_port, buf);
1412}
1413
1414static int fsl_diu_probe(struct of_device *ofdev,
1415 const struct of_device_id *match)
1416{
1417 struct device_node *np = ofdev->node;
1418 struct mfb_info *mfbi;
1419 phys_addr_t dummy_ad_addr;
1420 int ret, i, error = 0;
1421 struct resource res;
1422 struct fsl_diu_data *machine_data;
1423
1424 machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
1425 if (!machine_data)
1426 return -ENOMEM;
1427
1428 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1429 machine_data->fsl_diu_info[i] =
1430 framebuffer_alloc(sizeof(struct mfb_info), &ofdev->dev);
1431 if (!machine_data->fsl_diu_info[i]) {
1432 dev_err(&ofdev->dev, "cannot allocate memory\n");
1433 ret = -ENOMEM;
1434 goto error2;
1435 }
1436 mfbi = machine_data->fsl_diu_info[i]->par;
1437 memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
1438 mfbi->parent = machine_data;
1439 }
1440
1441 ret = of_address_to_resource(np, 0, &res);
1442 if (ret) {
1443 dev_err(&ofdev->dev, "could not obtain DIU address\n");
1444 goto error;
1445 }
1446 if (!res.start) {
1447 dev_err(&ofdev->dev, "invalid DIU address\n");
1448 goto error;
1449 }
1450 dev_dbg(&ofdev->dev, "%s, res.start: 0x%08x\n", __func__, res.start);
1451
1452 dr.diu_reg = ioremap(res.start, sizeof(struct diu));
1453 if (!dr.diu_reg) {
1454 dev_err(&ofdev->dev, "Err: can't map DIU registers!\n");
1455 ret = -EFAULT;
1456 goto error2;
1457 }
1458
1459 out_be32(&dr.diu_reg->diu_mode, 0); /* disable DIU anyway*/
1460
1461 /* Get the IRQ of the DIU */
1462 machine_data->irq = irq_of_parse_and_map(np, 0);
1463
1464 if (!machine_data->irq) {
1465 dev_err(&ofdev->dev, "could not get DIU IRQ\n");
1466 ret = -EINVAL;
1467 goto error;
1468 }
1469 machine_data->monitor_port = monitor_port;
1470
1471 /* Area descriptor memory pool aligns to 64-bit boundary */
1472 if (allocate_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
1473 return -ENOMEM;
1474
1475 /* Get memory for Gamma Table - 32-byte aligned memory */
1476 if (allocate_buf(&pool.gamma, 768, 32)) {
1477 ret = -ENOMEM;
1478 goto error;
1479 }
1480
1481 /* For performance, cursor bitmap buffer aligns to 32-byte boundary */
1482 if (allocate_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32)) {
1483 ret = -ENOMEM;
1484 goto error;
1485 }
1486
1487 i = ARRAY_SIZE(machine_data->fsl_diu_info);
1488 machine_data->dummy_ad = (struct diu_ad *)
1489 ((u32)pool.ad.vaddr + pool.ad.offset) + i;
1490 machine_data->dummy_ad->paddr = pool.ad.paddr +
1491 i * sizeof(struct diu_ad);
1492 machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
1493 if (!machine_data->dummy_aoi_virt) {
1494 ret = -ENOMEM;
1495 goto error;
1496 }
1497 machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
1498 machine_data->dummy_ad->pix_fmt = 0x88882317;
1499 machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
1500 machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) | 2);
1501 machine_data->dummy_ad->offset_xyi = 0;
1502 machine_data->dummy_ad->offset_xyd = 0;
1503 machine_data->dummy_ad->next_ad = 0;
1504
1505 out_be32(&dr.diu_reg->desc[0], machine_data->dummy_ad->paddr);
1506 out_be32(&dr.diu_reg->desc[1], machine_data->dummy_ad->paddr);
1507 out_be32(&dr.diu_reg->desc[2], machine_data->dummy_ad->paddr);
1508
1509 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
1510 machine_data->fsl_diu_info[i]->fix.smem_start = 0;
1511 mfbi = machine_data->fsl_diu_info[i]->par;
1512 mfbi->ad = (struct diu_ad *)((u32)pool.ad.vaddr
1513 + pool.ad.offset) + i;
1514 mfbi->ad->paddr = pool.ad.paddr + i * sizeof(struct diu_ad);
1515 ret = install_fb(machine_data->fsl_diu_info[i]);
1516 if (ret) {
1517 dev_err(&ofdev->dev,
1518 "Failed to register framebuffer %d\n",
1519 i);
1520 goto error;
1521 }
1522 }
1523
1524 if (request_irq_local(machine_data->irq)) {
1525 dev_err(machine_data->fsl_diu_info[0]->dev,
1526 "could not request irq for diu.");
1527 goto error;
1528 }
1529
1530 machine_data->dev_attr.attr.name = "monitor";
1531 machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
1532 machine_data->dev_attr.show = show_monitor;
1533 machine_data->dev_attr.store = store_monitor;
1534 error = device_create_file(machine_data->fsl_diu_info[0]->dev,
1535 &machine_data->dev_attr);
1536 if (error) {
1537 dev_err(machine_data->fsl_diu_info[0]->dev,
1538 "could not create sysfs %s file\n",
1539 machine_data->dev_attr.attr.name);
1540 }
1541
1542 dev_set_drvdata(&ofdev->dev, machine_data);
1543 return 0;
1544
1545error:
1546 for (i = ARRAY_SIZE(machine_data->fsl_diu_info);
1547 i > 0; i--)
1548 uninstall_fb(machine_data->fsl_diu_info[i - 1]);
1549 if (pool.ad.vaddr)
1550 free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
1551 if (pool.gamma.vaddr)
1552 free_buf(&pool.gamma, 768, 32);
1553 if (pool.cursor.vaddr)
1554 free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32);
1555 if (machine_data->dummy_aoi_virt)
1556 fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1557 iounmap(dr.diu_reg);
1558
1559error2:
1560 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1561 if (machine_data->fsl_diu_info[i])
1562 framebuffer_release(machine_data->fsl_diu_info[i]);
1563 kfree(machine_data);
1564
1565 return ret;
1566}
1567
1568
1569static int fsl_diu_remove(struct of_device *ofdev)
1570{
1571 struct fsl_diu_data *machine_data;
1572 int i;
1573
1574 machine_data = dev_get_drvdata(&ofdev->dev);
1575 disable_lcdc(machine_data->fsl_diu_info[0]);
1576 free_irq_local(machine_data->irq);
1577 for (i = ARRAY_SIZE(machine_data->fsl_diu_info); i > 0; i--)
1578 uninstall_fb(machine_data->fsl_diu_info[i - 1]);
1579 if (pool.ad.vaddr)
1580 free_buf(&pool.ad, sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
1581 if (pool.gamma.vaddr)
1582 free_buf(&pool.gamma, 768, 32);
1583 if (pool.cursor.vaddr)
1584 free_buf(&pool.cursor, MAX_CURS * MAX_CURS * 2, 32);
1585 if (machine_data->dummy_aoi_virt)
1586 fsl_diu_free(machine_data->dummy_aoi_virt, 64);
1587 iounmap(dr.diu_reg);
1588 for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
1589 if (machine_data->fsl_diu_info[i])
1590 framebuffer_release(machine_data->fsl_diu_info[i]);
1591 kfree(machine_data);
1592
1593 return 0;
1594}
1595
1596#ifndef MODULE
1597static int __init fsl_diu_setup(char *options)
1598{
1599 char *opt;
1600 unsigned long val;
1601
1602 if (!options || !*options)
1603 return 0;
1604
1605 while ((opt = strsep(&options, ",")) != NULL) {
1606 if (!*opt)
1607 continue;
1608 if (!strncmp(opt, "monitor=", 8)) {
1609 if (!strict_strtoul(opt + 8, 10, &val) && (val <= 2))
1610 monitor_port = val;
1611 } else if (!strncmp(opt, "bpp=", 4)) {
1612 if (!strict_strtoul(opt + 4, 10, &val))
1613 default_bpp = val;
1614 } else
1615 fb_mode = opt;
1616 }
1617
1618 return 0;
1619}
1620#endif
1621
1622static struct of_device_id fsl_diu_match[] = {
1623 {
1624 .compatible = "fsl,diu",
1625 },
1626 {}
1627};
1628MODULE_DEVICE_TABLE(of, fsl_diu_match);
1629
1630static struct of_platform_driver fsl_diu_driver = {
1631 .owner = THIS_MODULE,
1632 .name = "fsl_diu",
1633 .match_table = fsl_diu_match,
1634 .probe = fsl_diu_probe,
1635 .remove = fsl_diu_remove,
1636 .suspend = fsl_diu_suspend,
1637 .resume = fsl_diu_resume,
1638};
1639
1640static int __init fsl_diu_init(void)
1641{
1642#ifdef CONFIG_NOT_COHERENT_CACHE
1643 struct device_node *np;
1644 const u32 *prop;
1645#endif
1646 int ret;
1647#ifndef MODULE
1648 char *option;
1649
1650 /*
1651 * For kernel boot options (in 'video=xxxfb:<options>' format)
1652 */
1653 if (fb_get_options("fslfb", &option))
1654 return -ENODEV;
1655 fsl_diu_setup(option);
1656#endif
1657 printk(KERN_INFO "Freescale DIU driver\n");
1658
1659#ifdef CONFIG_NOT_COHERENT_CACHE
1660 np = of_find_node_by_type(NULL, "cpu");
1661 if (!np) {
1662 printk(KERN_ERR "Err: can't find device node 'cpu'\n");
1663 return -ENODEV;
1664 }
1665
1666 prop = of_get_property(np, "d-cache-size", NULL);
1667 if (prop == NULL)
1668 return -ENODEV;
1669
1670 /* Freescale PLRU requires 13/8 times the cache size to do a proper
1671 displacement flush
1672 */
1673 coherence_data_size = *prop * 13;
1674 coherence_data_size /= 8;
1675
1676 prop = of_get_property(np, "d-cache-line-size", NULL);
1677 if (prop == NULL)
1678 return -ENODEV;
1679 d_cache_line_size = *prop;
1680
1681 of_node_put(np);
1682 coherence_data = vmalloc(coherence_data_size);
1683 if (!coherence_data)
1684 return -ENOMEM;
1685#endif
1686 ret = of_register_platform_driver(&fsl_diu_driver);
1687 if (ret) {
1688 printk(KERN_ERR
1689 "fsl-diu: failed to register platform driver\n");
1690#if defined(CONFIG_NOT_COHERENT_CACHE)
1691 vfree(coherence_data);
1692#endif
1693 iounmap(dr.diu_reg);
1694 }
1695 return ret;
1696}
1697
1698static void __exit fsl_diu_exit(void)
1699{
1700 of_unregister_platform_driver(&fsl_diu_driver);
1701#if defined(CONFIG_NOT_COHERENT_CACHE)
1702 vfree(coherence_data);
1703#endif
1704}
1705
1706module_init(fsl_diu_init);
1707module_exit(fsl_diu_exit);
1708
1709MODULE_AUTHOR("York Sun <yorksun@freescale.com>");
1710MODULE_DESCRIPTION("Freescale DIU framebuffer driver");
1711MODULE_LICENSE("GPL");
1712
1713module_param_named(mode, fb_mode, charp, 0);
1714MODULE_PARM_DESC(mode,
1715 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
1716module_param_named(bpp, default_bpp, ulong, 0);
1717MODULE_PARM_DESC(bpp, "Specify bit-per-pixel if not specified mode");
1718module_param_named(monitor, monitor_port, int, 0);
1719MODULE_PARM_DESC(monitor,
1720 "Specify the monitor port (0, 1 or 2) if supported by the platform");
1721