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 <gxt@mprc.pku.edu.cn>
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#include <linux/mm.h>
22
23#include <asm/sizes.h>
24#include <asm/pgtable.h>
25#include <mach/hardware.h>
26
27/* Platform_data reserved for unifb registers. */
28#define UNIFB_REGS_NUM		10
29/* RAM reserved for the frame buffer. */
30#define UNIFB_MEMSIZE		(SZ_4M)		/* 4 MB for 1024*768*32b */
31
32/*
33 * cause UNIGFX don not have EDID
34 * all the modes are organized as follow
35 */
36static const struct fb_videomode unifb_modes[] = {
37	/* 0 640x480-60 VESA */
38	{ "640x480@60",  60,  640, 480,  25175000,  48, 16, 34, 10,  96, 1,
39	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
40	/* 1 640x480-75 VESA */
41	{ "640x480@75",  75,  640, 480,  31500000, 120, 16, 18,  1,  64, 1,
42	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
43	/* 2 800x600-60 VESA */
44	{ "800x600@60",  60,  800, 600,  40000000,  88, 40, 26,  1, 128, 1,
45	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
46	/* 3 800x600-75 VESA */
47	{ "800x600@75",  75,  800, 600,  49500000, 160, 16, 23,  1,  80, 1,
48	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
49	/* 4 1024x768-60 VESA */
50	{ "1024x768@60", 60, 1024, 768,  65000000, 160, 24, 34,  3, 136, 1,
51	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
52	/* 5 1024x768-75 VESA */
53	{ "1024x768@75", 75, 1024, 768,  78750000, 176, 16, 30,  1,  96, 1,
54	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
55	/* 6 1280x960-60 VESA */
56	{ "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38,  1, 112, 1,
57	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
58	/* 7 1440x900-60 VESA */
59	{ "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30,  3, 152, 1,
60	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
61	/* 8 FIXME 9 1024x600-60 VESA UNTESTED */
62	{ "1024x600@60", 60, 1024, 600,  50650000, 160, 24, 26,  1, 136, 1,
63	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
64	/* 9 FIXME 10 1024x600-75 VESA UNTESTED */
65	{ "1024x600@75", 75, 1024, 600,  61500000, 176, 16, 23,  1,  96, 1,
66	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
67	/* 10 FIXME 11 1366x768-60 VESA UNTESTED */
68	{ "1366x768@60", 60, 1366, 768,  85500000, 256, 58, 18,  1,  112, 3,
69	  0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
70};
71
72static struct fb_var_screeninfo unifb_default = {
73	.xres =		640,
74	.yres =		480,
75	.xres_virtual =	640,
76	.yres_virtual =	480,
77	.bits_per_pixel = 16,
78	.red =		{ 11, 5, 0 },
79	.green =	{ 5,  6, 0 },
80	.blue =		{ 0,  5, 0 },
81	.activate =	FB_ACTIVATE_NOW,
82	.height =	-1,
83	.width =	-1,
84	.pixclock =	25175000,
85	.left_margin =	48,
86	.right_margin =	16,
87	.upper_margin =	33,
88	.lower_margin =	10,
89	.hsync_len =	96,
90	.vsync_len =	2,
91	.vmode =	FB_VMODE_NONINTERLACED,
92};
93
94static struct fb_fix_screeninfo unifb_fix = {
95	.id =		"UNIGFX FB",
96	.type =		FB_TYPE_PACKED_PIXELS,
97	.visual =	FB_VISUAL_TRUECOLOR,
98	.xpanstep =	1,
99	.ypanstep =	1,
100	.ywrapstep =	1,
101	.accel =	FB_ACCEL_NONE,
102};
103
104static void unifb_sync(struct fb_info *info)
105{
106	/* TODO: may, this can be replaced by interrupt */
107	int cnt;
108
109	for (cnt = 0; cnt < 0x10000000; cnt++) {
110		if (readl(UGE_COMMAND) & 0x1000000)
111			return;
112	}
113
114	if (cnt > 0x8000000)
115		dev_warn(info->device, "Warning: UniGFX GE time out ...\n");
116}
117
118static void unifb_prim_fillrect(struct fb_info *info,
119				const struct fb_fillrect *region)
120{
121	int awidth = region->width;
122	int aheight = region->height;
123	int m_iBpp = info->var.bits_per_pixel;
124	int screen_width = info->var.xres;
125	int src_sel = 1;	/* from fg_color */
126	int pat_sel = 1;
127	int src_x0 = 0;
128	int dst_x0 = region->dx;
129	int src_y0 = 0;
130	int dst_y0 = region->dy;
131	int rop_alpha_sel = 0;
132	int rop_alpha_code = 0xCC;
133	int x_dir = 1;
134	int y_dir = 1;
135	int alpha_r = 0;
136	int alpha_sel = 0;
137	int dst_pitch = screen_width * (m_iBpp / 8);
138	int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
139	int src_pitch = screen_width * (m_iBpp / 8);
140	int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
141	unsigned int command = 0;
142	int clip_region = 0;
143	int clip_en = 0;
144	int tp_en = 0;
145	int fg_color = 0;
146	int bottom = info->var.yres - 1;
147	int right = info->var.xres - 1;
148	int top = 0;
149
150	bottom = (bottom << 16) | right;
151	command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16)
152		| (x_dir << 20) | (y_dir << 21) | (command << 24)
153		| (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
154	src_pitch = (dst_pitch << 16) | src_pitch;
155	awidth = awidth | (aheight << 16);
156	alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff)
157		| (alpha_sel << 16);
158	src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
159	dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
160	fg_color = region->color;
161
162	unifb_sync(info);
163
164	writel(((u32 *)(info->pseudo_palette))[fg_color], UGE_FCOLOR);
165	writel(0, UGE_BCOLOR);
166	writel(src_pitch, UGE_PITCH);
167	writel(src_offset, UGE_SRCSTART);
168	writel(dst_offset, UGE_DSTSTART);
169	writel(awidth, UGE_WIDHEIGHT);
170	writel(top, UGE_CLIP0);
171	writel(bottom, UGE_CLIP1);
172	writel(alpha_r, UGE_ROPALPHA);
173	writel(src_x0, UGE_SRCXY);
174	writel(dst_x0, UGE_DSTXY);
175	writel(command, UGE_COMMAND);
176}
177
178static void unifb_fillrect(struct fb_info *info,
179		const struct fb_fillrect *region)
180{
181	struct fb_fillrect modded;
182	int vxres, vyres;
183
184	if (info->flags & FBINFO_HWACCEL_DISABLED) {
185		sys_fillrect(info, region);
186		return;
187	}
188
189	vxres = info->var.xres_virtual;
190	vyres = info->var.yres_virtual;
191
192	memcpy(&modded, region, sizeof(struct fb_fillrect));
193
194	if (!modded.width || !modded.height ||
195	    modded.dx >= vxres || modded.dy >= vyres)
196		return;
197
198	if (modded.dx + modded.width > vxres)
199		modded.width = vxres - modded.dx;
200	if (modded.dy + modded.height > vyres)
201		modded.height = vyres - modded.dy;
202
203	unifb_prim_fillrect(info, &modded);
204}
205
206static void unifb_prim_copyarea(struct fb_info *info,
207				const struct fb_copyarea *area)
208{
209	int awidth = area->width;
210	int aheight = area->height;
211	int m_iBpp = info->var.bits_per_pixel;
212	int screen_width = info->var.xres;
213	int src_sel = 2;	/* from mem */
214	int pat_sel = 0;
215	int src_x0 = area->sx;
216	int dst_x0 = area->dx;
217	int src_y0 = area->sy;
218	int dst_y0 = area->dy;
219
220	int rop_alpha_sel = 0;
221	int rop_alpha_code = 0xCC;
222	int x_dir = 1;
223	int y_dir = 1;
224
225	int alpha_r = 0;
226	int alpha_sel = 0;
227	int dst_pitch = screen_width * (m_iBpp / 8);
228	int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
229	int src_pitch = screen_width * (m_iBpp / 8);
230	int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
231	unsigned int command = 0;
232	int clip_region = 0;
233	int clip_en = 1;
234	int tp_en = 0;
235	int top = 0;
236	int bottom = info->var.yres;
237	int right = info->var.xres;
238	int fg_color = 0;
239	int bg_color = 0;
240
241	if (src_x0 < 0)
242		src_x0 = 0;
243	if (src_y0 < 0)
244		src_y0 = 0;
245
246	if (src_y0 - dst_y0 > 0) {
247		y_dir = 1;
248	} else {
249		y_dir = 0;
250		src_offset = (src_y0 + aheight) * src_pitch +
251				src_x0 * (m_iBpp / 8);
252		dst_offset = (dst_y0 + aheight) * dst_pitch +
253				dst_x0 * (m_iBpp / 8);
254		src_y0 += aheight;
255		dst_y0 += aheight;
256	}
257
258	command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16) |
259		(x_dir << 20) | (y_dir << 21) | (command << 24) |
260		(clip_region << 23) | (clip_en << 22) | (tp_en << 27);
261	src_pitch = (dst_pitch << 16) | src_pitch;
262	awidth = awidth | (aheight << 16);
263	alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff) |
264		(alpha_sel << 16);
265	src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
266	dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
267	bottom = (bottom << 16) | right;
268
269	unifb_sync(info);
270
271	writel(src_pitch, UGE_PITCH);
272	writel(src_offset, UGE_SRCSTART);
273	writel(dst_offset, UGE_DSTSTART);
274	writel(awidth, UGE_WIDHEIGHT);
275	writel(top, UGE_CLIP0);
276	writel(bottom, UGE_CLIP1);
277	writel(bg_color, UGE_BCOLOR);
278	writel(fg_color, UGE_FCOLOR);
279	writel(alpha_r, UGE_ROPALPHA);
280	writel(src_x0, UGE_SRCXY);
281	writel(dst_x0, UGE_DSTXY);
282	writel(command, UGE_COMMAND);
283}
284
285static void unifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
286{
287	struct fb_copyarea modded;
288	u32 vxres, vyres;
289	modded.sx = area->sx;
290	modded.sy = area->sy;
291	modded.dx = area->dx;
292	modded.dy = area->dy;
293	modded.width = area->width;
294	modded.height = area->height;
295
296	if (info->flags & FBINFO_HWACCEL_DISABLED) {
297		sys_copyarea(info, area);
298		return;
299	}
300
301	vxres = info->var.xres_virtual;
302	vyres = info->var.yres_virtual;
303
304	if (!modded.width || !modded.height ||
305	    modded.sx >= vxres || modded.sy >= vyres ||
306	    modded.dx >= vxres || modded.dy >= vyres)
307		return;
308
309	if (modded.sx + modded.width > vxres)
310		modded.width = vxres - modded.sx;
311	if (modded.dx + modded.width > vxres)
312		modded.width = vxres - modded.dx;
313	if (modded.sy + modded.height > vyres)
314		modded.height = vyres - modded.sy;
315	if (modded.dy + modded.height > vyres)
316		modded.height = vyres - modded.dy;
317
318	unifb_prim_copyarea(info, &modded);
319}
320
321static void unifb_imageblit(struct fb_info *info, const struct fb_image *image)
322{
323	sys_imageblit(info, image);
324}
325
326static u_long get_line_length(int xres_virtual, int bpp)
327{
328	u_long length;
329
330	length = xres_virtual * bpp;
331	length = (length + 31) & ~31;
332	length >>= 3;
333	return length;
334}
335
336/*
337 *  Setting the video mode has been split into two parts.
338 *  First part, xxxfb_check_var, must not write anything
339 *  to hardware, it should only verify and adjust var.
340 *  This means it doesn't alter par but it does use hardware
341 *  data from it to check this var.
342 */
343static int unifb_check_var(struct fb_var_screeninfo *var,
344			 struct fb_info *info)
345{
346	u_long line_length;
347
348	/*
349	 *  FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
350	 *  as FB_VMODE_SMOOTH_XPAN is only used internally
351	 */
352
353	if (var->vmode & FB_VMODE_CONUPDATE) {
354		var->vmode |= FB_VMODE_YWRAP;
355		var->xoffset = info->var.xoffset;
356		var->yoffset = info->var.yoffset;
357	}
358
359	/*
360	 *  Some very basic checks
361	 */
362	if (!var->xres)
363		var->xres = 1;
364	if (!var->yres)
365		var->yres = 1;
366	if (var->xres > var->xres_virtual)
367		var->xres_virtual = var->xres;
368	if (var->yres > var->yres_virtual)
369		var->yres_virtual = var->yres;
370	if (var->bits_per_pixel <= 1)
371		var->bits_per_pixel = 1;
372	else if (var->bits_per_pixel <= 8)
373		var->bits_per_pixel = 8;
374	else if (var->bits_per_pixel <= 16)
375		var->bits_per_pixel = 16;
376	else if (var->bits_per_pixel <= 24)
377		var->bits_per_pixel = 24;
378	else if (var->bits_per_pixel <= 32)
379		var->bits_per_pixel = 32;
380	else
381		return -EINVAL;
382
383	if (var->xres_virtual < var->xoffset + var->xres)
384		var->xres_virtual = var->xoffset + var->xres;
385	if (var->yres_virtual < var->yoffset + var->yres)
386		var->yres_virtual = var->yoffset + var->yres;
387
388	/*
389	 *  Memory limit
390	 */
391	line_length =
392	    get_line_length(var->xres_virtual, var->bits_per_pixel);
393	if (line_length * var->yres_virtual > UNIFB_MEMSIZE)
394		return -ENOMEM;
395
396	/*
397	 * Now that we checked it we alter var. The reason being is that the
398	 * video mode passed in might not work but slight changes to it might
399	 * make it work. This way we let the user know what is acceptable.
400	 */
401	switch (var->bits_per_pixel) {
402	case 1:
403	case 8:
404		var->red.offset = 0;
405		var->red.length = 8;
406		var->green.offset = 0;
407		var->green.length = 8;
408		var->blue.offset = 0;
409		var->blue.length = 8;
410		var->transp.offset = 0;
411		var->transp.length = 0;
412		break;
413	case 16:		/* RGBA 5551 */
414		if (var->transp.length) {
415			var->red.offset = 0;
416			var->red.length = 5;
417			var->green.offset = 5;
418			var->green.length = 5;
419			var->blue.offset = 10;
420			var->blue.length = 5;
421			var->transp.offset = 15;
422			var->transp.length = 1;
423		} else {	/* RGB 565 */
424			var->red.offset = 11;
425			var->red.length = 5;
426			var->green.offset = 5;
427			var->green.length = 6;
428			var->blue.offset = 0;
429			var->blue.length = 5;
430			var->transp.offset = 0;
431			var->transp.length = 0;
432		}
433		break;
434	case 24:		/* RGB 888 */
435		var->red.offset = 0;
436		var->red.length = 8;
437		var->green.offset = 8;
438		var->green.length = 8;
439		var->blue.offset = 16;
440		var->blue.length = 8;
441		var->transp.offset = 0;
442		var->transp.length = 0;
443		break;
444	case 32:		/* RGBA 8888 */
445		var->red.offset = 16;
446		var->red.length = 8;
447		var->green.offset = 8;
448		var->green.length = 8;
449		var->blue.offset = 0;
450		var->blue.length = 8;
451		var->transp.offset = 24;
452		var->transp.length = 8;
453		break;
454	}
455	var->red.msb_right = 0;
456	var->green.msb_right = 0;
457	var->blue.msb_right = 0;
458	var->transp.msb_right = 0;
459
460	return 0;
461}
462
463/*
464 * This routine actually sets the video mode. It's in here where we
465 * the hardware state info->par and fix which can be affected by the
466 * change in par. For this driver it doesn't do much.
467 */
468static int unifb_set_par(struct fb_info *info)
469{
470	int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
471	int format;
472
473#ifdef CONFIG_PUV3_PM
474	struct clk *clk_vga;
475	u32 pixclk = 0;
476	int i;
477
478	for (i = 0; i <= 10; i++) {
479		if    (info->var.xres         == unifb_modes[i].xres
480		    && info->var.yres         == unifb_modes[i].yres
481		    && info->var.upper_margin == unifb_modes[i].upper_margin
482		    && info->var.lower_margin == unifb_modes[i].lower_margin
483		    && info->var.left_margin  == unifb_modes[i].left_margin
484		    && info->var.right_margin == unifb_modes[i].right_margin
485		    && info->var.hsync_len    == unifb_modes[i].hsync_len
486		    && info->var.vsync_len    == unifb_modes[i].vsync_len) {
487			pixclk = unifb_modes[i].pixclock;
488			break;
489		}
490	}
491
492	/* set clock rate */
493	clk_vga = clk_get(info->device, "VGA_CLK");
494	if (clk_vga == ERR_PTR(-ENOENT))
495		return -ENOENT;
496
497	if (pixclk != 0) {
498		if (clk_set_rate(clk_vga, pixclk)) { /* set clock failed */
499			info->fix = unifb_fix;
500			info->var = unifb_default;
501			if (clk_set_rate(clk_vga, unifb_default.pixclock))
502				return -EINVAL;
503		}
504	}
505#endif
506
507	info->fix.line_length = get_line_length(info->var.xres_virtual,
508						info->var.bits_per_pixel);
509
510	hSyncStart = info->var.xres + info->var.right_margin;
511	hSyncEnd = hSyncStart + info->var.hsync_len;
512	hTotal = hSyncEnd + info->var.left_margin;
513
514	vSyncStart = info->var.yres + info->var.lower_margin;
515	vSyncEnd = vSyncStart + info->var.vsync_len;
516	vTotal = vSyncEnd + info->var.upper_margin;
517
518	switch (info->var.bits_per_pixel) {
519	case 8:
520		format = UDE_CFG_DST8;
521		break;
522	case 16:
523		format = UDE_CFG_DST16;
524		break;
525	case 24:
526		format = UDE_CFG_DST24;
527		break;
528	case 32:
529		format = UDE_CFG_DST32;
530		break;
531	default:
532		return -EINVAL;
533	}
534
535	writel(info->fix.smem_start, UDE_FSA);
536	writel(info->var.yres, UDE_LS);
537	writel(get_line_length(info->var.xres,
538			info->var.bits_per_pixel) >> 3, UDE_PS);
539			/* >> 3 for hardware required. */
540	writel((hTotal << 16) | (info->var.xres), UDE_HAT);
541	writel(((hTotal - 1) << 16) | (info->var.xres - 1), UDE_HBT);
542	writel(((hSyncEnd - 1) << 16) | (hSyncStart - 1), UDE_HST);
543	writel((vTotal << 16) | (info->var.yres), UDE_VAT);
544	writel(((vTotal - 1) << 16) | (info->var.yres - 1), UDE_VBT);
545	writel(((vSyncEnd - 1) << 16) | (vSyncStart - 1), UDE_VST);
546	writel(UDE_CFG_GDEN_ENABLE | UDE_CFG_TIMEUP_ENABLE
547			| format | 0xC0000001, UDE_CFG);
548
549	return 0;
550}
551
552/*
553 *  Set a single color register. The values supplied are already
554 *  rounded down to the hardware's capabilities (according to the
555 *  entries in the var structure). Return != 0 for invalid regno.
556 */
557static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
558			 u_int transp, struct fb_info *info)
559{
560	if (regno >= 256)	/* no. of hw registers */
561		return 1;
562
563	/* grayscale works only partially under directcolor */
564	if (info->var.grayscale) {
565		/* grayscale = 0.30*R + 0.59*G + 0.11*B */
566		red = green = blue =
567		    (red * 77 + green * 151 + blue * 28) >> 8;
568	}
569
570#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
571	switch (info->fix.visual) {
572	case FB_VISUAL_TRUECOLOR:
573	case FB_VISUAL_PSEUDOCOLOR:
574		red = CNVT_TOHW(red, info->var.red.length);
575		green = CNVT_TOHW(green, info->var.green.length);
576		blue = CNVT_TOHW(blue, info->var.blue.length);
577		transp = CNVT_TOHW(transp, info->var.transp.length);
578		break;
579	case FB_VISUAL_DIRECTCOLOR:
580		red = CNVT_TOHW(red, 8);	/* expect 8 bit DAC */
581		green = CNVT_TOHW(green, 8);
582		blue = CNVT_TOHW(blue, 8);
583		/* hey, there is bug in transp handling... */
584		transp = CNVT_TOHW(transp, 8);
585		break;
586	}
587#undef CNVT_TOHW
588	/* Truecolor has hardware independent palette */
589	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
590		u32 v;
591
592		if (regno >= 16)
593			return 1;
594
595		v = (red << info->var.red.offset) |
596		    (green << info->var.green.offset) |
597		    (blue << info->var.blue.offset) |
598		    (transp << info->var.transp.offset);
599		switch (info->var.bits_per_pixel) {
600		case 8:
601			break;
602		case 16:
603		case 24:
604		case 32:
605			((u32 *) (info->pseudo_palette))[regno] = v;
606			break;
607		default:
608			return 1;
609		}
610		return 0;
611	}
612	return 0;
613}
614
615/*
616 *  Pan or Wrap the Display
617 *
618 *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
619 */
620static int unifb_pan_display(struct fb_var_screeninfo *var,
621			   struct fb_info *info)
622{
623	if (var->vmode & FB_VMODE_YWRAP) {
624		if (var->yoffset < 0
625		    || var->yoffset >= info->var.yres_virtual
626		    || var->xoffset)
627			return -EINVAL;
628	} else {
629		if (var->xoffset + info->var.xres > info->var.xres_virtual ||
630		    var->yoffset + info->var.yres > info->var.yres_virtual)
631			return -EINVAL;
632	}
633	info->var.xoffset = var->xoffset;
634	info->var.yoffset = var->yoffset;
635	if (var->vmode & FB_VMODE_YWRAP)
636		info->var.vmode |= FB_VMODE_YWRAP;
637	else
638		info->var.vmode &= ~FB_VMODE_YWRAP;
639	return 0;
640}
641
642int unifb_mmap(struct fb_info *info,
643		    struct vm_area_struct *vma)
644{
645	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
646
647	return vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len);
648}
649
650static struct fb_ops unifb_ops = {
651	.fb_read        = fb_sys_read,
652	.fb_write       = fb_sys_write,
653	.fb_check_var	= unifb_check_var,
654	.fb_set_par	= unifb_set_par,
655	.fb_setcolreg	= unifb_setcolreg,
656	.fb_pan_display	= unifb_pan_display,
657	.fb_fillrect	= unifb_fillrect,
658	.fb_copyarea	= unifb_copyarea,
659	.fb_imageblit   = unifb_imageblit,
660	.fb_mmap	= unifb_mmap,
661};
662
663/*
664 *  Initialisation
665 */
666static int unifb_probe(struct platform_device *dev)
667{
668	struct fb_info *info;
669	u32 unifb_regs[UNIFB_REGS_NUM];
670	int retval = -ENOMEM;
671	struct resource *iomem;
672	void *videomemory;
673
674	videomemory = (void *)__get_free_pages(GFP_KERNEL | __GFP_COMP,
675				get_order(UNIFB_MEMSIZE));
676	if (!videomemory)
677		goto err;
678
679	memset(videomemory, 0, UNIFB_MEMSIZE);
680
681	unifb_fix.smem_start = virt_to_phys(videomemory);
682	unifb_fix.smem_len = UNIFB_MEMSIZE;
683
684	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
685	unifb_fix.mmio_start = iomem->start;
686
687	info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
688	if (!info)
689		goto err;
690
691	info->screen_base = (char __iomem *)videomemory;
692	info->fbops = &unifb_ops;
693
694	retval = fb_find_mode(&info->var, info, NULL,
695			      unifb_modes, 10, &unifb_modes[0], 16);
696
697	if (!retval || (retval == 4))
698		info->var = unifb_default;
699
700	info->fix = unifb_fix;
701	info->pseudo_palette = info->par;
702	info->par = NULL;
703	info->flags = FBINFO_FLAG_DEFAULT;
704#ifdef FB_ACCEL_PUV3_UNIGFX
705	info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
706#endif
707
708	retval = fb_alloc_cmap(&info->cmap, 256, 0);
709	if (retval < 0)
710		goto err1;
711
712	retval = register_framebuffer(info);
713	if (retval < 0)
714		goto err2;
715	platform_set_drvdata(dev, info);
716	platform_device_add_data(dev, unifb_regs, sizeof(u32) * UNIFB_REGS_NUM);
717
718	fb_info(info, "Virtual frame buffer device, using %dM of video memory\n",
719		UNIFB_MEMSIZE >> 20);
720	return 0;
721err2:
722	fb_dealloc_cmap(&info->cmap);
723err1:
724	framebuffer_release(info);
725err:
726	return retval;
727}
728
729static int unifb_remove(struct platform_device *dev)
730{
731	struct fb_info *info = platform_get_drvdata(dev);
732
733	if (info) {
734		unregister_framebuffer(info);
735		fb_dealloc_cmap(&info->cmap);
736		framebuffer_release(info);
737	}
738	return 0;
739}
740
741#ifdef CONFIG_PM
742static int unifb_resume(struct platform_device *dev)
743{
744	int rc = 0;
745	u32 *unifb_regs = dev->dev.platform_data;
746
747	if (dev->dev.power.power_state.event == PM_EVENT_ON)
748		return 0;
749
750	console_lock();
751
752	if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
753		writel(unifb_regs[0], UDE_FSA);
754		writel(unifb_regs[1], UDE_LS);
755		writel(unifb_regs[2], UDE_PS);
756		writel(unifb_regs[3], UDE_HAT);
757		writel(unifb_regs[4], UDE_HBT);
758		writel(unifb_regs[5], UDE_HST);
759		writel(unifb_regs[6], UDE_VAT);
760		writel(unifb_regs[7], UDE_VBT);
761		writel(unifb_regs[8], UDE_VST);
762		writel(unifb_regs[9], UDE_CFG);
763	}
764	dev->dev.power.power_state = PMSG_ON;
765
766	console_unlock();
767
768	return rc;
769}
770
771static int unifb_suspend(struct platform_device *dev, pm_message_t mesg)
772{
773	u32 *unifb_regs = dev->dev.platform_data;
774
775	unifb_regs[0] = readl(UDE_FSA);
776	unifb_regs[1] = readl(UDE_LS);
777	unifb_regs[2] = readl(UDE_PS);
778	unifb_regs[3] = readl(UDE_HAT);
779	unifb_regs[4] = readl(UDE_HBT);
780	unifb_regs[5] = readl(UDE_HST);
781	unifb_regs[6] = readl(UDE_VAT);
782	unifb_regs[7] = readl(UDE_VBT);
783	unifb_regs[8] = readl(UDE_VST);
784	unifb_regs[9] = readl(UDE_CFG);
785
786	if (mesg.event == dev->dev.power.power_state.event)
787		return 0;
788
789	switch (mesg.event) {
790	case PM_EVENT_FREEZE:		/* about to take snapshot */
791	case PM_EVENT_PRETHAW:		/* before restoring snapshot */
792		goto done;
793	}
794
795	console_lock();
796
797	/* do nothing... */
798
799	console_unlock();
800
801done:
802	dev->dev.power.power_state = mesg;
803
804	return 0;
805}
806#else
807#define	unifb_resume	NULL
808#define unifb_suspend	NULL
809#endif
810
811static struct platform_driver unifb_driver = {
812	.probe	 = unifb_probe,
813	.remove  = unifb_remove,
814	.resume  = unifb_resume,
815	.suspend = unifb_suspend,
816	.driver  = {
817		.name	= "PKUnity-v3-UNIGFX",
818	},
819};
820
821static int __init unifb_init(void)
822{
823#ifndef MODULE
824	if (fb_get_options("unifb", NULL))
825		return -ENODEV;
826#endif
827
828	return platform_driver_register(&unifb_driver);
829}
830
831module_init(unifb_init);
832
833static void __exit unifb_exit(void)
834{
835	platform_driver_unregister(&unifb_driver);
836}
837
838module_exit(unifb_exit);
839
840MODULE_LICENSE("GPL v2");
841