2 * Frame Buffer Driver for PKUnity-v3 Unigfx
3 * Code specific to PKUnity SoC and UniCore ISA
5 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
6 * Copyright (C) 2001-2010 Guan Xuetao
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/module.h>
14 #include <linux/kernel.h>
15 #include <linux/errno.h>
16 #include <linux/platform_device.h>
17 #include <linux/clk.h>
19 #include <linux/init.h>
20 #include <linux/console.h>
22 #include <asm/sizes.h>
23 #include <mach/hardware.h>
25 /* Platform_data reserved for unifb registers. */
26 #define UNIFB_REGS_NUM 10
27 /* RAM reserved for the frame buffer. */
28 #define UNIFB_MEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
31 * cause UNIGFX don not have EDID
32 * all the modes are organized as follow
34 static const struct fb_videomode unifb_modes
[] = {
35 /* 0 640x480-60 VESA */
36 { "640x480@60", 60, 640, 480, 25175000, 48, 16, 34, 10, 96, 1,
37 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
38 /* 1 640x480-75 VESA */
39 { "640x480@75", 75, 640, 480, 31500000, 120, 16, 18, 1, 64, 1,
40 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
41 /* 2 800x600-60 VESA */
42 { "800x600@60", 60, 800, 600, 40000000, 88, 40, 26, 1, 128, 1,
43 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
44 /* 3 800x600-75 VESA */
45 { "800x600@75", 75, 800, 600, 49500000, 160, 16, 23, 1, 80, 1,
46 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
47 /* 4 1024x768-60 VESA */
48 { "1024x768@60", 60, 1024, 768, 65000000, 160, 24, 34, 3, 136, 1,
49 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
50 /* 5 1024x768-75 VESA */
51 { "1024x768@75", 75, 1024, 768, 78750000, 176, 16, 30, 1, 96, 1,
52 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
53 /* 6 1280x960-60 VESA */
54 { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38, 1, 112, 1,
55 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
56 /* 7 1440x900-60 VESA */
57 { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30, 3, 152, 1,
58 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
59 /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
60 { "1024x600@60", 60, 1024, 600, 50650000, 160, 24, 26, 1, 136, 1,
61 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
62 /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
63 { "1024x600@75", 75, 1024, 600, 61500000, 176, 16, 23, 1, 96, 1,
64 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
65 /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
66 { "1366x768@60", 60, 1366, 768, 85500000, 256, 58, 18, 1, 112, 3,
67 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
70 static struct fb_var_screeninfo unifb_default
= {
79 .activate
= FB_ACTIVATE_NOW
,
89 .vmode
= FB_VMODE_NONINTERLACED
,
92 static struct fb_fix_screeninfo unifb_fix
= {
94 .type
= FB_TYPE_PACKED_PIXELS
,
95 .visual
= FB_VISUAL_TRUECOLOR
,
99 .accel
= FB_ACCEL_NONE
,
102 static void unifb_sync(struct fb_info
*info
)
104 /* TODO: may, this can be replaced by interrupt */
107 for (cnt
= 0; cnt
< 0x10000000; cnt
++) {
108 if (readl(UGE_COMMAND
) & 0x1000000)
113 dev_warn(info
->device
, "Warning: UniGFX GE time out ...\n");
116 static void unifb_prim_fillrect(struct fb_info
*info
,
117 const struct fb_fillrect
*region
)
119 int awidth
= region
->width
;
120 int aheight
= region
->height
;
121 int m_iBpp
= info
->var
.bits_per_pixel
;
122 int screen_width
= info
->var
.xres
;
123 int src_sel
= 1; /* from fg_color */
126 int dst_x0
= region
->dx
;
128 int dst_y0
= region
->dy
;
129 int rop_alpha_sel
= 0;
130 int rop_alpha_code
= 0xCC;
135 int dst_pitch
= screen_width
* (m_iBpp
/ 8);
136 int dst_offset
= dst_y0
* dst_pitch
+ dst_x0
* (m_iBpp
/ 8);
137 int src_pitch
= screen_width
* (m_iBpp
/ 8);
138 int src_offset
= src_y0
* src_pitch
+ src_x0
* (m_iBpp
/ 8);
139 unsigned int command
= 0;
144 int bottom
= info
->var
.yres
- 1;
145 int right
= info
->var
.xres
- 1;
148 bottom
= (bottom
<< 16) | right
;
149 command
= (rop_alpha_sel
<< 26) | (pat_sel
<< 18) | (src_sel
<< 16)
150 | (x_dir
<< 20) | (y_dir
<< 21) | (command
<< 24)
151 | (clip_region
<< 23) | (clip_en
<< 22) | (tp_en
<< 27);
152 src_pitch
= (dst_pitch
<< 16) | src_pitch
;
153 awidth
= awidth
| (aheight
<< 16);
154 alpha_r
= ((rop_alpha_code
& 0xff) << 8) | (alpha_r
& 0xff)
156 src_x0
= (src_x0
& 0x1fff) | ((src_y0
& 0x1fff) << 16);
157 dst_x0
= (dst_x0
& 0x1fff) | ((dst_y0
& 0x1fff) << 16);
158 fg_color
= region
->color
;
162 writel(((u32
*)(info
->pseudo_palette
))[fg_color
], UGE_FCOLOR
);
163 writel(0, UGE_BCOLOR
);
164 writel(src_pitch
, UGE_PITCH
);
165 writel(src_offset
, UGE_SRCSTART
);
166 writel(dst_offset
, UGE_DSTSTART
);
167 writel(awidth
, UGE_WIDHEIGHT
);
168 writel(top
, UGE_CLIP0
);
169 writel(bottom
, UGE_CLIP1
);
170 writel(alpha_r
, UGE_ROPALPHA
);
171 writel(src_x0
, UGE_SRCXY
);
172 writel(dst_x0
, UGE_DSTXY
);
173 writel(command
, UGE_COMMAND
);
176 static void unifb_fillrect(struct fb_info
*info
,
177 const struct fb_fillrect
*region
)
179 struct fb_fillrect modded
;
182 if (info
->flags
& FBINFO_HWACCEL_DISABLED
) {
183 sys_fillrect(info
, region
);
187 vxres
= info
->var
.xres_virtual
;
188 vyres
= info
->var
.yres_virtual
;
190 memcpy(&modded
, region
, sizeof(struct fb_fillrect
));
192 if (!modded
.width
|| !modded
.height
||
193 modded
.dx
>= vxres
|| modded
.dy
>= vyres
)
196 if (modded
.dx
+ modded
.width
> vxres
)
197 modded
.width
= vxres
- modded
.dx
;
198 if (modded
.dy
+ modded
.height
> vyres
)
199 modded
.height
= vyres
- modded
.dy
;
201 unifb_prim_fillrect(info
, &modded
);
204 static void unifb_prim_copyarea(struct fb_info
*info
,
205 const struct fb_copyarea
*area
)
207 int awidth
= area
->width
;
208 int aheight
= area
->height
;
209 int m_iBpp
= info
->var
.bits_per_pixel
;
210 int screen_width
= info
->var
.xres
;
211 int src_sel
= 2; /* from mem */
213 int src_x0
= area
->sx
;
214 int dst_x0
= area
->dx
;
215 int src_y0
= area
->sy
;
216 int dst_y0
= area
->dy
;
218 int rop_alpha_sel
= 0;
219 int rop_alpha_code
= 0xCC;
225 int dst_pitch
= screen_width
* (m_iBpp
/ 8);
226 int dst_offset
= dst_y0
* dst_pitch
+ dst_x0
* (m_iBpp
/ 8);
227 int src_pitch
= screen_width
* (m_iBpp
/ 8);
228 int src_offset
= src_y0
* src_pitch
+ src_x0
* (m_iBpp
/ 8);
229 unsigned int command
= 0;
234 int bottom
= info
->var
.yres
;
235 int right
= info
->var
.xres
;
244 if (src_y0
- dst_y0
> 0) {
248 src_offset
= (src_y0
+ aheight
) * src_pitch
+
249 src_x0
* (m_iBpp
/ 8);
250 dst_offset
= (dst_y0
+ aheight
) * dst_pitch
+
251 dst_x0
* (m_iBpp
/ 8);
256 command
= (rop_alpha_sel
<< 26) | (pat_sel
<< 18) | (src_sel
<< 16) |
257 (x_dir
<< 20) | (y_dir
<< 21) | (command
<< 24) |
258 (clip_region
<< 23) | (clip_en
<< 22) | (tp_en
<< 27);
259 src_pitch
= (dst_pitch
<< 16) | src_pitch
;
260 awidth
= awidth
| (aheight
<< 16);
261 alpha_r
= ((rop_alpha_code
& 0xff) << 8) | (alpha_r
& 0xff) |
263 src_x0
= (src_x0
& 0x1fff) | ((src_y0
& 0x1fff) << 16);
264 dst_x0
= (dst_x0
& 0x1fff) | ((dst_y0
& 0x1fff) << 16);
265 bottom
= (bottom
<< 16) | right
;
269 writel(src_pitch
, UGE_PITCH
);
270 writel(src_offset
, UGE_SRCSTART
);
271 writel(dst_offset
, UGE_DSTSTART
);
272 writel(awidth
, UGE_WIDHEIGHT
);
273 writel(top
, UGE_CLIP0
);
274 writel(bottom
, UGE_CLIP1
);
275 writel(bg_color
, UGE_BCOLOR
);
276 writel(fg_color
, UGE_FCOLOR
);
277 writel(alpha_r
, UGE_ROPALPHA
);
278 writel(src_x0
, UGE_SRCXY
);
279 writel(dst_x0
, UGE_DSTXY
);
280 writel(command
, UGE_COMMAND
);
283 static void unifb_copyarea(struct fb_info
*info
, const struct fb_copyarea
*area
)
285 struct fb_copyarea modded
;
287 modded
.sx
= area
->sx
;
288 modded
.sy
= area
->sy
;
289 modded
.dx
= area
->dx
;
290 modded
.dy
= area
->dy
;
291 modded
.width
= area
->width
;
292 modded
.height
= area
->height
;
294 if (info
->flags
& FBINFO_HWACCEL_DISABLED
) {
295 sys_copyarea(info
, area
);
299 vxres
= info
->var
.xres_virtual
;
300 vyres
= info
->var
.yres_virtual
;
302 if (!modded
.width
|| !modded
.height
||
303 modded
.sx
>= vxres
|| modded
.sy
>= vyres
||
304 modded
.dx
>= vxres
|| modded
.dy
>= vyres
)
307 if (modded
.sx
+ modded
.width
> vxres
)
308 modded
.width
= vxres
- modded
.sx
;
309 if (modded
.dx
+ modded
.width
> vxres
)
310 modded
.width
= vxres
- modded
.dx
;
311 if (modded
.sy
+ modded
.height
> vyres
)
312 modded
.height
= vyres
- modded
.sy
;
313 if (modded
.dy
+ modded
.height
> vyres
)
314 modded
.height
= vyres
- modded
.dy
;
316 unifb_prim_copyarea(info
, &modded
);
319 static void unifb_imageblit(struct fb_info
*info
, const struct fb_image
*image
)
321 sys_imageblit(info
, image
);
324 static u_long
get_line_length(int xres_virtual
, int bpp
)
328 length
= xres_virtual
* bpp
;
329 length
= (length
+ 31) & ~31;
335 * Setting the video mode has been split into two parts.
336 * First part, xxxfb_check_var, must not write anything
337 * to hardware, it should only verify and adjust var.
338 * This means it doesn't alter par but it does use hardware
339 * data from it to check this var.
341 static int unifb_check_var(struct fb_var_screeninfo
*var
,
342 struct fb_info
*info
)
347 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
348 * as FB_VMODE_SMOOTH_XPAN is only used internally
351 if (var
->vmode
& FB_VMODE_CONUPDATE
) {
352 var
->vmode
|= FB_VMODE_YWRAP
;
353 var
->xoffset
= info
->var
.xoffset
;
354 var
->yoffset
= info
->var
.yoffset
;
358 * Some very basic checks
364 if (var
->xres
> var
->xres_virtual
)
365 var
->xres_virtual
= var
->xres
;
366 if (var
->yres
> var
->yres_virtual
)
367 var
->yres_virtual
= var
->yres
;
368 if (var
->bits_per_pixel
<= 1)
369 var
->bits_per_pixel
= 1;
370 else if (var
->bits_per_pixel
<= 8)
371 var
->bits_per_pixel
= 8;
372 else if (var
->bits_per_pixel
<= 16)
373 var
->bits_per_pixel
= 16;
374 else if (var
->bits_per_pixel
<= 24)
375 var
->bits_per_pixel
= 24;
376 else if (var
->bits_per_pixel
<= 32)
377 var
->bits_per_pixel
= 32;
381 if (var
->xres_virtual
< var
->xoffset
+ var
->xres
)
382 var
->xres_virtual
= var
->xoffset
+ var
->xres
;
383 if (var
->yres_virtual
< var
->yoffset
+ var
->yres
)
384 var
->yres_virtual
= var
->yoffset
+ var
->yres
;
390 get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
391 if (line_length
* var
->yres_virtual
> UNIFB_MEMSIZE
)
395 * Now that we checked it we alter var. The reason being is that the
396 * video mode passed in might not work but slight changes to it might
397 * make it work. This way we let the user know what is acceptable.
399 switch (var
->bits_per_pixel
) {
404 var
->green
.offset
= 0;
405 var
->green
.length
= 8;
406 var
->blue
.offset
= 0;
407 var
->blue
.length
= 8;
408 var
->transp
.offset
= 0;
409 var
->transp
.length
= 0;
411 case 16: /* RGBA 5551 */
412 if (var
->transp
.length
) {
415 var
->green
.offset
= 5;
416 var
->green
.length
= 5;
417 var
->blue
.offset
= 10;
418 var
->blue
.length
= 5;
419 var
->transp
.offset
= 15;
420 var
->transp
.length
= 1;
421 } else { /* RGB 565 */
422 var
->red
.offset
= 11;
424 var
->green
.offset
= 5;
425 var
->green
.length
= 6;
426 var
->blue
.offset
= 0;
427 var
->blue
.length
= 5;
428 var
->transp
.offset
= 0;
429 var
->transp
.length
= 0;
432 case 24: /* RGB 888 */
435 var
->green
.offset
= 8;
436 var
->green
.length
= 8;
437 var
->blue
.offset
= 16;
438 var
->blue
.length
= 8;
439 var
->transp
.offset
= 0;
440 var
->transp
.length
= 0;
442 case 32: /* RGBA 8888 */
443 var
->red
.offset
= 16;
445 var
->green
.offset
= 8;
446 var
->green
.length
= 8;
447 var
->blue
.offset
= 0;
448 var
->blue
.length
= 8;
449 var
->transp
.offset
= 24;
450 var
->transp
.length
= 8;
453 var
->red
.msb_right
= 0;
454 var
->green
.msb_right
= 0;
455 var
->blue
.msb_right
= 0;
456 var
->transp
.msb_right
= 0;
462 * This routine actually sets the video mode. It's in here where we
463 * the hardware state info->par and fix which can be affected by the
464 * change in par. For this driver it doesn't do much.
466 static int unifb_set_par(struct fb_info
*info
)
468 int hTotal
, vTotal
, hSyncStart
, hSyncEnd
, vSyncStart
, vSyncEnd
;
471 #ifdef CONFIG_PUV3_PM
476 for (i
= 0; i
<= 10; i
++) {
477 if (info
->var
.xres
== unifb_modes
[i
].xres
478 && info
->var
.yres
== unifb_modes
[i
].yres
479 && info
->var
.upper_margin
== unifb_modes
[i
].upper_margin
480 && info
->var
.lower_margin
== unifb_modes
[i
].lower_margin
481 && info
->var
.left_margin
== unifb_modes
[i
].left_margin
482 && info
->var
.right_margin
== unifb_modes
[i
].right_margin
483 && info
->var
.hsync_len
== unifb_modes
[i
].hsync_len
484 && info
->var
.vsync_len
== unifb_modes
[i
].vsync_len
) {
485 pixclk
= unifb_modes
[i
].pixclock
;
491 clk_vga
= clk_get(info
->device
, "VGA_CLK");
492 if (clk_vga
== ERR_PTR(-ENOENT
))
496 if (clk_set_rate(clk_vga
, pixclk
)) { /* set clock failed */
497 info
->fix
= unifb_fix
;
498 info
->var
= unifb_default
;
499 if (clk_set_rate(clk_vga
, unifb_default
.pixclock
))
505 info
->fix
.line_length
= get_line_length(info
->var
.xres_virtual
,
506 info
->var
.bits_per_pixel
);
508 hSyncStart
= info
->var
.xres
+ info
->var
.right_margin
;
509 hSyncEnd
= hSyncStart
+ info
->var
.hsync_len
;
510 hTotal
= hSyncEnd
+ info
->var
.left_margin
;
512 vSyncStart
= info
->var
.yres
+ info
->var
.lower_margin
;
513 vSyncEnd
= vSyncStart
+ info
->var
.vsync_len
;
514 vTotal
= vSyncEnd
+ info
->var
.upper_margin
;
516 switch (info
->var
.bits_per_pixel
) {
518 format
= UDE_CFG_DST8
;
521 format
= UDE_CFG_DST16
;
524 format
= UDE_CFG_DST24
;
527 format
= UDE_CFG_DST32
;
533 writel(info
->fix
.smem_start
, UDE_FSA
);
534 writel(info
->var
.yres
, UDE_LS
);
535 writel(get_line_length(info
->var
.xres
,
536 info
->var
.bits_per_pixel
) >> 3, UDE_PS
);
537 /* >> 3 for hardware required. */
538 writel((hTotal
<< 16) | (info
->var
.xres
), UDE_HAT
);
539 writel(((hTotal
- 1) << 16) | (info
->var
.xres
- 1), UDE_HBT
);
540 writel(((hSyncEnd
- 1) << 16) | (hSyncStart
- 1), UDE_HST
);
541 writel((vTotal
<< 16) | (info
->var
.yres
), UDE_VAT
);
542 writel(((vTotal
- 1) << 16) | (info
->var
.yres
- 1), UDE_VBT
);
543 writel(((vSyncEnd
- 1) << 16) | (vSyncStart
- 1), UDE_VST
);
544 writel(UDE_CFG_GDEN_ENABLE
| UDE_CFG_TIMEUP_ENABLE
545 | format
| 0xC0000001, UDE_CFG
);
551 * Set a single color register. The values supplied are already
552 * rounded down to the hardware's capabilities (according to the
553 * entries in the var structure). Return != 0 for invalid regno.
555 static int unifb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
556 u_int transp
, struct fb_info
*info
)
558 if (regno
>= 256) /* no. of hw registers */
561 /* grayscale works only partially under directcolor */
562 if (info
->var
.grayscale
) {
563 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
565 (red
* 77 + green
* 151 + blue
* 28) >> 8;
568 #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
569 switch (info
->fix
.visual
) {
570 case FB_VISUAL_TRUECOLOR
:
571 case FB_VISUAL_PSEUDOCOLOR
:
572 red
= CNVT_TOHW(red
, info
->var
.red
.length
);
573 green
= CNVT_TOHW(green
, info
->var
.green
.length
);
574 blue
= CNVT_TOHW(blue
, info
->var
.blue
.length
);
575 transp
= CNVT_TOHW(transp
, info
->var
.transp
.length
);
577 case FB_VISUAL_DIRECTCOLOR
:
578 red
= CNVT_TOHW(red
, 8); /* expect 8 bit DAC */
579 green
= CNVT_TOHW(green
, 8);
580 blue
= CNVT_TOHW(blue
, 8);
581 /* hey, there is bug in transp handling... */
582 transp
= CNVT_TOHW(transp
, 8);
586 /* Truecolor has hardware independent palette */
587 if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
593 v
= (red
<< info
->var
.red
.offset
) |
594 (green
<< info
->var
.green
.offset
) |
595 (blue
<< info
->var
.blue
.offset
) |
596 (transp
<< info
->var
.transp
.offset
);
597 switch (info
->var
.bits_per_pixel
) {
603 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
614 * Pan or Wrap the Display
616 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
618 static int unifb_pan_display(struct fb_var_screeninfo
*var
,
619 struct fb_info
*info
)
621 if (var
->vmode
& FB_VMODE_YWRAP
) {
623 || var
->yoffset
>= info
->var
.yres_virtual
627 if (var
->xoffset
+ info
->var
.xres
> info
->var
.xres_virtual
||
628 var
->yoffset
+ info
->var
.yres
> info
->var
.yres_virtual
)
631 info
->var
.xoffset
= var
->xoffset
;
632 info
->var
.yoffset
= var
->yoffset
;
633 if (var
->vmode
& FB_VMODE_YWRAP
)
634 info
->var
.vmode
|= FB_VMODE_YWRAP
;
636 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
640 int unifb_mmap(struct fb_info
*info
,
641 struct vm_area_struct
*vma
)
643 unsigned long size
= vma
->vm_end
- vma
->vm_start
;
644 unsigned long offset
= vma
->vm_pgoff
<< PAGE_SHIFT
;
645 unsigned long pos
= info
->fix
.smem_start
+ offset
;
647 if (offset
+ size
> info
->fix
.smem_len
)
650 vma
->vm_page_prot
= pgprot_noncached(vma
->vm_page_prot
);
652 if (io_remap_pfn_range(vma
, vma
->vm_start
, pos
>> PAGE_SHIFT
, size
,
656 vma
->vm_flags
|= VM_RESERVED
; /* avoid to swap out this VMA */
661 static struct fb_ops unifb_ops
= {
662 .fb_read
= fb_sys_read
,
663 .fb_write
= fb_sys_write
,
664 .fb_check_var
= unifb_check_var
,
665 .fb_set_par
= unifb_set_par
,
666 .fb_setcolreg
= unifb_setcolreg
,
667 .fb_pan_display
= unifb_pan_display
,
668 .fb_fillrect
= unifb_fillrect
,
669 .fb_copyarea
= unifb_copyarea
,
670 .fb_imageblit
= unifb_imageblit
,
671 .fb_mmap
= unifb_mmap
,
677 static int unifb_probe(struct platform_device
*dev
)
679 struct fb_info
*info
;
680 u32 unifb_regs
[UNIFB_REGS_NUM
];
681 int retval
= -ENOMEM
;
682 struct resource
*iomem
;
685 videomemory
= (void *)__get_free_pages(GFP_KERNEL
| __GFP_COMP
,
686 get_order(UNIFB_MEMSIZE
));
690 memset(videomemory
, 0, UNIFB_MEMSIZE
);
692 unifb_fix
.smem_start
= virt_to_phys(videomemory
);
693 unifb_fix
.smem_len
= UNIFB_MEMSIZE
;
695 iomem
= platform_get_resource(dev
, IORESOURCE_MEM
, 0);
696 unifb_fix
.mmio_start
= iomem
->start
;
698 info
= framebuffer_alloc(sizeof(u32
)*256, &dev
->dev
);
702 info
->screen_base
= (char __iomem
*)videomemory
;
703 info
->fbops
= &unifb_ops
;
705 retval
= fb_find_mode(&info
->var
, info
, NULL
,
706 unifb_modes
, 10, &unifb_modes
[0], 16);
708 if (!retval
|| (retval
== 4))
709 info
->var
= unifb_default
;
711 info
->fix
= unifb_fix
;
712 info
->pseudo_palette
= info
->par
;
714 info
->flags
= FBINFO_FLAG_DEFAULT
;
715 #ifdef FB_ACCEL_PUV3_UNIGFX
716 info
->fix
.accel
= FB_ACCEL_PUV3_UNIGFX
;
719 retval
= fb_alloc_cmap(&info
->cmap
, 256, 0);
723 retval
= register_framebuffer(info
);
726 platform_set_drvdata(dev
, info
);
727 platform_device_add_data(dev
, unifb_regs
, sizeof(u32
) * UNIFB_REGS_NUM
);
730 "fb%d: Virtual frame buffer device, using %dM of video memory\n",
731 info
->node
, UNIFB_MEMSIZE
>> 20);
734 fb_dealloc_cmap(&info
->cmap
);
736 framebuffer_release(info
);
741 static int unifb_remove(struct platform_device
*dev
)
743 struct fb_info
*info
= platform_get_drvdata(dev
);
746 unregister_framebuffer(info
);
747 fb_dealloc_cmap(&info
->cmap
);
748 framebuffer_release(info
);
754 static int unifb_resume(struct platform_device
*dev
)
757 u32
*unifb_regs
= dev
->dev
.platform_data
;
759 if (dev
->dev
.power
.power_state
.event
== PM_EVENT_ON
)
764 if (dev
->dev
.power
.power_state
.event
== PM_EVENT_SUSPEND
) {
765 writel(unifb_regs
[0], UDE_FSA
);
766 writel(unifb_regs
[1], UDE_LS
);
767 writel(unifb_regs
[2], UDE_PS
);
768 writel(unifb_regs
[3], UDE_HAT
);
769 writel(unifb_regs
[4], UDE_HBT
);
770 writel(unifb_regs
[5], UDE_HST
);
771 writel(unifb_regs
[6], UDE_VAT
);
772 writel(unifb_regs
[7], UDE_VBT
);
773 writel(unifb_regs
[8], UDE_VST
);
774 writel(unifb_regs
[9], UDE_CFG
);
776 dev
->dev
.power
.power_state
= PMSG_ON
;
783 static int unifb_suspend(struct platform_device
*dev
, pm_message_t mesg
)
785 u32
*unifb_regs
= dev
->dev
.platform_data
;
787 unifb_regs
[0] = readl(UDE_FSA
);
788 unifb_regs
[1] = readl(UDE_LS
);
789 unifb_regs
[2] = readl(UDE_PS
);
790 unifb_regs
[3] = readl(UDE_HAT
);
791 unifb_regs
[4] = readl(UDE_HBT
);
792 unifb_regs
[5] = readl(UDE_HST
);
793 unifb_regs
[6] = readl(UDE_VAT
);
794 unifb_regs
[7] = readl(UDE_VBT
);
795 unifb_regs
[8] = readl(UDE_VST
);
796 unifb_regs
[9] = readl(UDE_CFG
);
798 if (mesg
.event
== dev
->dev
.power
.power_state
.event
)
801 switch (mesg
.event
) {
802 case PM_EVENT_FREEZE
: /* about to take snapshot */
803 case PM_EVENT_PRETHAW
: /* before restoring snapshot */
814 dev
->dev
.power
.power_state
= mesg
;
819 #define unifb_resume NULL
820 #define unifb_suspend NULL
823 static struct platform_driver unifb_driver
= {
824 .probe
= unifb_probe
,
825 .remove
= unifb_remove
,
826 .resume
= unifb_resume
,
827 .suspend
= unifb_suspend
,
829 .name
= "PKUnity-v3-UNIGFX",
833 static int __init
unifb_init(void)
836 if (fb_get_options("unifb", NULL
))
840 return platform_driver_register(&unifb_driver
);
843 module_init(unifb_init
);
845 static void __exit
unifb_exit(void)
847 platform_driver_unregister(&unifb_driver
);
850 module_exit(unifb_exit
);
852 MODULE_LICENSE("GPL v2");