1 // SPDX-License-Identifier: GPL-2.0-only
3 * Frame Buffer Driver for PKUnity-v3 Unigfx
4 * Code specific to PKUnity SoC and UniCore ISA
6 * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
7 * Copyright (C) 2001-2010 Guan Xuetao
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/errno.h>
13 #include <linux/platform_device.h>
14 #include <linux/clk.h>
16 #include <linux/init.h>
17 #include <linux/console.h>
20 #include <linux/sizes.h>
21 #include <asm/pgtable.h>
22 #include <mach/hardware.h>
24 /* Platform_data reserved for unifb registers. */
25 #define UNIFB_REGS_NUM 10
26 /* RAM reserved for the frame buffer. */
27 #define UNIFB_MEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
30 * cause UNIGFX don not have EDID
31 * all the modes are organized as follow
33 static const struct fb_videomode unifb_modes
[] = {
34 /* 0 640x480-60 VESA */
35 { "640x480@60", 60, 640, 480, 25175000, 48, 16, 34, 10, 96, 1,
36 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
37 /* 1 640x480-75 VESA */
38 { "640x480@75", 75, 640, 480, 31500000, 120, 16, 18, 1, 64, 1,
39 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
40 /* 2 800x600-60 VESA */
41 { "800x600@60", 60, 800, 600, 40000000, 88, 40, 26, 1, 128, 1,
42 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
43 /* 3 800x600-75 VESA */
44 { "800x600@75", 75, 800, 600, 49500000, 160, 16, 23, 1, 80, 1,
45 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
46 /* 4 1024x768-60 VESA */
47 { "1024x768@60", 60, 1024, 768, 65000000, 160, 24, 34, 3, 136, 1,
48 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
49 /* 5 1024x768-75 VESA */
50 { "1024x768@75", 75, 1024, 768, 78750000, 176, 16, 30, 1, 96, 1,
51 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
52 /* 6 1280x960-60 VESA */
53 { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38, 1, 112, 1,
54 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
55 /* 7 1440x900-60 VESA */
56 { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30, 3, 152, 1,
57 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
58 /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
59 { "1024x600@60", 60, 1024, 600, 50650000, 160, 24, 26, 1, 136, 1,
60 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
61 /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
62 { "1024x600@75", 75, 1024, 600, 61500000, 176, 16, 23, 1, 96, 1,
63 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
64 /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
65 { "1366x768@60", 60, 1366, 768, 85500000, 256, 58, 18, 1, 112, 3,
66 0, FB_VMODE_NONINTERLACED
, FB_MODE_IS_VESA
},
69 static const struct fb_var_screeninfo unifb_default
= {
78 .activate
= FB_ACTIVATE_NOW
,
88 .vmode
= FB_VMODE_NONINTERLACED
,
91 static struct fb_fix_screeninfo unifb_fix
= {
93 .type
= FB_TYPE_PACKED_PIXELS
,
94 .visual
= FB_VISUAL_TRUECOLOR
,
98 .accel
= FB_ACCEL_NONE
,
101 static void unifb_sync(struct fb_info
*info
)
103 /* TODO: may, this can be replaced by interrupt */
106 for (cnt
= 0; cnt
< 0x10000000; cnt
++) {
107 if (readl(UGE_COMMAND
) & 0x1000000)
112 dev_warn(info
->device
, "Warning: UniGFX GE time out ...\n");
115 static void unifb_prim_fillrect(struct fb_info
*info
,
116 const struct fb_fillrect
*region
)
118 int awidth
= region
->width
;
119 int aheight
= region
->height
;
120 int m_iBpp
= info
->var
.bits_per_pixel
;
121 int screen_width
= info
->var
.xres
;
122 int src_sel
= 1; /* from fg_color */
125 int dst_x0
= region
->dx
;
127 int dst_y0
= region
->dy
;
128 int rop_alpha_sel
= 0;
129 int rop_alpha_code
= 0xCC;
134 int dst_pitch
= screen_width
* (m_iBpp
/ 8);
135 int dst_offset
= dst_y0
* dst_pitch
+ dst_x0
* (m_iBpp
/ 8);
136 int src_pitch
= screen_width
* (m_iBpp
/ 8);
137 int src_offset
= src_y0
* src_pitch
+ src_x0
* (m_iBpp
/ 8);
138 unsigned int command
= 0;
143 int bottom
= info
->var
.yres
- 1;
144 int right
= info
->var
.xres
- 1;
147 bottom
= (bottom
<< 16) | right
;
148 command
= (rop_alpha_sel
<< 26) | (pat_sel
<< 18) | (src_sel
<< 16)
149 | (x_dir
<< 20) | (y_dir
<< 21) | (command
<< 24)
150 | (clip_region
<< 23) | (clip_en
<< 22) | (tp_en
<< 27);
151 src_pitch
= (dst_pitch
<< 16) | src_pitch
;
152 awidth
= awidth
| (aheight
<< 16);
153 alpha_r
= ((rop_alpha_code
& 0xff) << 8) | (alpha_r
& 0xff)
155 src_x0
= (src_x0
& 0x1fff) | ((src_y0
& 0x1fff) << 16);
156 dst_x0
= (dst_x0
& 0x1fff) | ((dst_y0
& 0x1fff) << 16);
157 fg_color
= region
->color
;
161 writel(((u32
*)(info
->pseudo_palette
))[fg_color
], UGE_FCOLOR
);
162 writel(0, UGE_BCOLOR
);
163 writel(src_pitch
, UGE_PITCH
);
164 writel(src_offset
, UGE_SRCSTART
);
165 writel(dst_offset
, UGE_DSTSTART
);
166 writel(awidth
, UGE_WIDHEIGHT
);
167 writel(top
, UGE_CLIP0
);
168 writel(bottom
, UGE_CLIP1
);
169 writel(alpha_r
, UGE_ROPALPHA
);
170 writel(src_x0
, UGE_SRCXY
);
171 writel(dst_x0
, UGE_DSTXY
);
172 writel(command
, UGE_COMMAND
);
175 static void unifb_fillrect(struct fb_info
*info
,
176 const struct fb_fillrect
*region
)
178 struct fb_fillrect modded
;
181 if (info
->flags
& FBINFO_HWACCEL_DISABLED
) {
182 sys_fillrect(info
, region
);
186 vxres
= info
->var
.xres_virtual
;
187 vyres
= info
->var
.yres_virtual
;
189 memcpy(&modded
, region
, sizeof(struct fb_fillrect
));
191 if (!modded
.width
|| !modded
.height
||
192 modded
.dx
>= vxres
|| modded
.dy
>= vyres
)
195 if (modded
.dx
+ modded
.width
> vxres
)
196 modded
.width
= vxres
- modded
.dx
;
197 if (modded
.dy
+ modded
.height
> vyres
)
198 modded
.height
= vyres
- modded
.dy
;
200 unifb_prim_fillrect(info
, &modded
);
203 static void unifb_prim_copyarea(struct fb_info
*info
,
204 const struct fb_copyarea
*area
)
206 int awidth
= area
->width
;
207 int aheight
= area
->height
;
208 int m_iBpp
= info
->var
.bits_per_pixel
;
209 int screen_width
= info
->var
.xres
;
210 int src_sel
= 2; /* from mem */
212 int src_x0
= area
->sx
;
213 int dst_x0
= area
->dx
;
214 int src_y0
= area
->sy
;
215 int dst_y0
= area
->dy
;
217 int rop_alpha_sel
= 0;
218 int rop_alpha_code
= 0xCC;
224 int dst_pitch
= screen_width
* (m_iBpp
/ 8);
225 int dst_offset
= dst_y0
* dst_pitch
+ dst_x0
* (m_iBpp
/ 8);
226 int src_pitch
= screen_width
* (m_iBpp
/ 8);
227 int src_offset
= src_y0
* src_pitch
+ src_x0
* (m_iBpp
/ 8);
228 unsigned int command
= 0;
233 int bottom
= info
->var
.yres
;
234 int right
= info
->var
.xres
;
243 if (src_y0
- dst_y0
> 0) {
247 src_offset
= (src_y0
+ aheight
) * src_pitch
+
248 src_x0
* (m_iBpp
/ 8);
249 dst_offset
= (dst_y0
+ aheight
) * dst_pitch
+
250 dst_x0
* (m_iBpp
/ 8);
255 command
= (rop_alpha_sel
<< 26) | (pat_sel
<< 18) | (src_sel
<< 16) |
256 (x_dir
<< 20) | (y_dir
<< 21) | (command
<< 24) |
257 (clip_region
<< 23) | (clip_en
<< 22) | (tp_en
<< 27);
258 src_pitch
= (dst_pitch
<< 16) | src_pitch
;
259 awidth
= awidth
| (aheight
<< 16);
260 alpha_r
= ((rop_alpha_code
& 0xff) << 8) | (alpha_r
& 0xff) |
262 src_x0
= (src_x0
& 0x1fff) | ((src_y0
& 0x1fff) << 16);
263 dst_x0
= (dst_x0
& 0x1fff) | ((dst_y0
& 0x1fff) << 16);
264 bottom
= (bottom
<< 16) | right
;
268 writel(src_pitch
, UGE_PITCH
);
269 writel(src_offset
, UGE_SRCSTART
);
270 writel(dst_offset
, UGE_DSTSTART
);
271 writel(awidth
, UGE_WIDHEIGHT
);
272 writel(top
, UGE_CLIP0
);
273 writel(bottom
, UGE_CLIP1
);
274 writel(bg_color
, UGE_BCOLOR
);
275 writel(fg_color
, UGE_FCOLOR
);
276 writel(alpha_r
, UGE_ROPALPHA
);
277 writel(src_x0
, UGE_SRCXY
);
278 writel(dst_x0
, UGE_DSTXY
);
279 writel(command
, UGE_COMMAND
);
282 static void unifb_copyarea(struct fb_info
*info
, const struct fb_copyarea
*area
)
284 struct fb_copyarea modded
;
286 modded
.sx
= area
->sx
;
287 modded
.sy
= area
->sy
;
288 modded
.dx
= area
->dx
;
289 modded
.dy
= area
->dy
;
290 modded
.width
= area
->width
;
291 modded
.height
= area
->height
;
293 if (info
->flags
& FBINFO_HWACCEL_DISABLED
) {
294 sys_copyarea(info
, area
);
298 vxres
= info
->var
.xres_virtual
;
299 vyres
= info
->var
.yres_virtual
;
301 if (!modded
.width
|| !modded
.height
||
302 modded
.sx
>= vxres
|| modded
.sy
>= vyres
||
303 modded
.dx
>= vxres
|| modded
.dy
>= vyres
)
306 if (modded
.sx
+ modded
.width
> vxres
)
307 modded
.width
= vxres
- modded
.sx
;
308 if (modded
.dx
+ modded
.width
> vxres
)
309 modded
.width
= vxres
- modded
.dx
;
310 if (modded
.sy
+ modded
.height
> vyres
)
311 modded
.height
= vyres
- modded
.sy
;
312 if (modded
.dy
+ modded
.height
> vyres
)
313 modded
.height
= vyres
- modded
.dy
;
315 unifb_prim_copyarea(info
, &modded
);
318 static void unifb_imageblit(struct fb_info
*info
, const struct fb_image
*image
)
320 sys_imageblit(info
, image
);
323 static u_long
get_line_length(int xres_virtual
, int bpp
)
327 length
= xres_virtual
* bpp
;
328 length
= (length
+ 31) & ~31;
334 * Setting the video mode has been split into two parts.
335 * First part, xxxfb_check_var, must not write anything
336 * to hardware, it should only verify and adjust var.
337 * This means it doesn't alter par but it does use hardware
338 * data from it to check this var.
340 static int unifb_check_var(struct fb_var_screeninfo
*var
,
341 struct fb_info
*info
)
346 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
347 * as FB_VMODE_SMOOTH_XPAN is only used internally
350 if (var
->vmode
& FB_VMODE_CONUPDATE
) {
351 var
->vmode
|= FB_VMODE_YWRAP
;
352 var
->xoffset
= info
->var
.xoffset
;
353 var
->yoffset
= info
->var
.yoffset
;
357 * Some very basic checks
363 if (var
->xres
> var
->xres_virtual
)
364 var
->xres_virtual
= var
->xres
;
365 if (var
->yres
> var
->yres_virtual
)
366 var
->yres_virtual
= var
->yres
;
367 if (var
->bits_per_pixel
<= 1)
368 var
->bits_per_pixel
= 1;
369 else if (var
->bits_per_pixel
<= 8)
370 var
->bits_per_pixel
= 8;
371 else if (var
->bits_per_pixel
<= 16)
372 var
->bits_per_pixel
= 16;
373 else if (var
->bits_per_pixel
<= 24)
374 var
->bits_per_pixel
= 24;
375 else if (var
->bits_per_pixel
<= 32)
376 var
->bits_per_pixel
= 32;
380 if (var
->xres_virtual
< var
->xoffset
+ var
->xres
)
381 var
->xres_virtual
= var
->xoffset
+ var
->xres
;
382 if (var
->yres_virtual
< var
->yoffset
+ var
->yres
)
383 var
->yres_virtual
= var
->yoffset
+ var
->yres
;
389 get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
390 if (line_length
* var
->yres_virtual
> UNIFB_MEMSIZE
)
394 * Now that we checked it we alter var. The reason being is that the
395 * video mode passed in might not work but slight changes to it might
396 * make it work. This way we let the user know what is acceptable.
398 switch (var
->bits_per_pixel
) {
403 var
->green
.offset
= 0;
404 var
->green
.length
= 8;
405 var
->blue
.offset
= 0;
406 var
->blue
.length
= 8;
407 var
->transp
.offset
= 0;
408 var
->transp
.length
= 0;
410 case 16: /* RGBA 5551 */
411 if (var
->transp
.length
) {
414 var
->green
.offset
= 5;
415 var
->green
.length
= 5;
416 var
->blue
.offset
= 10;
417 var
->blue
.length
= 5;
418 var
->transp
.offset
= 15;
419 var
->transp
.length
= 1;
420 } else { /* RGB 565 */
421 var
->red
.offset
= 11;
423 var
->green
.offset
= 5;
424 var
->green
.length
= 6;
425 var
->blue
.offset
= 0;
426 var
->blue
.length
= 5;
427 var
->transp
.offset
= 0;
428 var
->transp
.length
= 0;
431 case 24: /* RGB 888 */
434 var
->green
.offset
= 8;
435 var
->green
.length
= 8;
436 var
->blue
.offset
= 16;
437 var
->blue
.length
= 8;
438 var
->transp
.offset
= 0;
439 var
->transp
.length
= 0;
441 case 32: /* RGBA 8888 */
442 var
->red
.offset
= 16;
444 var
->green
.offset
= 8;
445 var
->green
.length
= 8;
446 var
->blue
.offset
= 0;
447 var
->blue
.length
= 8;
448 var
->transp
.offset
= 24;
449 var
->transp
.length
= 8;
452 var
->red
.msb_right
= 0;
453 var
->green
.msb_right
= 0;
454 var
->blue
.msb_right
= 0;
455 var
->transp
.msb_right
= 0;
461 * This routine actually sets the video mode. It's in here where we
462 * the hardware state info->par and fix which can be affected by the
463 * change in par. For this driver it doesn't do much.
465 static int unifb_set_par(struct fb_info
*info
)
467 int hTotal
, vTotal
, hSyncStart
, hSyncEnd
, vSyncStart
, vSyncEnd
;
470 #ifdef CONFIG_PUV3_PM
475 for (i
= 0; i
<= 10; i
++) {
476 if (info
->var
.xres
== unifb_modes
[i
].xres
477 && info
->var
.yres
== unifb_modes
[i
].yres
478 && info
->var
.upper_margin
== unifb_modes
[i
].upper_margin
479 && info
->var
.lower_margin
== unifb_modes
[i
].lower_margin
480 && info
->var
.left_margin
== unifb_modes
[i
].left_margin
481 && info
->var
.right_margin
== unifb_modes
[i
].right_margin
482 && info
->var
.hsync_len
== unifb_modes
[i
].hsync_len
483 && info
->var
.vsync_len
== unifb_modes
[i
].vsync_len
) {
484 pixclk
= unifb_modes
[i
].pixclock
;
490 clk_vga
= clk_get(info
->device
, "VGA_CLK");
491 if (clk_vga
== ERR_PTR(-ENOENT
))
495 if (clk_set_rate(clk_vga
, pixclk
)) { /* set clock failed */
496 info
->fix
= unifb_fix
;
497 info
->var
= unifb_default
;
498 if (clk_set_rate(clk_vga
, unifb_default
.pixclock
))
504 info
->fix
.line_length
= get_line_length(info
->var
.xres_virtual
,
505 info
->var
.bits_per_pixel
);
507 hSyncStart
= info
->var
.xres
+ info
->var
.right_margin
;
508 hSyncEnd
= hSyncStart
+ info
->var
.hsync_len
;
509 hTotal
= hSyncEnd
+ info
->var
.left_margin
;
511 vSyncStart
= info
->var
.yres
+ info
->var
.lower_margin
;
512 vSyncEnd
= vSyncStart
+ info
->var
.vsync_len
;
513 vTotal
= vSyncEnd
+ info
->var
.upper_margin
;
515 switch (info
->var
.bits_per_pixel
) {
517 format
= UDE_CFG_DST8
;
520 format
= UDE_CFG_DST16
;
523 format
= UDE_CFG_DST24
;
526 format
= UDE_CFG_DST32
;
532 writel(info
->fix
.smem_start
, UDE_FSA
);
533 writel(info
->var
.yres
, UDE_LS
);
534 writel(get_line_length(info
->var
.xres
,
535 info
->var
.bits_per_pixel
) >> 3, UDE_PS
);
536 /* >> 3 for hardware required. */
537 writel((hTotal
<< 16) | (info
->var
.xres
), UDE_HAT
);
538 writel(((hTotal
- 1) << 16) | (info
->var
.xres
- 1), UDE_HBT
);
539 writel(((hSyncEnd
- 1) << 16) | (hSyncStart
- 1), UDE_HST
);
540 writel((vTotal
<< 16) | (info
->var
.yres
), UDE_VAT
);
541 writel(((vTotal
- 1) << 16) | (info
->var
.yres
- 1), UDE_VBT
);
542 writel(((vSyncEnd
- 1) << 16) | (vSyncStart
- 1), UDE_VST
);
543 writel(UDE_CFG_GDEN_ENABLE
| UDE_CFG_TIMEUP_ENABLE
544 | format
| 0xC0000001, UDE_CFG
);
550 * Set a single color register. The values supplied are already
551 * rounded down to the hardware's capabilities (according to the
552 * entries in the var structure). Return != 0 for invalid regno.
554 static int unifb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
555 u_int transp
, struct fb_info
*info
)
557 if (regno
>= 256) /* no. of hw registers */
560 /* grayscale works only partially under directcolor */
561 if (info
->var
.grayscale
) {
562 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
564 (red
* 77 + green
* 151 + blue
* 28) >> 8;
567 #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
568 switch (info
->fix
.visual
) {
569 case FB_VISUAL_TRUECOLOR
:
570 case FB_VISUAL_PSEUDOCOLOR
:
571 red
= CNVT_TOHW(red
, info
->var
.red
.length
);
572 green
= CNVT_TOHW(green
, info
->var
.green
.length
);
573 blue
= CNVT_TOHW(blue
, info
->var
.blue
.length
);
574 transp
= CNVT_TOHW(transp
, info
->var
.transp
.length
);
576 case FB_VISUAL_DIRECTCOLOR
:
577 red
= CNVT_TOHW(red
, 8); /* expect 8 bit DAC */
578 green
= CNVT_TOHW(green
, 8);
579 blue
= CNVT_TOHW(blue
, 8);
580 /* hey, there is bug in transp handling... */
581 transp
= CNVT_TOHW(transp
, 8);
585 /* Truecolor has hardware independent palette */
586 if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
592 v
= (red
<< info
->var
.red
.offset
) |
593 (green
<< info
->var
.green
.offset
) |
594 (blue
<< info
->var
.blue
.offset
) |
595 (transp
<< info
->var
.transp
.offset
);
596 switch (info
->var
.bits_per_pixel
) {
602 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
613 * Pan or Wrap the Display
615 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
617 static int unifb_pan_display(struct fb_var_screeninfo
*var
,
618 struct fb_info
*info
)
620 if (var
->vmode
& FB_VMODE_YWRAP
) {
622 || var
->yoffset
>= info
->var
.yres_virtual
626 if (var
->xoffset
+ info
->var
.xres
> info
->var
.xres_virtual
||
627 var
->yoffset
+ info
->var
.yres
> info
->var
.yres_virtual
)
630 info
->var
.xoffset
= var
->xoffset
;
631 info
->var
.yoffset
= var
->yoffset
;
632 if (var
->vmode
& FB_VMODE_YWRAP
)
633 info
->var
.vmode
|= FB_VMODE_YWRAP
;
635 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
639 int unifb_mmap(struct fb_info
*info
,
640 struct vm_area_struct
*vma
)
642 vma
->vm_page_prot
= pgprot_noncached(vma
->vm_page_prot
);
644 return vm_iomap_memory(vma
, info
->fix
.smem_start
, info
->fix
.smem_len
);
647 static const struct fb_ops unifb_ops
= {
648 .fb_read
= fb_sys_read
,
649 .fb_write
= fb_sys_write
,
650 .fb_check_var
= unifb_check_var
,
651 .fb_set_par
= unifb_set_par
,
652 .fb_setcolreg
= unifb_setcolreg
,
653 .fb_pan_display
= unifb_pan_display
,
654 .fb_fillrect
= unifb_fillrect
,
655 .fb_copyarea
= unifb_copyarea
,
656 .fb_imageblit
= unifb_imageblit
,
657 .fb_mmap
= unifb_mmap
,
663 static int unifb_probe(struct platform_device
*dev
)
665 struct fb_info
*info
;
666 u32 unifb_regs
[UNIFB_REGS_NUM
];
667 int retval
= -ENOMEM
;
668 struct resource
*iomem
;
671 videomemory
= (void *)__get_free_pages(GFP_KERNEL
| __GFP_COMP
,
672 get_order(UNIFB_MEMSIZE
));
676 memset(videomemory
, 0, UNIFB_MEMSIZE
);
678 unifb_fix
.smem_start
= virt_to_phys(videomemory
);
679 unifb_fix
.smem_len
= UNIFB_MEMSIZE
;
681 iomem
= platform_get_resource(dev
, IORESOURCE_MEM
, 0);
682 unifb_fix
.mmio_start
= iomem
->start
;
684 info
= framebuffer_alloc(sizeof(u32
)*256, &dev
->dev
);
688 info
->screen_base
= (char __iomem
*)videomemory
;
689 info
->fbops
= &unifb_ops
;
691 retval
= fb_find_mode(&info
->var
, info
, NULL
,
692 unifb_modes
, 10, &unifb_modes
[0], 16);
694 if (!retval
|| (retval
== 4))
695 info
->var
= unifb_default
;
697 info
->fix
= unifb_fix
;
698 info
->pseudo_palette
= info
->par
;
700 info
->flags
= FBINFO_FLAG_DEFAULT
;
701 #ifdef FB_ACCEL_PUV3_UNIGFX
702 info
->fix
.accel
= FB_ACCEL_PUV3_UNIGFX
;
705 retval
= fb_alloc_cmap(&info
->cmap
, 256, 0);
709 retval
= register_framebuffer(info
);
712 platform_set_drvdata(dev
, info
);
713 platform_device_add_data(dev
, unifb_regs
, sizeof(u32
) * UNIFB_REGS_NUM
);
715 fb_info(info
, "Virtual frame buffer device, using %dM of video memory\n",
716 UNIFB_MEMSIZE
>> 20);
719 fb_dealloc_cmap(&info
->cmap
);
721 framebuffer_release(info
);
726 static int unifb_remove(struct platform_device
*dev
)
728 struct fb_info
*info
= platform_get_drvdata(dev
);
731 unregister_framebuffer(info
);
732 fb_dealloc_cmap(&info
->cmap
);
733 framebuffer_release(info
);
739 static int unifb_resume(struct platform_device
*dev
)
742 u32
*unifb_regs
= dev
->dev
.platform_data
;
744 if (dev
->dev
.power
.power_state
.event
== PM_EVENT_ON
)
749 if (dev
->dev
.power
.power_state
.event
== PM_EVENT_SUSPEND
) {
750 writel(unifb_regs
[0], UDE_FSA
);
751 writel(unifb_regs
[1], UDE_LS
);
752 writel(unifb_regs
[2], UDE_PS
);
753 writel(unifb_regs
[3], UDE_HAT
);
754 writel(unifb_regs
[4], UDE_HBT
);
755 writel(unifb_regs
[5], UDE_HST
);
756 writel(unifb_regs
[6], UDE_VAT
);
757 writel(unifb_regs
[7], UDE_VBT
);
758 writel(unifb_regs
[8], UDE_VST
);
759 writel(unifb_regs
[9], UDE_CFG
);
761 dev
->dev
.power
.power_state
= PMSG_ON
;
768 static int unifb_suspend(struct platform_device
*dev
, pm_message_t mesg
)
770 u32
*unifb_regs
= dev
->dev
.platform_data
;
772 unifb_regs
[0] = readl(UDE_FSA
);
773 unifb_regs
[1] = readl(UDE_LS
);
774 unifb_regs
[2] = readl(UDE_PS
);
775 unifb_regs
[3] = readl(UDE_HAT
);
776 unifb_regs
[4] = readl(UDE_HBT
);
777 unifb_regs
[5] = readl(UDE_HST
);
778 unifb_regs
[6] = readl(UDE_VAT
);
779 unifb_regs
[7] = readl(UDE_VBT
);
780 unifb_regs
[8] = readl(UDE_VST
);
781 unifb_regs
[9] = readl(UDE_CFG
);
783 if (mesg
.event
== dev
->dev
.power
.power_state
.event
)
786 switch (mesg
.event
) {
787 case PM_EVENT_FREEZE
: /* about to take snapshot */
788 case PM_EVENT_PRETHAW
: /* before restoring snapshot */
799 dev
->dev
.power
.power_state
= mesg
;
804 #define unifb_resume NULL
805 #define unifb_suspend NULL
808 static struct platform_driver unifb_driver
= {
809 .probe
= unifb_probe
,
810 .remove
= unifb_remove
,
811 .resume
= unifb_resume
,
812 .suspend
= unifb_suspend
,
814 .name
= "PKUnity-v3-UNIGFX",
818 static int __init
unifb_init(void)
821 if (fb_get_options("unifb", NULL
))
825 return platform_driver_register(&unifb_driver
);
828 module_init(unifb_init
);
830 static void __exit
unifb_exit(void)
832 platform_driver_unregister(&unifb_driver
);
835 module_exit(unifb_exit
);
837 MODULE_LICENSE("GPL v2");