* [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
@ 2011-01-22 10:29 ` Guan Xuetao
0 siblings, 0 replies; 6+ messages in thread
From: Guan Xuetao @ 2011-01-22 10:29 UTC (permalink / raw)
To: linux-fbdev; +Cc: linux-kernel
From: Guan Xuetao <gxt@mprc.pku.edu.cn>
This patch adds framebuffer driver for unigfx engine in PKUnity SoC.
1. add CONFIG_FB_PUV3_UNIGFX for configuration and build
2. add FB_ACCEL_PUV3_UNIGFX definition in include/linux/fb.h
3. driver based on drivers/video/vfb.c
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
---
drivers/video/Kconfig | 11 +
drivers/video/Makefile | 1 +
drivers/video/puv3_fb.c | 932 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fb.h | 2 +
4 files changed, 946 insertions(+), 0 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..13bf36e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2259,6 +2259,17 @@ config FB_JZ4740
help
Framebuffer support for the JZ4740 SoC.
+config FB_PUV3_UNIGFX
+ tristate "PKUnity v3 Unigfx framebuffer support"
+ depends on FB && UNICORE32 && ARCH_PUV3
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ help
+ Choose this option if you want to use the Unigfx device as a
+ framebuffer device. Without the support of PCI & AGP.
+
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..45ecd11 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_FB_MB862XX) += mb862xx/
obj-$(CONFIG_FB_MSM) += msm/
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
+obj-$(CONFIG_FB_PUV3_UNIGFX) += puv3_fb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/puv3_fb.c b/drivers/video/puv3_fb.c
new file mode 100644
index 0000000..d938a73
--- /dev/null
+++ b/drivers/video/puv3_fb.c
@@ -0,0 +1,932 @@
+/*
+ * Frame Buffer Driver for PKUnity-v3 Unigfx
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ * Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/console.h>
+
+#include <asm/sizes.h>
+#include <mach/hardware.h>
+
+static char *unifb_option = NULL;
+static unsigned long unifb_regs[10];
+
+/*
+ * cause UNIGFX don not have EDID
+ * all the modes are organized as follow
+ */
+static const struct fb_videomode unifb_modes[] = {
+ /* 0 640x480-60 VESA */
+ { "640x480@60", 60, 640, 480, 25175000, 48, 16, 34, 10, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 1 640x480-75 VESA */
+ { "640x480@75", 75, 640, 480, 31500000, 120, 16, 18, 1, 64, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 2 800x600-60 VESA */
+ { "800x600@60", 60, 800, 600, 40000000, 88, 40, 26, 1, 128, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 3 800x600-75 VESA */
+ { "800x600@75", 75, 800, 600, 49500000, 160, 16, 23, 1, 80, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 4 1024x768-60 VESA */
+ { "1024x768@60", 60, 1024, 768, 65000000, 160, 24, 34, 3, 136, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 5 1024x768-75 VESA */
+ { "1024x768@75", 75, 1024, 768, 78750000, 176, 16, 30, 1, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 6 1280x960-60 VESA */
+ { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38, 1, 112, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 7 1440x900-60 VESA */
+ { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30, 3, 152, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
+ { "1024x600@60", 60, 1024, 600, 50650000, 160, 24, 26, 1, 136, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
+ { "1024x600@75", 75, 1024, 600, 61500000, 176, 16, 23, 1, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
+ { "1366x768@60", 60, 1366, 768, 85500000, 256, 58, 18, 1, 112, 3,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+};
+
+/*
+ * RAM we reserve for the frame buffer. This defines the maximum screen size
+ *
+ * The default can be overridden if the driver is compiled as a module
+ */
+#define VIDEOMEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
+
+static void *videomemory;
+static u_long videomemorysize = VIDEOMEMSIZE;
+module_param(videomemorysize, ulong, 0);
+
+static struct fb_var_screeninfo unifb_default = {
+ .xres = 640,
+ .yres = 480,
+ .xres_virtual = 640,
+ .yres_virtual = 480,
+ .bits_per_pixel = 16,
+ .red = { 11, 5, 0 },
+ .green = { 5, 6, 0 },
+ .blue = { 0, 5, 0 },
+ .activate = FB_ACTIVATE_NOW,
+ .height = -1,
+ .width = -1,
+ .pixclock = 25175000,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 33,
+ .lower_margin = 10,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static struct fb_fix_screeninfo unifb_fix = {
+ .id = "UNIGFX FB",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 1,
+ .ypanstep = 1,
+ .ywrapstep = 1,
+ .accel = FB_ACCEL_NONE,
+};
+
+static int unifb_enable __initdata = 1; /* enable by default */
+module_param(unifb_enable, bool, 0);
+
+static int unifb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int unifb_set_par(struct fb_info *info);
+static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static int unifb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int unifb_mmap(struct fb_info *info,
+ struct vm_area_struct *vma);
+
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+static void unifb_sync(void)
+{
+ /* TODO: may, this can be replaced by interrupt */
+ int cnt;
+
+ for (cnt = 0; cnt < 0x10000000; cnt++) {
+ if (UGE_COMMAND & 0x1000000)
+ return;
+ }
+
+ if (cnt > 0x8000000)
+ printk(KERN_WARNING "Warning: UniGFX GE time out ...\n");
+}
+
+static void unifb_prim_fillrect(struct fb_info *info,
+ const struct fb_fillrect *region)
+{
+ int awidth = region->width;
+ int aheight = region->height;
+ int m_iBpp = info->var.bits_per_pixel;
+ int screen_width = info->var.xres;
+ int src_sel = 1; /* from fg_color */
+ int pat_sel = 1;
+ int src_x0 = 0;
+ int dst_x0 = region->dx;
+ int src_y0 = 0;
+ int dst_y0 = region->dy;
+ int rop_alpha_sel = 0;
+ int rop_alpha_code = 0xCC;
+ int x_dir = 1;
+ int y_dir = 1;
+ int alpha_r = 0;
+ int alpha_sel = 0;
+ int dst_pitch = screen_width * (m_iBpp / 8);
+ int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
+ int src_pitch = screen_width * (m_iBpp / 8);
+ int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
+ unsigned int command = 0;
+ int clip_region = 0;
+ int clip_en = 0;
+ int tp_en = 0;
+ int fg_color = 0;
+ int bottom = info->var.yres - 1;
+ int right = info->var.xres - 1;
+ int top = 0;
+
+ bottom = (bottom << 16) | right;
+ command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16)
+ | (x_dir << 20) | (y_dir << 21) | (command << 24)
+ | (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
+ src_pitch = (dst_pitch << 16) | src_pitch;
+ awidth = awidth | (aheight << 16);
+ alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff)
+ | (alpha_sel << 16);
+ src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
+ dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
+ fg_color = region->color;
+
+ unifb_sync();
+
+ UGE_FCOLOR = ((u32 *)(info->pseudo_palette))[fg_color];
+ UGE_BCOLOR = 0;
+ UGE_PITCH = src_pitch;
+ UGE_SRCSTART = src_offset;
+ UGE_DSTSTART = dst_offset;
+ UGE_WIDHEIGHT = awidth;
+ UGE_CLIP0 = top;
+ UGE_CLIP1 = bottom;
+ UGE_ROPALPHA = alpha_r;
+ UGE_SRCXY = src_x0;
+ UGE_DSTXY = dst_x0;
+ UGE_COMMAND = command;
+}
+
+static void unifb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *region)
+{
+ struct fb_fillrect modded;
+ int vxres, vyres;
+
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ sys_fillrect(info, region);
+ return;
+ }
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ memcpy(&modded, region, sizeof(struct fb_fillrect));
+
+ if (!modded.width || !modded.height ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if (modded.dx + modded.width > vxres)
+ modded.width = vxres - modded.dx;
+ if (modded.dy + modded.height > vyres)
+ modded.height = vyres - modded.dy;
+
+ unifb_prim_fillrect(info, &modded);
+}
+
+static void unifb_prim_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ int awidth = area->width;
+ int aheight = area->height;
+ int m_iBpp = info->var.bits_per_pixel;
+ int screen_width = info->var.xres;
+ int src_sel = 2; /* from mem */
+ int pat_sel = 0;
+ int src_x0 = area->sx;
+ int dst_x0 = area->dx;
+ int src_y0 = area->sy;
+ int dst_y0 = area->dy;
+
+ int rop_alpha_sel = 0;
+ int rop_alpha_code = 0xCC;
+ int x_dir = 1;
+ int y_dir = 1;
+
+ int alpha_r = 0;
+ int alpha_sel = 0;
+ int dst_pitch = screen_width * (m_iBpp / 8);
+ int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
+ int src_pitch = screen_width * (m_iBpp / 8);
+ int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
+ unsigned int command = 0;
+ int clip_region = 0;
+ int clip_en = 1;
+ int tp_en = 0;
+ int top = 0;
+ int bottom = info->var.yres;
+ int right = info->var.xres;
+ int fg_color = 0;
+ int bg_color = 0;
+
+ if (src_x0 < 0)
+ src_x0 = 0;
+ if (src_y0 < 0)
+ src_y0 = 0;
+
+ if (src_y0 - dst_y0 > 0) {
+ y_dir = 1;
+ } else {
+ y_dir = 0;
+ src_offset = (src_y0 + aheight) * src_pitch +
+ src_x0 * (m_iBpp / 8);
+ dst_offset = (dst_y0 + aheight) * dst_pitch +
+ dst_x0 * (m_iBpp / 8);
+ src_y0 += aheight;
+ dst_y0 += aheight;
+ }
+
+ command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16) |
+ (x_dir << 20) | (y_dir << 21) | (command << 24) |
+ (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
+ src_pitch = (dst_pitch << 16) | src_pitch;
+ awidth = awidth | (aheight << 16);
+ alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff) |
+ (alpha_sel << 16);
+ src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
+ dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
+ bottom = (bottom << 16) | right;
+
+ unifb_sync();
+
+ UGE_PITCH = src_pitch;
+ UGE_SRCSTART = src_offset;
+ UGE_DSTSTART = dst_offset;
+ UGE_WIDHEIGHT = awidth;
+ UGE_CLIP0 = top;
+ UGE_CLIP1 = bottom;
+ UGE_BCOLOR = bg_color;
+ UGE_FCOLOR = fg_color;
+ UGE_ROPALPHA = alpha_r;
+ UGE_SRCXY = src_x0;
+ UGE_DSTXY = dst_x0;
+ UGE_COMMAND = command;
+}
+
+static void unifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct fb_copyarea modded;
+ u32 vxres, vyres;
+ modded.sx = area->sx;
+ modded.sy = area->sy;
+ modded.dx = area->dx;
+ modded.dy = area->dy;
+ modded.width = area->width;
+ modded.height = area->height;
+
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ sys_copyarea(info, area);
+ return;
+ }
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ if (!modded.width || !modded.height ||
+ modded.sx >= vxres || modded.sy >= vyres ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if (modded.sx + modded.width > vxres)
+ modded.width = vxres - modded.sx;
+ if (modded.dx + modded.width > vxres)
+ modded.width = vxres - modded.dx;
+ if (modded.sy + modded.height > vyres)
+ modded.height = vyres - modded.sy;
+ if (modded.dy + modded.height > vyres)
+ modded.height = vyres - modded.dy;
+
+ unifb_prim_copyarea(info, &modded);
+}
+
+static void unifb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ sys_imageblit(info, image);
+}
+#endif
+
+static struct fb_ops unifb_ops = {
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_check_var = unifb_check_var,
+ .fb_set_par = unifb_set_par,
+ .fb_setcolreg = unifb_setcolreg,
+ .fb_pan_display = unifb_pan_display,
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+ .fb_fillrect = unifb_fillrect,
+ .fb_copyarea = unifb_copyarea,
+ .fb_imageblit = unifb_imageblit,
+#else
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+#endif
+ .fb_mmap = unifb_mmap,
+};
+
+static u_long get_line_length(int xres_virtual, int bpp)
+{
+ u_long length;
+
+ length = xres_virtual * bpp;
+ length = (length + 31) & ~31;
+ length >>= 3;
+ return length;
+}
+
+/*
+ * Setting the video mode has been split into two parts.
+ * First part, xxxfb_check_var, must not write anything
+ * to hardware, it should only verify and adjust var.
+ * This means it doesn't alter par but it does use hardware
+ * data from it to check this var.
+ */
+static int unifb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ u_long line_length;
+
+ /*
+ * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+ * as FB_VMODE_SMOOTH_XPAN is only used internally
+ */
+
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+ var->xoffset = info->var.xoffset;
+ var->yoffset = info->var.yoffset;
+ }
+
+ /*
+ * Some very basic checks
+ */
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+ if (var->bits_per_pixel <= 1)
+ var->bits_per_pixel = 1;
+ else if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+ else if (var->bits_per_pixel <= 24)
+ var->bits_per_pixel = 24;
+ else if (var->bits_per_pixel <= 32)
+ var->bits_per_pixel = 32;
+ else
+ return -EINVAL;
+
+ if (var->xres_virtual < var->xoffset + var->xres)
+ var->xres_virtual = var->xoffset + var->xres;
+ if (var->yres_virtual < var->yoffset + var->yres)
+ var->yres_virtual = var->yoffset + var->yres;
+
+ /*
+ * Memory limit
+ */
+ line_length =
+ get_line_length(var->xres_virtual, var->bits_per_pixel);
+ if (line_length * var->yres_virtual > videomemorysize)
+ return -ENOMEM;
+
+ /*
+ * Now that we checked it we alter var. The reason being is that the
+ * video mode passed in might not work but slight changes to it might
+ * make it work. This way we let the user know what is acceptable.
+ */
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGBA 5551 */
+ if (var->transp.length) {
+ var->red.offset = 0;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 10;
+ var->blue.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ } else { /* RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ }
+ break;
+ case 24: /* RGB 888 */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 32: /* RGBA 8888 */
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ }
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+
+ return 0;
+}
+
+/*
+ * This routine actually sets the video mode. It's in here where we
+ * the hardware state info->par and fix which can be affected by the
+ * change in par. For this driver it doesn't do much.
+ */
+static int unifb_set_par(struct fb_info *info)
+{
+ int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
+ int format;
+
+#ifdef CONFIG_PUV3_PM
+ struct clk *clk_vga;
+ u32 pixclk = 0;
+ int i;
+
+ for (i = 0; i <= 10; i++) {
+ if (info->var.xres == unifb_modes[i].xres
+ && info->var.yres == unifb_modes[i].yres
+ && info->var.upper_margin == unifb_modes[i].upper_margin
+ && info->var.lower_margin == unifb_modes[i].lower_margin
+ && info->var.left_margin == unifb_modes[i].left_margin
+ && info->var.right_margin == unifb_modes[i].right_margin
+ && info->var.hsync_len == unifb_modes[i].hsync_len
+ && info->var.vsync_len == unifb_modes[i].vsync_len) {
+ pixclk = unifb_modes[i].pixclock;
+ break;
+ }
+ }
+
+ /* set clock rate */
+ clk_vga = clk_get(NULL, "VGA_CLK");
+ i = 0;
+ if (pixclk != 0) {
+ if (clk_set_rate(clk_vga, pixclk) == 0)
+ i = 1;
+ }
+
+ if (i == 0) { /* set clock failed */
+ info->fix = unifb_fix;
+ info->var = unifb_default;
+ clk_set_rate(clk_vga, unifb_default.pixclock);
+ }
+#endif
+
+ info->fix.line_length = get_line_length(info->var.xres_virtual,
+ info->var.bits_per_pixel);
+
+ hSyncStart = info->var.xres + info->var.right_margin;
+ hSyncEnd = hSyncStart + info->var.hsync_len;
+ hTotal = hSyncEnd + info->var.left_margin;
+
+ vSyncStart = info->var.yres + info->var.lower_margin;
+ vSyncEnd = vSyncStart + info->var.vsync_len;
+ vTotal = vSyncEnd + info->var.upper_margin;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ format = UDE_CFG_DST8;
+ break;
+ case 16:
+ format = UDE_CFG_DST16;
+ break;
+ case 24:
+ format = UDE_CFG_DST24;
+ break;
+ case 32:
+ format = UDE_CFG_DST32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ UDE_FSA = unifb_regs[0] = PKUNITY_UNIGFX_MMAP_BASE;
+ UDE_LS = unifb_regs[1] = info->var.yres;
+ UDE_PS = unifb_regs[2] = get_line_length(info->var.xres,
+ info->var.bits_per_pixel) >> 3;
+ /* >> 3 for hardware required. */
+ UDE_HAT = unifb_regs[3] = (hTotal << 16) | (info->var.xres);
+ UDE_HBT = unifb_regs[4] = ((hTotal - 1) << 16) | (info->var.xres - 1);
+ UDE_HST = unifb_regs[5] = ((hSyncEnd - 1) << 16) | (hSyncStart - 1);
+ UDE_VAT = unifb_regs[6] = (vTotal << 16) | (info->var.yres);
+ UDE_VBT = unifb_regs[7] = ((vTotal - 1) << 16) | (info->var.yres - 1);
+ UDE_VST = unifb_regs[8] = ((vSyncEnd - 1) << 16) | (vSyncStart - 1);
+ UDE_CFG = unifb_regs[9] = UDE_CFG_GDEN_ENABLE | UDE_CFG_TIMEUP_ENABLE
+ | format | 0xC0000001;
+
+ return 0;
+}
+
+/*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ if (regno >= 256) /* no. of hw registers */
+ return 1;
+
+ /* grayscale works only partially under directcolor */
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue =
+ (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
+ green = CNVT_TOHW(green, 8);
+ blue = CNVT_TOHW(blue, 8);
+ /* hey, there is bug in transp handling... */
+ transp = CNVT_TOHW(transp, 8);
+ break;
+ }
+#undef CNVT_TOHW
+ /* Truecolor has hardware independent palette */
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
+ u32 v;
+
+ if (regno >= 16)
+ return 1;
+
+ v = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ break;
+ case 16:
+ ((u32 *) (info->pseudo_palette))[regno] = v;
+ break;
+ case 24:
+ case 32:
+ ((u32 *) (info->pseudo_palette))[regno] = v;
+ break;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+static int unifb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (var->yoffset < 0
+ || var->yoffset >= info->var.yres_virtual
+ || var->xoffset)
+ return -EINVAL;
+ } else {
+ if (var->xoffset + var->xres > info->var.xres_virtual ||
+ var->yoffset + var->yres > info->var.yres_virtual)
+ return -EINVAL;
+ }
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+
+int unifb_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long pos = info->fix.smem_start + offset;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+
+}
+
+#ifndef MODULE
+/*
+ * The virtual framebuffer driver is only enabled if explicitly
+ * requested by passing 'video=unifb:' (or any actual options).
+ */
+static int __init unifb_setup(char *options)
+{
+ char *this_opt;
+
+ unifb_enable = 0;
+
+ if (!options)
+ return 1;
+
+ unifb_enable = 1;
+
+ if (!*options)
+ return 1;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt)
+ continue;
+ /* Test disable for backwards compatibility */
+ if (!strcmp(this_opt, "disable"))
+ unifb_enable = 0;
+ }
+ return 1;
+}
+#endif /* MODULE */
+
+ /*
+ * Initialisation
+ */
+
+static int unifb_probe(struct platform_device *dev)
+{
+ struct fb_info *info;
+ int retval = -ENOMEM;
+
+ /*
+ * For real video cards we use ioremap.
+ */
+ videomemory = (void *)KUSER_UNIGFX_BASE;
+
+ /*
+ * VFB could clear memory to prevent kernel info
+ * leakage into userspace and set black screen
+ * VGA-based drivers MUST NOT clear memory if
+ * they want to be able to take over vgacon
+ */
+ /* memset(videomemory, 0, videomemorysize); */
+
+ info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
+ if (!info)
+ goto err;
+
+ info->screen_base = (char __iomem *)videomemory;
+ info->fbops = &unifb_ops;
+
+ retval = fb_find_mode(&info->var, info, unifb_option,
+ unifb_modes, 10, &unifb_modes[0], 16);
+
+ if (!retval || (retval == 4))
+ info->var = unifb_default;
+ unifb_fix.smem_start = PKUNITY_UNIGFX_MMAP_BASE;
+ unifb_fix.smem_len = videomemorysize;
+ unifb_fix.mmio_start = PKUNITY_UNIGFX_BASE;
+ info->fix = unifb_fix;
+ info->pseudo_palette = info->par;
+ info->par = NULL;
+ info->flags = FBINFO_FLAG_DEFAULT;
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+ info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
+ info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
+#else
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+#endif
+
+ retval = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (retval < 0)
+ goto err1;
+
+ retval = register_framebuffer(info);
+ if (retval < 0)
+ goto err2;
+ platform_set_drvdata(dev, info);
+
+ printk(KERN_INFO
+ "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
+ info->node, videomemorysize >> 10);
+ return 0;
+err2:
+ fb_dealloc_cmap(&info->cmap);
+err1:
+ framebuffer_release(info);
+err:
+ return retval;
+}
+
+static int unifb_remove(struct platform_device *dev)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+
+ if (info) {
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int unifb_resume(struct platform_device *dev)
+{
+ int rc = 0;
+
+ if (dev->dev.power.power_state.event == PM_EVENT_ON)
+ return 0;
+
+ acquire_console_sem();
+
+ if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ UDE_FSA = unifb_regs[0];
+ UDE_LS = unifb_regs[1];
+ UDE_PS = unifb_regs[2];
+ UDE_HAT = unifb_regs[3];
+ UDE_HBT = unifb_regs[4];
+ UDE_HST = unifb_regs[5];
+ UDE_VAT = unifb_regs[6];
+ UDE_VBT = unifb_regs[7];
+ UDE_VST = unifb_regs[8];
+ UDE_CFG = unifb_regs[9];
+ }
+ dev->dev.power.power_state = PMSG_ON;
+
+ release_console_sem();
+
+ return rc;
+}
+
+static int unifb_suspend(struct platform_device *dev, pm_message_t mesg)
+{
+ if (mesg.event == dev->dev.power.power_state.event)
+ return 0;
+
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE: /* about to take snapshot */
+ case PM_EVENT_PRETHAW: /* before restoring snapshot */
+ goto done;
+ }
+
+ acquire_console_sem();
+
+ /* do nothing... */
+
+ release_console_sem();
+
+done:
+ dev->dev.power.power_state = mesg;
+
+ return 0;
+}
+#else
+#define unifb_resume NULL
+#define unifb_suspend NULL
+#endif
+
+static struct platform_driver unifb_driver = {
+ .probe = unifb_probe,
+ .remove = unifb_remove,
+ .resume = unifb_resume,
+ .suspend = unifb_suspend,
+ .driver = {
+ .name = "unifb",
+ },
+};
+
+static struct platform_device *unifb_device;
+
+static int __init unifb_init(void)
+{
+ int ret = 0;
+
+#ifndef MODULE
+ if (fb_get_options("unifb", &unifb_option))
+ return -ENODEV;
+ unifb_setup(unifb_option);
+#endif
+
+ if (!unifb_enable)
+ return -ENXIO;
+
+ ret = platform_driver_register(&unifb_driver);
+
+ if (!ret) {
+ unifb_device = platform_device_alloc("unifb", 0);
+
+ if (unifb_device)
+ ret = platform_device_add(unifb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(unifb_device);
+ platform_driver_unregister(&unifb_driver);
+ }
+ }
+
+ return ret;
+}
+
+module_init(unifb_init);
+
+#ifdef MODULE
+static void __exit unifb_exit(void)
+{
+ platform_device_unregister(unifb_device);
+ platform_driver_unregister(&unifb_driver);
+}
+
+module_exit(unifb_exit);
+
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/include/linux/fb.h b/include/linux/fb.h
index d1631d3..4e9da1b 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -152,6 +152,8 @@
#define FB_ACCEL_PROSAVAGE_DDR 0x8d /* S3 ProSavage DDR */
#define FB_ACCEL_PROSAVAGE_DDRK 0x8e /* S3 ProSavage DDR-K */
+#define FB_ACCEL_PUV3_UNIGFX 0xa0 /* PKUnity-v3 Unigfx */
+
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
@ 2011-01-22 10:29 ` Guan Xuetao
0 siblings, 0 replies; 6+ messages in thread
From: Guan Xuetao @ 2011-01-22 10:29 UTC (permalink / raw)
To: linux-fbdev; +Cc: linux-kernel
From: Guan Xuetao <gxt@mprc.pku.edu.cn>
This patch adds framebuffer driver for unigfx engine in PKUnity SoC.
1. add CONFIG_FB_PUV3_UNIGFX for configuration and build
2. add FB_ACCEL_PUV3_UNIGFX definition in include/linux/fb.h
3. driver based on drivers/video/vfb.c
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
---
drivers/video/Kconfig | 11 +
drivers/video/Makefile | 1 +
drivers/video/puv3_fb.c | 932 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/fb.h | 2 +
4 files changed, 946 insertions(+), 0 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 27c1fb4..13bf36e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2259,6 +2259,17 @@ config FB_JZ4740
help
Framebuffer support for the JZ4740 SoC.
+config FB_PUV3_UNIGFX
+ tristate "PKUnity v3 Unigfx framebuffer support"
+ depends on FB && UNICORE32 && ARCH_PUV3
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_SYS_FOPS
+ help
+ Choose this option if you want to use the Unigfx device as a
+ framebuffer device. Without the support of PCI & AGP.
+
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 485e8ed..45ecd11 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -134,6 +134,7 @@ obj-$(CONFIG_FB_MB862XX) += mb862xx/
obj-$(CONFIG_FB_MSM) += msm/
obj-$(CONFIG_FB_NUC900) += nuc900fb.o
obj-$(CONFIG_FB_JZ4740) += jz4740_fb.o
+obj-$(CONFIG_FB_PUV3_UNIGFX) += puv3_fb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_UVESA) += uvesafb.o
diff --git a/drivers/video/puv3_fb.c b/drivers/video/puv3_fb.c
new file mode 100644
index 0000000..d938a73
--- /dev/null
+++ b/drivers/video/puv3_fb.c
@@ -0,0 +1,932 @@
+/*
+ * Frame Buffer Driver for PKUnity-v3 Unigfx
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ * Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/console.h>
+
+#include <asm/sizes.h>
+#include <mach/hardware.h>
+
+static char *unifb_option = NULL;
+static unsigned long unifb_regs[10];
+
+/*
+ * cause UNIGFX don not have EDID
+ * all the modes are organized as follow
+ */
+static const struct fb_videomode unifb_modes[] = {
+ /* 0 640x480-60 VESA */
+ { "640x480@60", 60, 640, 480, 25175000, 48, 16, 34, 10, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 1 640x480-75 VESA */
+ { "640x480@75", 75, 640, 480, 31500000, 120, 16, 18, 1, 64, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 2 800x600-60 VESA */
+ { "800x600@60", 60, 800, 600, 40000000, 88, 40, 26, 1, 128, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 3 800x600-75 VESA */
+ { "800x600@75", 75, 800, 600, 49500000, 160, 16, 23, 1, 80, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 4 1024x768-60 VESA */
+ { "1024x768@60", 60, 1024, 768, 65000000, 160, 24, 34, 3, 136, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 5 1024x768-75 VESA */
+ { "1024x768@75", 75, 1024, 768, 78750000, 176, 16, 30, 1, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 6 1280x960-60 VESA */
+ { "1280x960@60", 60, 1280, 960, 108000000, 312, 96, 38, 1, 112, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 7 1440x900-60 VESA */
+ { "1440x900@60", 60, 1440, 900, 106500000, 232, 80, 30, 3, 152, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 8 FIXME 9 1024x600-60 VESA UNTESTED */
+ { "1024x600@60", 60, 1024, 600, 50650000, 160, 24, 26, 1, 136, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 9 FIXME 10 1024x600-75 VESA UNTESTED */
+ { "1024x600@75", 75, 1024, 600, 61500000, 176, 16, 23, 1, 96, 1,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+ /* 10 FIXME 11 1366x768-60 VESA UNTESTED */
+ { "1366x768@60", 60, 1366, 768, 85500000, 256, 58, 18, 1, 112, 3,
+ 0, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA },
+};
+
+/*
+ * RAM we reserve for the frame buffer. This defines the maximum screen size
+ *
+ * The default can be overridden if the driver is compiled as a module
+ */
+#define VIDEOMEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
+
+static void *videomemory;
+static u_long videomemorysize = VIDEOMEMSIZE;
+module_param(videomemorysize, ulong, 0);
+
+static struct fb_var_screeninfo unifb_default = {
+ .xres = 640,
+ .yres = 480,
+ .xres_virtual = 640,
+ .yres_virtual = 480,
+ .bits_per_pixel = 16,
+ .red = { 11, 5, 0 },
+ .green = { 5, 6, 0 },
+ .blue = { 0, 5, 0 },
+ .activate = FB_ACTIVATE_NOW,
+ .height = -1,
+ .width = -1,
+ .pixclock = 25175000,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 33,
+ .lower_margin = 10,
+ .hsync_len = 96,
+ .vsync_len = 2,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static struct fb_fix_screeninfo unifb_fix = {
+ .id = "UNIGFX FB",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 1,
+ .ypanstep = 1,
+ .ywrapstep = 1,
+ .accel = FB_ACCEL_NONE,
+};
+
+static int unifb_enable __initdata = 1; /* enable by default */
+module_param(unifb_enable, bool, 0);
+
+static int unifb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int unifb_set_par(struct fb_info *info);
+static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static int unifb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+static int unifb_mmap(struct fb_info *info,
+ struct vm_area_struct *vma);
+
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+static void unifb_sync(void)
+{
+ /* TODO: may, this can be replaced by interrupt */
+ int cnt;
+
+ for (cnt = 0; cnt < 0x10000000; cnt++) {
+ if (UGE_COMMAND & 0x1000000)
+ return;
+ }
+
+ if (cnt > 0x8000000)
+ printk(KERN_WARNING "Warning: UniGFX GE time out ...\n");
+}
+
+static void unifb_prim_fillrect(struct fb_info *info,
+ const struct fb_fillrect *region)
+{
+ int awidth = region->width;
+ int aheight = region->height;
+ int m_iBpp = info->var.bits_per_pixel;
+ int screen_width = info->var.xres;
+ int src_sel = 1; /* from fg_color */
+ int pat_sel = 1;
+ int src_x0 = 0;
+ int dst_x0 = region->dx;
+ int src_y0 = 0;
+ int dst_y0 = region->dy;
+ int rop_alpha_sel = 0;
+ int rop_alpha_code = 0xCC;
+ int x_dir = 1;
+ int y_dir = 1;
+ int alpha_r = 0;
+ int alpha_sel = 0;
+ int dst_pitch = screen_width * (m_iBpp / 8);
+ int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
+ int src_pitch = screen_width * (m_iBpp / 8);
+ int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
+ unsigned int command = 0;
+ int clip_region = 0;
+ int clip_en = 0;
+ int tp_en = 0;
+ int fg_color = 0;
+ int bottom = info->var.yres - 1;
+ int right = info->var.xres - 1;
+ int top = 0;
+
+ bottom = (bottom << 16) | right;
+ command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16)
+ | (x_dir << 20) | (y_dir << 21) | (command << 24)
+ | (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
+ src_pitch = (dst_pitch << 16) | src_pitch;
+ awidth = awidth | (aheight << 16);
+ alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff)
+ | (alpha_sel << 16);
+ src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
+ dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
+ fg_color = region->color;
+
+ unifb_sync();
+
+ UGE_FCOLOR = ((u32 *)(info->pseudo_palette))[fg_color];
+ UGE_BCOLOR = 0;
+ UGE_PITCH = src_pitch;
+ UGE_SRCSTART = src_offset;
+ UGE_DSTSTART = dst_offset;
+ UGE_WIDHEIGHT = awidth;
+ UGE_CLIP0 = top;
+ UGE_CLIP1 = bottom;
+ UGE_ROPALPHA = alpha_r;
+ UGE_SRCXY = src_x0;
+ UGE_DSTXY = dst_x0;
+ UGE_COMMAND = command;
+}
+
+static void unifb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *region)
+{
+ struct fb_fillrect modded;
+ int vxres, vyres;
+
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ sys_fillrect(info, region);
+ return;
+ }
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ memcpy(&modded, region, sizeof(struct fb_fillrect));
+
+ if (!modded.width || !modded.height ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if (modded.dx + modded.width > vxres)
+ modded.width = vxres - modded.dx;
+ if (modded.dy + modded.height > vyres)
+ modded.height = vyres - modded.dy;
+
+ unifb_prim_fillrect(info, &modded);
+}
+
+static void unifb_prim_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ int awidth = area->width;
+ int aheight = area->height;
+ int m_iBpp = info->var.bits_per_pixel;
+ int screen_width = info->var.xres;
+ int src_sel = 2; /* from mem */
+ int pat_sel = 0;
+ int src_x0 = area->sx;
+ int dst_x0 = area->dx;
+ int src_y0 = area->sy;
+ int dst_y0 = area->dy;
+
+ int rop_alpha_sel = 0;
+ int rop_alpha_code = 0xCC;
+ int x_dir = 1;
+ int y_dir = 1;
+
+ int alpha_r = 0;
+ int alpha_sel = 0;
+ int dst_pitch = screen_width * (m_iBpp / 8);
+ int dst_offset = dst_y0 * dst_pitch + dst_x0 * (m_iBpp / 8);
+ int src_pitch = screen_width * (m_iBpp / 8);
+ int src_offset = src_y0 * src_pitch + src_x0 * (m_iBpp / 8);
+ unsigned int command = 0;
+ int clip_region = 0;
+ int clip_en = 1;
+ int tp_en = 0;
+ int top = 0;
+ int bottom = info->var.yres;
+ int right = info->var.xres;
+ int fg_color = 0;
+ int bg_color = 0;
+
+ if (src_x0 < 0)
+ src_x0 = 0;
+ if (src_y0 < 0)
+ src_y0 = 0;
+
+ if (src_y0 - dst_y0 > 0) {
+ y_dir = 1;
+ } else {
+ y_dir = 0;
+ src_offset = (src_y0 + aheight) * src_pitch +
+ src_x0 * (m_iBpp / 8);
+ dst_offset = (dst_y0 + aheight) * dst_pitch +
+ dst_x0 * (m_iBpp / 8);
+ src_y0 += aheight;
+ dst_y0 += aheight;
+ }
+
+ command = (rop_alpha_sel << 26) | (pat_sel << 18) | (src_sel << 16) |
+ (x_dir << 20) | (y_dir << 21) | (command << 24) |
+ (clip_region << 23) | (clip_en << 22) | (tp_en << 27);
+ src_pitch = (dst_pitch << 16) | src_pitch;
+ awidth = awidth | (aheight << 16);
+ alpha_r = ((rop_alpha_code & 0xff) << 8) | (alpha_r & 0xff) |
+ (alpha_sel << 16);
+ src_x0 = (src_x0 & 0x1fff) | ((src_y0 & 0x1fff) << 16);
+ dst_x0 = (dst_x0 & 0x1fff) | ((dst_y0 & 0x1fff) << 16);
+ bottom = (bottom << 16) | right;
+
+ unifb_sync();
+
+ UGE_PITCH = src_pitch;
+ UGE_SRCSTART = src_offset;
+ UGE_DSTSTART = dst_offset;
+ UGE_WIDHEIGHT = awidth;
+ UGE_CLIP0 = top;
+ UGE_CLIP1 = bottom;
+ UGE_BCOLOR = bg_color;
+ UGE_FCOLOR = fg_color;
+ UGE_ROPALPHA = alpha_r;
+ UGE_SRCXY = src_x0;
+ UGE_DSTXY = dst_x0;
+ UGE_COMMAND = command;
+}
+
+static void unifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
+{
+ struct fb_copyarea modded;
+ u32 vxres, vyres;
+ modded.sx = area->sx;
+ modded.sy = area->sy;
+ modded.dx = area->dx;
+ modded.dy = area->dy;
+ modded.width = area->width;
+ modded.height = area->height;
+
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ sys_copyarea(info, area);
+ return;
+ }
+
+ vxres = info->var.xres_virtual;
+ vyres = info->var.yres_virtual;
+
+ if (!modded.width || !modded.height ||
+ modded.sx >= vxres || modded.sy >= vyres ||
+ modded.dx >= vxres || modded.dy >= vyres)
+ return;
+
+ if (modded.sx + modded.width > vxres)
+ modded.width = vxres - modded.sx;
+ if (modded.dx + modded.width > vxres)
+ modded.width = vxres - modded.dx;
+ if (modded.sy + modded.height > vyres)
+ modded.height = vyres - modded.sy;
+ if (modded.dy + modded.height > vyres)
+ modded.height = vyres - modded.dy;
+
+ unifb_prim_copyarea(info, &modded);
+}
+
+static void unifb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ sys_imageblit(info, image);
+}
+#endif
+
+static struct fb_ops unifb_ops = {
+ .fb_read = fb_sys_read,
+ .fb_write = fb_sys_write,
+ .fb_check_var = unifb_check_var,
+ .fb_set_par = unifb_set_par,
+ .fb_setcolreg = unifb_setcolreg,
+ .fb_pan_display = unifb_pan_display,
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+ .fb_fillrect = unifb_fillrect,
+ .fb_copyarea = unifb_copyarea,
+ .fb_imageblit = unifb_imageblit,
+#else
+ .fb_fillrect = sys_fillrect,
+ .fb_copyarea = sys_copyarea,
+ .fb_imageblit = sys_imageblit,
+#endif
+ .fb_mmap = unifb_mmap,
+};
+
+static u_long get_line_length(int xres_virtual, int bpp)
+{
+ u_long length;
+
+ length = xres_virtual * bpp;
+ length = (length + 31) & ~31;
+ length >>= 3;
+ return length;
+}
+
+/*
+ * Setting the video mode has been split into two parts.
+ * First part, xxxfb_check_var, must not write anything
+ * to hardware, it should only verify and adjust var.
+ * This means it doesn't alter par but it does use hardware
+ * data from it to check this var.
+ */
+static int unifb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ u_long line_length;
+
+ /*
+ * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
+ * as FB_VMODE_SMOOTH_XPAN is only used internally
+ */
+
+ if (var->vmode & FB_VMODE_CONUPDATE) {
+ var->vmode |= FB_VMODE_YWRAP;
+ var->xoffset = info->var.xoffset;
+ var->yoffset = info->var.yoffset;
+ }
+
+ /*
+ * Some very basic checks
+ */
+ if (!var->xres)
+ var->xres = 1;
+ if (!var->yres)
+ var->yres = 1;
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+ if (var->bits_per_pixel <= 1)
+ var->bits_per_pixel = 1;
+ else if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+ else if (var->bits_per_pixel <= 24)
+ var->bits_per_pixel = 24;
+ else if (var->bits_per_pixel <= 32)
+ var->bits_per_pixel = 32;
+ else
+ return -EINVAL;
+
+ if (var->xres_virtual < var->xoffset + var->xres)
+ var->xres_virtual = var->xoffset + var->xres;
+ if (var->yres_virtual < var->yoffset + var->yres)
+ var->yres_virtual = var->yoffset + var->yres;
+
+ /*
+ * Memory limit
+ */
+ line_length + get_line_length(var->xres_virtual, var->bits_per_pixel);
+ if (line_length * var->yres_virtual > videomemorysize)
+ return -ENOMEM;
+
+ /*
+ * Now that we checked it we alter var. The reason being is that the
+ * video mode passed in might not work but slight changes to it might
+ * make it work. This way we let the user know what is acceptable.
+ */
+ switch (var->bits_per_pixel) {
+ case 1:
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGBA 5551 */
+ if (var->transp.length) {
+ var->red.offset = 0;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 5;
+ var->blue.offset = 10;
+ var->blue.length = 5;
+ var->transp.offset = 15;
+ var->transp.length = 1;
+ } else { /* RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ }
+ break;
+ case 24: /* RGB 888 */
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 16;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 32: /* RGBA 8888 */
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 24;
+ var->transp.length = 8;
+ break;
+ }
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+
+ return 0;
+}
+
+/*
+ * This routine actually sets the video mode. It's in here where we
+ * the hardware state info->par and fix which can be affected by the
+ * change in par. For this driver it doesn't do much.
+ */
+static int unifb_set_par(struct fb_info *info)
+{
+ int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
+ int format;
+
+#ifdef CONFIG_PUV3_PM
+ struct clk *clk_vga;
+ u32 pixclk = 0;
+ int i;
+
+ for (i = 0; i <= 10; i++) {
+ if (info->var.xres = unifb_modes[i].xres
+ && info->var.yres = unifb_modes[i].yres
+ && info->var.upper_margin = unifb_modes[i].upper_margin
+ && info->var.lower_margin = unifb_modes[i].lower_margin
+ && info->var.left_margin = unifb_modes[i].left_margin
+ && info->var.right_margin = unifb_modes[i].right_margin
+ && info->var.hsync_len = unifb_modes[i].hsync_len
+ && info->var.vsync_len = unifb_modes[i].vsync_len) {
+ pixclk = unifb_modes[i].pixclock;
+ break;
+ }
+ }
+
+ /* set clock rate */
+ clk_vga = clk_get(NULL, "VGA_CLK");
+ i = 0;
+ if (pixclk != 0) {
+ if (clk_set_rate(clk_vga, pixclk) = 0)
+ i = 1;
+ }
+
+ if (i = 0) { /* set clock failed */
+ info->fix = unifb_fix;
+ info->var = unifb_default;
+ clk_set_rate(clk_vga, unifb_default.pixclock);
+ }
+#endif
+
+ info->fix.line_length = get_line_length(info->var.xres_virtual,
+ info->var.bits_per_pixel);
+
+ hSyncStart = info->var.xres + info->var.right_margin;
+ hSyncEnd = hSyncStart + info->var.hsync_len;
+ hTotal = hSyncEnd + info->var.left_margin;
+
+ vSyncStart = info->var.yres + info->var.lower_margin;
+ vSyncEnd = vSyncStart + info->var.vsync_len;
+ vTotal = vSyncEnd + info->var.upper_margin;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ format = UDE_CFG_DST8;
+ break;
+ case 16:
+ format = UDE_CFG_DST16;
+ break;
+ case 24:
+ format = UDE_CFG_DST24;
+ break;
+ case 32:
+ format = UDE_CFG_DST32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ UDE_FSA = unifb_regs[0] = PKUNITY_UNIGFX_MMAP_BASE;
+ UDE_LS = unifb_regs[1] = info->var.yres;
+ UDE_PS = unifb_regs[2] = get_line_length(info->var.xres,
+ info->var.bits_per_pixel) >> 3;
+ /* >> 3 for hardware required. */
+ UDE_HAT = unifb_regs[3] = (hTotal << 16) | (info->var.xres);
+ UDE_HBT = unifb_regs[4] = ((hTotal - 1) << 16) | (info->var.xres - 1);
+ UDE_HST = unifb_regs[5] = ((hSyncEnd - 1) << 16) | (hSyncStart - 1);
+ UDE_VAT = unifb_regs[6] = (vTotal << 16) | (info->var.yres);
+ UDE_VBT = unifb_regs[7] = ((vTotal - 1) << 16) | (info->var.yres - 1);
+ UDE_VST = unifb_regs[8] = ((vSyncEnd - 1) << 16) | (vSyncStart - 1);
+ UDE_CFG = unifb_regs[9] = UDE_CFG_GDEN_ENABLE | UDE_CFG_TIMEUP_ENABLE
+ | format | 0xC0000001;
+
+ return 0;
+}
+
+/*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+static int unifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ if (regno >= 256) /* no. of hw registers */
+ return 1;
+
+ /* grayscale works only partially under directcolor */
+ if (info->var.grayscale) {
+ /* grayscale = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue + (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+#define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16)
+ switch (info->fix.visual) {
+ case FB_VISUAL_TRUECOLOR:
+ case FB_VISUAL_PSEUDOCOLOR:
+ red = CNVT_TOHW(red, info->var.red.length);
+ green = CNVT_TOHW(green, info->var.green.length);
+ blue = CNVT_TOHW(blue, info->var.blue.length);
+ transp = CNVT_TOHW(transp, info->var.transp.length);
+ break;
+ case FB_VISUAL_DIRECTCOLOR:
+ red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
+ green = CNVT_TOHW(green, 8);
+ blue = CNVT_TOHW(blue, 8);
+ /* hey, there is bug in transp handling... */
+ transp = CNVT_TOHW(transp, 8);
+ break;
+ }
+#undef CNVT_TOHW
+ /* Truecolor has hardware independent palette */
+ if (info->fix.visual = FB_VISUAL_TRUECOLOR) {
+ u32 v;
+
+ if (regno >= 16)
+ return 1;
+
+ v = (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset) |
+ (transp << info->var.transp.offset);
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ break;
+ case 16:
+ ((u32 *) (info->pseudo_palette))[regno] = v;
+ break;
+ case 24:
+ case 32:
+ ((u32 *) (info->pseudo_palette))[regno] = v;
+ break;
+ }
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+static int unifb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ if (var->vmode & FB_VMODE_YWRAP) {
+ if (var->yoffset < 0
+ || var->yoffset >= info->var.yres_virtual
+ || var->xoffset)
+ return -EINVAL;
+ } else {
+ if (var->xoffset + var->xres > info->var.xres_virtual ||
+ var->yoffset + var->yres > info->var.yres_virtual)
+ return -EINVAL;
+ }
+ info->var.xoffset = var->xoffset;
+ info->var.yoffset = var->yoffset;
+ if (var->vmode & FB_VMODE_YWRAP)
+ info->var.vmode |= FB_VMODE_YWRAP;
+ else
+ info->var.vmode &= ~FB_VMODE_YWRAP;
+ return 0;
+}
+
+int unifb_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long pos = info->fix.smem_start + offset;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start, pos >> PAGE_SHIFT, size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+
+}
+
+#ifndef MODULE
+/*
+ * The virtual framebuffer driver is only enabled if explicitly
+ * requested by passing 'video=unifb:' (or any actual options).
+ */
+static int __init unifb_setup(char *options)
+{
+ char *this_opt;
+
+ unifb_enable = 0;
+
+ if (!options)
+ return 1;
+
+ unifb_enable = 1;
+
+ if (!*options)
+ return 1;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt)
+ continue;
+ /* Test disable for backwards compatibility */
+ if (!strcmp(this_opt, "disable"))
+ unifb_enable = 0;
+ }
+ return 1;
+}
+#endif /* MODULE */
+
+ /*
+ * Initialisation
+ */
+
+static int unifb_probe(struct platform_device *dev)
+{
+ struct fb_info *info;
+ int retval = -ENOMEM;
+
+ /*
+ * For real video cards we use ioremap.
+ */
+ videomemory = (void *)KUSER_UNIGFX_BASE;
+
+ /*
+ * VFB could clear memory to prevent kernel info
+ * leakage into userspace and set black screen
+ * VGA-based drivers MUST NOT clear memory if
+ * they want to be able to take over vgacon
+ */
+ /* memset(videomemory, 0, videomemorysize); */
+
+ info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
+ if (!info)
+ goto err;
+
+ info->screen_base = (char __iomem *)videomemory;
+ info->fbops = &unifb_ops;
+
+ retval = fb_find_mode(&info->var, info, unifb_option,
+ unifb_modes, 10, &unifb_modes[0], 16);
+
+ if (!retval || (retval = 4))
+ info->var = unifb_default;
+ unifb_fix.smem_start = PKUNITY_UNIGFX_MMAP_BASE;
+ unifb_fix.smem_len = videomemorysize;
+ unifb_fix.mmio_start = PKUNITY_UNIGFX_BASE;
+ info->fix = unifb_fix;
+ info->pseudo_palette = info->par;
+ info->par = NULL;
+ info->flags = FBINFO_FLAG_DEFAULT;
+#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
+ info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
+ info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
+#else
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+#endif
+
+ retval = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (retval < 0)
+ goto err1;
+
+ retval = register_framebuffer(info);
+ if (retval < 0)
+ goto err2;
+ platform_set_drvdata(dev, info);
+
+ printk(KERN_INFO
+ "fb%d: Virtual frame buffer device, using %ldK of video memory\n",
+ info->node, videomemorysize >> 10);
+ return 0;
+err2:
+ fb_dealloc_cmap(&info->cmap);
+err1:
+ framebuffer_release(info);
+err:
+ return retval;
+}
+
+static int unifb_remove(struct platform_device *dev)
+{
+ struct fb_info *info = platform_get_drvdata(dev);
+
+ if (info) {
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int unifb_resume(struct platform_device *dev)
+{
+ int rc = 0;
+
+ if (dev->dev.power.power_state.event = PM_EVENT_ON)
+ return 0;
+
+ acquire_console_sem();
+
+ if (dev->dev.power.power_state.event = PM_EVENT_SUSPEND) {
+ UDE_FSA = unifb_regs[0];
+ UDE_LS = unifb_regs[1];
+ UDE_PS = unifb_regs[2];
+ UDE_HAT = unifb_regs[3];
+ UDE_HBT = unifb_regs[4];
+ UDE_HST = unifb_regs[5];
+ UDE_VAT = unifb_regs[6];
+ UDE_VBT = unifb_regs[7];
+ UDE_VST = unifb_regs[8];
+ UDE_CFG = unifb_regs[9];
+ }
+ dev->dev.power.power_state = PMSG_ON;
+
+ release_console_sem();
+
+ return rc;
+}
+
+static int unifb_suspend(struct platform_device *dev, pm_message_t mesg)
+{
+ if (mesg.event = dev->dev.power.power_state.event)
+ return 0;
+
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE: /* about to take snapshot */
+ case PM_EVENT_PRETHAW: /* before restoring snapshot */
+ goto done;
+ }
+
+ acquire_console_sem();
+
+ /* do nothing... */
+
+ release_console_sem();
+
+done:
+ dev->dev.power.power_state = mesg;
+
+ return 0;
+}
+#else
+#define unifb_resume NULL
+#define unifb_suspend NULL
+#endif
+
+static struct platform_driver unifb_driver = {
+ .probe = unifb_probe,
+ .remove = unifb_remove,
+ .resume = unifb_resume,
+ .suspend = unifb_suspend,
+ .driver = {
+ .name = "unifb",
+ },
+};
+
+static struct platform_device *unifb_device;
+
+static int __init unifb_init(void)
+{
+ int ret = 0;
+
+#ifndef MODULE
+ if (fb_get_options("unifb", &unifb_option))
+ return -ENODEV;
+ unifb_setup(unifb_option);
+#endif
+
+ if (!unifb_enable)
+ return -ENXIO;
+
+ ret = platform_driver_register(&unifb_driver);
+
+ if (!ret) {
+ unifb_device = platform_device_alloc("unifb", 0);
+
+ if (unifb_device)
+ ret = platform_device_add(unifb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(unifb_device);
+ platform_driver_unregister(&unifb_driver);
+ }
+ }
+
+ return ret;
+}
+
+module_init(unifb_init);
+
+#ifdef MODULE
+static void __exit unifb_exit(void)
+{
+ platform_device_unregister(unifb_device);
+ platform_driver_unregister(&unifb_driver);
+}
+
+module_exit(unifb_exit);
+
+MODULE_LICENSE("GPL v2");
+#endif
diff --git a/include/linux/fb.h b/include/linux/fb.h
index d1631d3..4e9da1b 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -152,6 +152,8 @@
#define FB_ACCEL_PROSAVAGE_DDR 0x8d /* S3 ProSavage DDR */
#define FB_ACCEL_PROSAVAGE_DDRK 0x8e /* S3 ProSavage DDR-K */
+#define FB_ACCEL_PUV3_UNIGFX 0xa0 /* PKUnity-v3 Unigfx */
+
struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start; /* Start of frame buffer mem */
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
2011-01-22 10:29 ` Guan Xuetao
@ 2011-01-25 9:02 ` Paul Mundt
-1 siblings, 0 replies; 6+ messages in thread
From: Paul Mundt @ 2011-01-25 9:02 UTC (permalink / raw)
To: Guan Xuetao; +Cc: linux-fbdev, linux-kernel
On Sat, Jan 22, 2011 at 06:29:45PM +0800, Guan Xuetao wrote:
> diff --git a/drivers/video/puv3_fb.c b/drivers/video/puv3_fb.c
> new file mode 100644
> index 0000000..d938a73
> --- /dev/null
> +++ b/drivers/video/puv3_fb.c
[snip]
> +static unsigned long unifb_regs[10];
> +
This is probably something that you will want to move in to platform
data.
> +#define VIDEOMEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
> +
> +static void *videomemory;
> +static u_long videomemorysize = VIDEOMEMSIZE;
> +module_param(videomemorysize, ulong, 0);
> +
Along with these, given that they're not dynamically acquired by the
driver.
> +static int unifb_enable __initdata = 1; /* enable by default */
> +module_param(unifb_enable, bool, 0);
> +
No need for this, you can use fb_get_options() and -ENODEV out in the
init path then simply use the generic "off" parsing done by the fbmem
code.
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> +static void unifb_sync(void)
> +{
> + /* TODO: may, this can be replaced by interrupt */
> + int cnt;
> +
> + for (cnt = 0; cnt < 0x10000000; cnt++) {
> + if (UGE_COMMAND & 0x1000000)
> + return;
> + }
> +
> + if (cnt > 0x8000000)
> + printk(KERN_WARNING "Warning: UniGFX GE time out ...\n");
> +}
> +
In addition to switching to platform data for the I/O offsets, you can
also use dev_warn() here on the device pointer (fb_info->device).
> +static struct fb_ops unifb_ops = {
> + .fb_read = fb_sys_read,
> + .fb_write = fb_sys_write,
> + .fb_check_var = unifb_check_var,
> + .fb_set_par = unifb_set_par,
> + .fb_setcolreg = unifb_setcolreg,
> + .fb_pan_display = unifb_pan_display,
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> + .fb_fillrect = unifb_fillrect,
> + .fb_copyarea = unifb_copyarea,
> + .fb_imageblit = unifb_imageblit,
> +#else
> + .fb_fillrect = sys_fillrect,
> + .fb_copyarea = sys_copyarea,
> + .fb_imageblit = sys_imageblit,
> +#endif
> + .fb_mmap = unifb_mmap,
> +};
> +
There's no need for the ifdef here, all of your accel routines manually
check if acceleration is disabled or not and fall back on the sys_xxx()
paths, so it's safe to always reference them.
> +static int unifb_set_par(struct fb_info *info)
> +{
> + int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
> + int format;
> +
> +#ifdef CONFIG_PUV3_PM
> + struct clk *clk_vga;
> + u32 pixclk = 0;
> + int i;
> +
> + for (i = 0; i <= 10; i++) {
> + if (info->var.xres == unifb_modes[i].xres
> + && info->var.yres == unifb_modes[i].yres
> + && info->var.upper_margin == unifb_modes[i].upper_margin
> + && info->var.lower_margin == unifb_modes[i].lower_margin
> + && info->var.left_margin == unifb_modes[i].left_margin
> + && info->var.right_margin == unifb_modes[i].right_margin
> + && info->var.hsync_len == unifb_modes[i].hsync_len
> + && info->var.vsync_len == unifb_modes[i].vsync_len) {
> + pixclk = unifb_modes[i].pixclock;
> + break;
> + }
> + }
> +
> + /* set clock rate */
> + clk_vga = clk_get(NULL, "VGA_CLK");
> + i = 0;
You can pass info->device to clk_get() instead of NULL. This will make it
easier to match specific clocks and lookups per device in the future.
You'll presumably also want to check if clk_get() returned an error and
handle that accordingly.
> + if (pixclk != 0) {
> + if (clk_set_rate(clk_vga, pixclk) == 0)
> + i = 1;
> + }
> +
> + if (i == 0) { /* set clock failed */
> + info->fix = unifb_fix;
> + info->var = unifb_default;
> + clk_set_rate(clk_vga, unifb_default.pixclock);
> + }
If clk_set_rate() can fail the first time, it can fail the second time
too. You'll want more error handling here.
> + /* Truecolor has hardware independent palette */
> + if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
> + u32 v;
> +
> + if (regno >= 16)
> + return 1;
> +
> + v = (red << info->var.red.offset) |
> + (green << info->var.green.offset) |
> + (blue << info->var.blue.offset) |
> + (transp << info->var.transp.offset);
> + switch (info->var.bits_per_pixel) {
> + case 8:
> + break;
> + case 16:
> + ((u32 *) (info->pseudo_palette))[regno] = v;
> + break;
> + case 24:
> + case 32:
> + ((u32 *) (info->pseudo_palette))[regno] = v;
> + break;
> + }
The 16 case is no different than the 24/32 case.
The default case should also return 1 to indicate an error.
> +#ifndef MODULE
> +/*
> + * The virtual framebuffer driver is only enabled if explicitly
> + * requested by passing 'video=unifb:' (or any actual options).
> + */
> +static int __init unifb_setup(char *options)
> +{
> + char *this_opt;
> +
> + unifb_enable = 0;
> +
> + if (!options)
> + return 1;
> +
> + unifb_enable = 1;
> +
> + if (!*options)
> + return 1;
> +
> + while ((this_opt = strsep(&options, ",")) != NULL) {
> + if (!*this_opt)
> + continue;
> + /* Test disable for backwards compatibility */
> + if (!strcmp(this_opt, "disable"))
> + unifb_enable = 0;
> + }
> + return 1;
> +}
> +#endif /* MODULE */
> +
Just kill all of this and use the generic "off" handling.
> +static int unifb_probe(struct platform_device *dev)
> +{
> + struct fb_info *info;
> + int retval = -ENOMEM;
> +
> + /*
> + * For real video cards we use ioremap.
> + */
> + videomemory = (void *)KUSER_UNIGFX_BASE;
> +
> + /*
> + * VFB could clear memory to prevent kernel info
> + * leakage into userspace and set black screen
> + * VGA-based drivers MUST NOT clear memory if
> + * they want to be able to take over vgacon
> + */
> + /* memset(videomemory, 0, videomemorysize); */
> +
This is a remnant from a vmalloc acquired framebuffer base, so can be
killed off.
> + info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
> + if (!info)
> + goto err;
> +
> + info->screen_base = (char __iomem *)videomemory;
> + unifb_fix.smem_start = PKUNITY_UNIGFX_MMAP_BASE;
> + unifb_fix.smem_len = videomemorysize;
> + unifb_fix.mmio_start = PKUNITY_UNIGFX_BASE;
All of these should be passed in via your platform device resources, with
necessary remapping.
> + info->fix = unifb_fix;
> + info->pseudo_palette = info->par;
> + info->par = NULL;
> + info->flags = FBINFO_FLAG_DEFAULT;
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> + info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
> + info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
> +#else
> + info->flags |= FBINFO_HWACCEL_DISABLED;
> +#endif
> +
The accel flag is deprecated, so just drop it completely.
What happened to FBINFO_HWACCEL_IMAGEBLIT?
> +#ifdef CONFIG_PM
> +static int unifb_resume(struct platform_device *dev)
> +{
> + int rc = 0;
> +
> + if (dev->dev.power.power_state.event == PM_EVENT_ON)
> + return 0;
> +
> + acquire_console_sem();
> +
> + if (dev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
> + UDE_FSA = unifb_regs[0];
> + UDE_LS = unifb_regs[1];
> + UDE_PS = unifb_regs[2];
> + UDE_HAT = unifb_regs[3];
> + UDE_HBT = unifb_regs[4];
> + UDE_HST = unifb_regs[5];
> + UDE_VAT = unifb_regs[6];
> + UDE_VBT = unifb_regs[7];
> + UDE_VST = unifb_regs[8];
> + UDE_CFG = unifb_regs[9];
> + }
This is probably worth abstracting through platform data.
> +
> +static int __init unifb_init(void)
> +{
> + int ret = 0;
> +
> +#ifndef MODULE
> + if (fb_get_options("unifb", &unifb_option))
> + return -ENODEV;
> + unifb_setup(unifb_option);
> +#endif
> +
> + if (!unifb_enable)
> + return -ENXIO;
> +
All of this can just be replaced with:
if (fb_get_options("unifb", NULL))
return -ENODEV;
> + ret = platform_driver_register(&unifb_driver);
> +
> + if (!ret) {
> + unifb_device = platform_device_alloc("unifb", 0);
> +
> + if (unifb_device)
> + ret = platform_device_add(unifb_device);
> + else
> + ret = -ENOMEM;
> +
> + if (ret) {
> + platform_device_put(unifb_device);
> + platform_driver_unregister(&unifb_driver);
> + }
> + }
> +
> + return ret;
> +}
> +
And if your architecture will then register a platform device with the
platform data filled out, all of this can be turned in to just a simple
platform_driver_register().
> +module_init(unifb_init);
> +
> +#ifdef MODULE
> +static void __exit unifb_exit(void)
> +{
> + platform_device_unregister(unifb_device);
> + platform_driver_unregister(&unifb_driver);
> +}
> +
> +module_exit(unifb_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +#endif
No need for the ifdef here, either.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
@ 2011-01-25 9:02 ` Paul Mundt
0 siblings, 0 replies; 6+ messages in thread
From: Paul Mundt @ 2011-01-25 9:02 UTC (permalink / raw)
To: Guan Xuetao; +Cc: linux-fbdev, linux-kernel
On Sat, Jan 22, 2011 at 06:29:45PM +0800, Guan Xuetao wrote:
> diff --git a/drivers/video/puv3_fb.c b/drivers/video/puv3_fb.c
> new file mode 100644
> index 0000000..d938a73
> --- /dev/null
> +++ b/drivers/video/puv3_fb.c
[snip]
> +static unsigned long unifb_regs[10];
> +
This is probably something that you will want to move in to platform
data.
> +#define VIDEOMEMSIZE (SZ_4M) /* 4 MB for 1024*768*32b */
> +
> +static void *videomemory;
> +static u_long videomemorysize = VIDEOMEMSIZE;
> +module_param(videomemorysize, ulong, 0);
> +
Along with these, given that they're not dynamically acquired by the
driver.
> +static int unifb_enable __initdata = 1; /* enable by default */
> +module_param(unifb_enable, bool, 0);
> +
No need for this, you can use fb_get_options() and -ENODEV out in the
init path then simply use the generic "off" parsing done by the fbmem
code.
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> +static void unifb_sync(void)
> +{
> + /* TODO: may, this can be replaced by interrupt */
> + int cnt;
> +
> + for (cnt = 0; cnt < 0x10000000; cnt++) {
> + if (UGE_COMMAND & 0x1000000)
> + return;
> + }
> +
> + if (cnt > 0x8000000)
> + printk(KERN_WARNING "Warning: UniGFX GE time out ...\n");
> +}
> +
In addition to switching to platform data for the I/O offsets, you can
also use dev_warn() here on the device pointer (fb_info->device).
> +static struct fb_ops unifb_ops = {
> + .fb_read = fb_sys_read,
> + .fb_write = fb_sys_write,
> + .fb_check_var = unifb_check_var,
> + .fb_set_par = unifb_set_par,
> + .fb_setcolreg = unifb_setcolreg,
> + .fb_pan_display = unifb_pan_display,
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> + .fb_fillrect = unifb_fillrect,
> + .fb_copyarea = unifb_copyarea,
> + .fb_imageblit = unifb_imageblit,
> +#else
> + .fb_fillrect = sys_fillrect,
> + .fb_copyarea = sys_copyarea,
> + .fb_imageblit = sys_imageblit,
> +#endif
> + .fb_mmap = unifb_mmap,
> +};
> +
There's no need for the ifdef here, all of your accel routines manually
check if acceleration is disabled or not and fall back on the sys_xxx()
paths, so it's safe to always reference them.
> +static int unifb_set_par(struct fb_info *info)
> +{
> + int hTotal, vTotal, hSyncStart, hSyncEnd, vSyncStart, vSyncEnd;
> + int format;
> +
> +#ifdef CONFIG_PUV3_PM
> + struct clk *clk_vga;
> + u32 pixclk = 0;
> + int i;
> +
> + for (i = 0; i <= 10; i++) {
> + if (info->var.xres = unifb_modes[i].xres
> + && info->var.yres = unifb_modes[i].yres
> + && info->var.upper_margin = unifb_modes[i].upper_margin
> + && info->var.lower_margin = unifb_modes[i].lower_margin
> + && info->var.left_margin = unifb_modes[i].left_margin
> + && info->var.right_margin = unifb_modes[i].right_margin
> + && info->var.hsync_len = unifb_modes[i].hsync_len
> + && info->var.vsync_len = unifb_modes[i].vsync_len) {
> + pixclk = unifb_modes[i].pixclock;
> + break;
> + }
> + }
> +
> + /* set clock rate */
> + clk_vga = clk_get(NULL, "VGA_CLK");
> + i = 0;
You can pass info->device to clk_get() instead of NULL. This will make it
easier to match specific clocks and lookups per device in the future.
You'll presumably also want to check if clk_get() returned an error and
handle that accordingly.
> + if (pixclk != 0) {
> + if (clk_set_rate(clk_vga, pixclk) = 0)
> + i = 1;
> + }
> +
> + if (i = 0) { /* set clock failed */
> + info->fix = unifb_fix;
> + info->var = unifb_default;
> + clk_set_rate(clk_vga, unifb_default.pixclock);
> + }
If clk_set_rate() can fail the first time, it can fail the second time
too. You'll want more error handling here.
> + /* Truecolor has hardware independent palette */
> + if (info->fix.visual = FB_VISUAL_TRUECOLOR) {
> + u32 v;
> +
> + if (regno >= 16)
> + return 1;
> +
> + v = (red << info->var.red.offset) |
> + (green << info->var.green.offset) |
> + (blue << info->var.blue.offset) |
> + (transp << info->var.transp.offset);
> + switch (info->var.bits_per_pixel) {
> + case 8:
> + break;
> + case 16:
> + ((u32 *) (info->pseudo_palette))[regno] = v;
> + break;
> + case 24:
> + case 32:
> + ((u32 *) (info->pseudo_palette))[regno] = v;
> + break;
> + }
The 16 case is no different than the 24/32 case.
The default case should also return 1 to indicate an error.
> +#ifndef MODULE
> +/*
> + * The virtual framebuffer driver is only enabled if explicitly
> + * requested by passing 'video=unifb:' (or any actual options).
> + */
> +static int __init unifb_setup(char *options)
> +{
> + char *this_opt;
> +
> + unifb_enable = 0;
> +
> + if (!options)
> + return 1;
> +
> + unifb_enable = 1;
> +
> + if (!*options)
> + return 1;
> +
> + while ((this_opt = strsep(&options, ",")) != NULL) {
> + if (!*this_opt)
> + continue;
> + /* Test disable for backwards compatibility */
> + if (!strcmp(this_opt, "disable"))
> + unifb_enable = 0;
> + }
> + return 1;
> +}
> +#endif /* MODULE */
> +
Just kill all of this and use the generic "off" handling.
> +static int unifb_probe(struct platform_device *dev)
> +{
> + struct fb_info *info;
> + int retval = -ENOMEM;
> +
> + /*
> + * For real video cards we use ioremap.
> + */
> + videomemory = (void *)KUSER_UNIGFX_BASE;
> +
> + /*
> + * VFB could clear memory to prevent kernel info
> + * leakage into userspace and set black screen
> + * VGA-based drivers MUST NOT clear memory if
> + * they want to be able to take over vgacon
> + */
> + /* memset(videomemory, 0, videomemorysize); */
> +
This is a remnant from a vmalloc acquired framebuffer base, so can be
killed off.
> + info = framebuffer_alloc(sizeof(u32)*256, &dev->dev);
> + if (!info)
> + goto err;
> +
> + info->screen_base = (char __iomem *)videomemory;
> + unifb_fix.smem_start = PKUNITY_UNIGFX_MMAP_BASE;
> + unifb_fix.smem_len = videomemorysize;
> + unifb_fix.mmio_start = PKUNITY_UNIGFX_BASE;
All of these should be passed in via your platform device resources, with
necessary remapping.
> + info->fix = unifb_fix;
> + info->pseudo_palette = info->par;
> + info->par = NULL;
> + info->flags = FBINFO_FLAG_DEFAULT;
> +#ifdef CONFIG_PUV3_UNIGFX_HWACCEL
> + info->fix.accel = FB_ACCEL_PUV3_UNIGFX;
> + info->flags |= FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
> +#else
> + info->flags |= FBINFO_HWACCEL_DISABLED;
> +#endif
> +
The accel flag is deprecated, so just drop it completely.
What happened to FBINFO_HWACCEL_IMAGEBLIT?
> +#ifdef CONFIG_PM
> +static int unifb_resume(struct platform_device *dev)
> +{
> + int rc = 0;
> +
> + if (dev->dev.power.power_state.event = PM_EVENT_ON)
> + return 0;
> +
> + acquire_console_sem();
> +
> + if (dev->dev.power.power_state.event = PM_EVENT_SUSPEND) {
> + UDE_FSA = unifb_regs[0];
> + UDE_LS = unifb_regs[1];
> + UDE_PS = unifb_regs[2];
> + UDE_HAT = unifb_regs[3];
> + UDE_HBT = unifb_regs[4];
> + UDE_HST = unifb_regs[5];
> + UDE_VAT = unifb_regs[6];
> + UDE_VBT = unifb_regs[7];
> + UDE_VST = unifb_regs[8];
> + UDE_CFG = unifb_regs[9];
> + }
This is probably worth abstracting through platform data.
> +
> +static int __init unifb_init(void)
> +{
> + int ret = 0;
> +
> +#ifndef MODULE
> + if (fb_get_options("unifb", &unifb_option))
> + return -ENODEV;
> + unifb_setup(unifb_option);
> +#endif
> +
> + if (!unifb_enable)
> + return -ENXIO;
> +
All of this can just be replaced with:
if (fb_get_options("unifb", NULL))
return -ENODEV;
> + ret = platform_driver_register(&unifb_driver);
> +
> + if (!ret) {
> + unifb_device = platform_device_alloc("unifb", 0);
> +
> + if (unifb_device)
> + ret = platform_device_add(unifb_device);
> + else
> + ret = -ENOMEM;
> +
> + if (ret) {
> + platform_device_put(unifb_device);
> + platform_driver_unregister(&unifb_driver);
> + }
> + }
> +
> + return ret;
> +}
> +
And if your architecture will then register a platform device with the
platform data filled out, all of this can be turned in to just a simple
platform_driver_register().
> +module_init(unifb_init);
> +
> +#ifdef MODULE
> +static void __exit unifb_exit(void)
> +{
> + platform_device_unregister(unifb_device);
> + platform_driver_unregister(&unifb_driver);
> +}
> +
> +module_exit(unifb_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +#endif
No need for the ifdef here, either.
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
2011-01-22 10:29 ` Guan Xuetao
@ 2011-01-27 6:37 ` Guan Xuetao
-1 siblings, 0 replies; 6+ messages in thread
From: Guan Xuetao @ 2011-01-27 6:37 UTC (permalink / raw)
To: Paul Mundt; +Cc: linux-kernel, linux-fbdev
Paul:
All your advice accepted, please see my patch v2.
Thanks.
Guan Xuetao
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC
@ 2011-01-27 6:37 ` Guan Xuetao
0 siblings, 0 replies; 6+ messages in thread
From: Guan Xuetao @ 2011-01-27 6:37 UTC (permalink / raw)
To: Paul Mundt; +Cc: linux-kernel, linux-fbdev
Paul:
All your advice accepted, please see my patch v2.
Thanks.
Guan Xuetao
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2011-01-27 6:53 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-27 6:37 [PATCH] unicore32: add framebuffer driver for unigfx engine in PKUnity SoC Guan Xuetao
2011-01-27 6:37 ` Guan Xuetao
-- strict thread matches above, loose matches on Subject: below --
2011-01-22 10:29 Guan Xuetao
2011-01-22 10:29 ` Guan Xuetao
2011-01-25 9:02 ` Paul Mundt
2011-01-25 9:02 ` Paul Mundt
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.