Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fb-puv3.c
Go to the documentation of this file.
1 /*
2  * Frame Buffer Driver for PKUnity-v3 Unigfx
3  * Code specific to PKUnity SoC and UniCore ISA
4  *
5  * Maintained by GUAN Xue-tao <[email protected]>
6  * Copyright (C) 2001-2010 Guan Xuetao
7  *
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.
11  */
12 
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>
18 #include <linux/fb.h>
19 #include <linux/init.h>
20 #include <linux/console.h>
21 
22 #include <asm/sizes.h>
23 #include <mach/hardware.h>
24 
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 */
29 
30 /*
31  * cause UNIGFX don not have EDID
32  * all the modes are organized as follow
33  */
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,
38  /* 1 640x480-75 VESA */
39  { "640x480@75", 75, 640, 480, 31500000, 120, 16, 18, 1, 64, 1,
41  /* 2 800x600-60 VESA */
42  { "800x600@60", 60, 800, 600, 40000000, 88, 40, 26, 1, 128, 1,
44  /* 3 800x600-75 VESA */
45  { "800x600@75", 75, 800, 600, 49500000, 160, 16, 23, 1, 80, 1,
47  /* 4 1024x768-60 VESA */
48  { "1024x768@60", 60, 1024, 768, 65000000, 160, 24, 34, 3, 136, 1,
50  /* 5 1024x768-75 VESA */
51  { "1024x768@75", 75, 1024, 768, 78750000, 176, 16, 30, 1, 96, 1,
53  /* 6 1280x960-60 VESA */
54  { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38, 1, 112, 1,
56  /* 7 1440x900-60 VESA */
57  { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30, 3, 152, 1,
59  /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
60  { "1024x600@60", 60, 1024, 600, 50650000, 160, 24, 26, 1, 136, 1,
62  /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
63  { "1024x600@75", 75, 1024, 600, 61500000, 176, 16, 23, 1, 96, 1,
65  /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
66  { "1366x768@60", 60, 1366, 768, 85500000, 256, 58, 18, 1, 112, 3,
68 };
69 
70 static struct fb_var_screeninfo unifb_default = {
71  .xres = 640,
72  .yres = 480,
73  .xres_virtual = 640,
74  .yres_virtual = 480,
75  .bits_per_pixel = 16,
76  .red = { 11, 5, 0 },
77  .green = { 5, 6, 0 },
78  .blue = { 0, 5, 0 },
79  .activate = FB_ACTIVATE_NOW,
80  .height = -1,
81  .width = -1,
82  .pixclock = 25175000,
83  .left_margin = 48,
84  .right_margin = 16,
85  .upper_margin = 33,
86  .lower_margin = 10,
87  .hsync_len = 96,
88  .vsync_len = 2,
89  .vmode = FB_VMODE_NONINTERLACED,
90 };
91 
92 static struct fb_fix_screeninfo unifb_fix = {
93  .id = "UNIGFX FB",
94  .type = FB_TYPE_PACKED_PIXELS,
95  .visual = FB_VISUAL_TRUECOLOR,
96  .xpanstep = 1,
97  .ypanstep = 1,
98  .ywrapstep = 1,
99  .accel = FB_ACCEL_NONE,
100 };
101 
102 static void unifb_sync(struct fb_info *info)
103 {
104  /* TODO: may, this can be replaced by interrupt */
105  int cnt;
106 
107  for (cnt = 0; cnt < 0x10000000; cnt++) {
108  if (readl(UGE_COMMAND) & 0x1000000)
109  return;
110  }
111 
112  if (cnt > 0x8000000)
113  dev_warn(info->device, "Warning: UniGFX GE time out ...\n");
114 }
115 
116 static void unifb_prim_fillrect(struct fb_info *info,
117  const struct fb_fillrect *region)
118 {
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 */
124  int pat_sel = 1;
125  int src_x0 = 0;
126  int dst_x0 = region->dx;
127  int src_y0 = 0;
128  int dst_y0 = region->dy;
129  int rop_alpha_sel = 0;
130  int rop_alpha_code = 0xCC;
131  int x_dir = 1;
132  int y_dir = 1;
133  int alpha_r = 0;
134  int alpha_sel = 0;
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;
140  int clip_region = 0;
141  int clip_en = 0;
142  int tp_en = 0;
143  int fg_color = 0;
144  int bottom = info->var.yres - 1;
145  int right = info->var.xres - 1;
146  int top = 0;
147 
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)
155  | (alpha_sel << 16);
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;
159 
160  unifb_sync(info);
161 
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);
174 }
175 
176 static void unifb_fillrect(struct fb_info *info,
177  const struct fb_fillrect *region)
178 {
179  struct fb_fillrect modded;
180  int vxres, vyres;
181 
182  if (info->flags & FBINFO_HWACCEL_DISABLED) {
183  sys_fillrect(info, region);
184  return;
185  }
186 
187  vxres = info->var.xres_virtual;
188  vyres = info->var.yres_virtual;
189 
190  memcpy(&modded, region, sizeof(struct fb_fillrect));
191 
192  if (!modded.width || !modded.height ||
193  modded.dx >= vxres || modded.dy >= vyres)
194  return;
195 
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;
200 
201  unifb_prim_fillrect(info, &modded);
202 }
203 
204 static void unifb_prim_copyarea(struct fb_info *info,
205  const struct fb_copyarea *area)
206 {
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 */
212  int pat_sel = 0;
213  int src_x0 = area->sx;
214  int dst_x0 = area->dx;
215  int src_y0 = area->sy;
216  int dst_y0 = area->dy;
217 
218  int rop_alpha_sel = 0;
219  int rop_alpha_code = 0xCC;
220  int x_dir = 1;
221  int y_dir = 1;
222 
223  int alpha_r = 0;
224  int alpha_sel = 0;
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;
230  int clip_region = 0;
231  int clip_en = 1;
232  int tp_en = 0;
233  int top = 0;
234  int bottom = info->var.yres;
235  int right = info->var.xres;
236  int fg_color = 0;
237  int bg_color = 0;
238 
239  if (src_x0 < 0)
240  src_x0 = 0;
241  if (src_y0 < 0)
242  src_y0 = 0;
243 
244  if (src_y0 - dst_y0 > 0) {
245  y_dir = 1;
246  } else {
247  y_dir = 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);
252  src_y0 += aheight;
253  dst_y0 += aheight;
254  }
255 
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) |
262  (alpha_sel << 16);
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;
266 
267  unifb_sync(info);
268 
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);
281 }
282 
283 static void unifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
284 {
285  struct fb_copyarea modded;
286  u32 vxres, vyres;
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;
293 
294  if (info->flags & FBINFO_HWACCEL_DISABLED) {
295  sys_copyarea(info, area);
296  return;
297  }
298 
299  vxres = info->var.xres_virtual;
300  vyres = info->var.yres_virtual;
301 
302  if (!modded.width || !modded.height ||
303  modded.sx >= vxres || modded.sy >= vyres ||
304  modded.dx >= vxres || modded.dy >= vyres)
305  return;
306 
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;
315 
316  unifb_prim_copyarea(info, &modded);
317 }
318 
319 static void unifb_imageblit(struct fb_info *info, const struct fb_image *image)
320 {
321  sys_imageblit(info, image);
322 }
323 
324 static u_long get_line_length(int xres_virtual, int bpp)
325 {
326  u_long length;
327 
328  length = xres_virtual * bpp;
329  length = (length + 31) & ~31;
330  length >>= 3;
331  return length;
332 }
333 
334 /*
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.
340  */
341 static int unifb_check_var(struct fb_var_screeninfo *var,
342  struct fb_info *info)
343 {
344  u_long line_length;
345 
346  /*
347  * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
348  * as FB_VMODE_SMOOTH_XPAN is only used internally
349  */
350 
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;
355  }
356 
357  /*
358  * Some very basic checks
359  */
360  if (!var->xres)
361  var->xres = 1;
362  if (!var->yres)
363  var->yres = 1;
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;
378  else
379  return -EINVAL;
380 
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;
385 
386  /*
387  * Memory limit
388  */
389  line_length =
390  get_line_length(var->xres_virtual, var->bits_per_pixel);
391  if (line_length * var->yres_virtual > UNIFB_MEMSIZE)
392  return -ENOMEM;
393 
394  /*
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.
398  */
399  switch (var->bits_per_pixel) {
400  case 1:
401  case 8:
402  var->red.offset = 0;
403  var->red.length = 8;
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;
410  break;
411  case 16: /* RGBA 5551 */
412  if (var->transp.length) {
413  var->red.offset = 0;
414  var->red.length = 5;
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;
423  var->red.length = 5;
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;
430  }
431  break;
432  case 24: /* RGB 888 */
433  var->red.offset = 0;
434  var->red.length = 8;
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;
441  break;
442  case 32: /* RGBA 8888 */
443  var->red.offset = 16;
444  var->red.length = 8;
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;
451  break;
452  }
453  var->red.msb_right = 0;
454  var->green.msb_right = 0;
455  var->blue.msb_right = 0;
456  var->transp.msb_right = 0;
457 
458  return 0;
459 }
460 
461 /*
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.
465  */
466 static int unifb_set_par(struct fb_info *info)
467 {
468  int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
469  int format;
470 
471 #ifdef CONFIG_PUV3_PM
472  struct clk *clk_vga;
473  u32 pixclk = 0;
474  int i;
475 
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;
486  break;
487  }
488  }
489 
490  /* set clock rate */
491  clk_vga = clk_get(info->device, "VGA_CLK");
492  if (clk_vga == ERR_PTR(-ENOENT))
493  return -ENOENT;
494 
495  if (pixclk != 0) {
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))
500  return -EINVAL;
501  }
502  }
503 #endif
504 
505  info->fix.line_length = get_line_length(info->var.xres_virtual,
506  info->var.bits_per_pixel);
507 
508  hSyncStart = info->var.xres + info->var.right_margin;
509  hSyncEnd = hSyncStart + info->var.hsync_len;
510  hTotal = hSyncEnd + info->var.left_margin;
511 
512  vSyncStart = info->var.yres + info->var.lower_margin;
513  vSyncEnd = vSyncStart + info->var.vsync_len;
514  vTotal = vSyncEnd + info->var.upper_margin;
515 
516  switch (info->var.bits_per_pixel) {
517  case 8:
518  format = UDE_CFG_DST8;
519  break;
520  case 16:
521  format = UDE_CFG_DST16;
522  break;
523  case 24:
524  format = UDE_CFG_DST24;
525  break;
526  case 32:
527  format = UDE_CFG_DST32;
528  break;
529  default:
530  return -EINVAL;
531  }
532 
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);
545  | format | 0xC0000001, UDE_CFG);
546 
547  return 0;
548 }
549 
550 /*
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.
554  */
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)
557 {
558  if (regno >= 256) /* no. of hw registers */
559  return 1;
560 
561  /* grayscale works only partially under directcolor */
562  if (info->var.grayscale) {
563  /* grayscale = 0.30*R + 0.59*G + 0.11*B */
564  red = green = blue =
565  (red * 77 + green * 151 + blue * 28) >> 8;
566  }
567 
568 #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
569  switch (info->fix.visual) {
570  case FB_VISUAL_TRUECOLOR:
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);
576  break;
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);
583  break;
584  }
585 #undef CNVT_TOHW
586  /* Truecolor has hardware independent palette */
587  if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
588  u32 v;
589 
590  if (regno >= 16)
591  return 1;
592 
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) {
598  case 8:
599  break;
600  case 16:
601  case 24:
602  case 32:
603  ((u32 *) (info->pseudo_palette))[regno] = v;
604  break;
605  default:
606  return 1;
607  }
608  return 0;
609  }
610  return 0;
611 }
612 
613 /*
614  * Pan or Wrap the Display
615  *
616  * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
617  */
618 static int unifb_pan_display(struct fb_var_screeninfo *var,
619  struct fb_info *info)
620 {
621  if (var->vmode & FB_VMODE_YWRAP) {
622  if (var->yoffset < 0
623  || var->yoffset >= info->var.yres_virtual
624  || var->xoffset)
625  return -EINVAL;
626  } else {
627  if (var->xoffset + info->var.xres > info->var.xres_virtual ||
628  var->yoffset + info->var.yres > info->var.yres_virtual)
629  return -EINVAL;
630  }
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;
635  else
636  info->var.vmode &= ~FB_VMODE_YWRAP;
637  return 0;
638 }
639 
640 int unifb_mmap(struct fb_info *info,
641  struct vm_area_struct *vma)
642 {
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;
646 
647  if (offset + size > info->fix.smem_len)
648  return -EINVAL;
649 
651 
652  if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size,
653  vma->vm_page_prot))
654  return -EAGAIN;
655 
656  /* VM_IO | VM_DONTEXPAND | VM_DONTDUMP are set by remap_pfn_range() */
657  return 0;
658 }
659 
660 static struct fb_ops unifb_ops = {
661  .fb_read = fb_sys_read,
662  .fb_write = fb_sys_write,
663  .fb_check_var = unifb_check_var,
664  .fb_set_par = unifb_set_par,
665  .fb_setcolreg = unifb_setcolreg,
666  .fb_pan_display = unifb_pan_display,
667  .fb_fillrect = unifb_fillrect,
668  .fb_copyarea = unifb_copyarea,
669  .fb_imageblit = unifb_imageblit,
670  .fb_mmap = unifb_mmap,
671 };
672 
673 /*
674  * Initialisation
675  */
676 static int unifb_probe(struct platform_device *dev)
677 {
678  struct fb_info *info;
679  u32 unifb_regs[UNIFB_REGS_NUM];
680  int retval = -ENOMEM;
681  struct resource *iomem;
682  void *videomemory;
683 
684  videomemory = (void *)__get_free_pages(GFP_KERNEL | __GFP_COMP,
686  if (!videomemory)
687  goto err;
688 
689  memset(videomemory, 0, UNIFB_MEMSIZE);
690 
691  unifb_fix.smem_start = virt_to_phys(videomemory);
692  unifb_fix.smem_len = UNIFB_MEMSIZE;
693 
694  iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
695  unifb_fix.mmio_start = iomem->start;
696 
697  info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
698  if (!info)
699  goto err;
700 
701  info->screen_base = (char __iomem *)videomemory;
702  info->fbops = &unifb_ops;
703 
704  retval = fb_find_mode(&info->var, info, NULL,
705  unifb_modes, 10, &unifb_modes[0], 16);
706 
707  if (!retval || (retval == 4))
708  info->var = unifb_default;
709 
710  info->fix = unifb_fix;
711  info->pseudo_palette = info->par;
712  info->par = NULL;
713  info->flags = FBINFO_FLAG_DEFAULT;
714 #ifdef FB_ACCEL_PUV3_UNIGFX
715  info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
716 #endif
717 
718  retval = fb_alloc_cmap(&info->cmap, 256, 0);
719  if (retval < 0)
720  goto err1;
721 
722  retval = register_framebuffer(info);
723  if (retval < 0)
724  goto err2;
725  platform_set_drvdata(dev, info);
726  platform_device_add_data(dev, unifb_regs, sizeof(u32) * UNIFB_REGS_NUM);
727 
729  "fb%d: Virtual frame buffer device, using %dM of video memory\n",
730  info->node, UNIFB_MEMSIZE >> 20);
731  return 0;
732 err2:
733  fb_dealloc_cmap(&info->cmap);
734 err1:
735  framebuffer_release(info);
736 err:
737  return retval;
738 }
739 
740 static int unifb_remove(struct platform_device *dev)
741 {
742  struct fb_info *info = platform_get_drvdata(dev);
743 
744  if (info) {
746  fb_dealloc_cmap(&info->cmap);
747  framebuffer_release(info);
748  }
749  return 0;
750 }
751 
752 #ifdef CONFIG_PM
753 static int unifb_resume(struct platform_device *dev)
754 {
755  int rc = 0;
756  u32 *unifb_regs = dev->dev.platform_data;
757 
758  if (dev->dev.power.power_state.event == PM_EVENT_ON)
759  return 0;
760 
761  console_lock();
762 
763  if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
764  writel(unifb_regs[0], UDE_FSA);
765  writel(unifb_regs[1], UDE_LS);
766  writel(unifb_regs[2], UDE_PS);
767  writel(unifb_regs[3], UDE_HAT);
768  writel(unifb_regs[4], UDE_HBT);
769  writel(unifb_regs[5], UDE_HST);
770  writel(unifb_regs[6], UDE_VAT);
771  writel(unifb_regs[7], UDE_VBT);
772  writel(unifb_regs[8], UDE_VST);
773  writel(unifb_regs[9], UDE_CFG);
774  }
775  dev->dev.power.power_state = PMSG_ON;
776 
777  console_unlock();
778 
779  return rc;
780 }
781 
782 static int unifb_suspend(struct platform_device *dev, pm_message_t mesg)
783 {
784  u32 *unifb_regs = dev->dev.platform_data;
785 
786  unifb_regs[0] = readl(UDE_FSA);
787  unifb_regs[1] = readl(UDE_LS);
788  unifb_regs[2] = readl(UDE_PS);
789  unifb_regs[3] = readl(UDE_HAT);
790  unifb_regs[4] = readl(UDE_HBT);
791  unifb_regs[5] = readl(UDE_HST);
792  unifb_regs[6] = readl(UDE_VAT);
793  unifb_regs[7] = readl(UDE_VBT);
794  unifb_regs[8] = readl(UDE_VST);
795  unifb_regs[9] = readl(UDE_CFG);
796 
797  if (mesg.event == dev->dev.power.power_state.event)
798  return 0;
799 
800  switch (mesg.event) {
801  case PM_EVENT_FREEZE: /* about to take snapshot */
802  case PM_EVENT_PRETHAW: /* before restoring snapshot */
803  goto done;
804  }
805 
806  console_lock();
807 
808  /* do nothing... */
809 
810  console_unlock();
811 
812 done:
813  dev->dev.power.power_state = mesg;
814 
815  return 0;
816 }
817 #else
818 #define unifb_resume NULL
819 #define unifb_suspend NULL
820 #endif
821 
822 static struct platform_driver unifb_driver = {
823  .probe = unifb_probe,
824  .remove = unifb_remove,
825  .resume = unifb_resume,
826  .suspend = unifb_suspend,
827  .driver = {
828  .name = "PKUnity-v3-UNIGFX",
829  },
830 };
831 
832 static int __init unifb_init(void)
833 {
834 #ifndef MODULE
835  if (fb_get_options("unifb", NULL))
836  return -ENODEV;
837 #endif
838 
839  return platform_driver_register(&unifb_driver);
840 }
841 
842 module_init(unifb_init);
843 
844 static void __exit unifb_exit(void)
845 {
846  platform_driver_unregister(&unifb_driver);
847 }
848 
849 module_exit(unifb_exit);
850 
851 MODULE_LICENSE("GPL v2");