diff options
Diffstat (limited to 'drivers/video/sm501fb.c')
-rw-r--r-- | drivers/video/sm501fb.c | 249 |
1 files changed, 230 insertions, 19 deletions
diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 924d7946278..35370d0ecf0 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -29,8 +29,8 @@ #include <linux/platform_device.h> #include <linux/clk.h> #include <linux/console.h> +#include <linux/io.h> -#include <asm/io.h> #include <asm/uaccess.h> #include <asm/div64.h> @@ -66,6 +66,7 @@ struct sm501fb_info { struct fb_info *fb[2]; /* fb info for both heads */ struct resource *fbmem_res; /* framebuffer resource */ struct resource *regs_res; /* registers resource */ + struct resource *regs2d_res; /* 2d registers resource */ struct sm501_platdata_fb *pdata; /* our platform data */ unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ @@ -73,6 +74,7 @@ struct sm501fb_info { int irq; int swap_endian; /* set to swap rgb=>bgr */ void __iomem *regs; /* remapped registers */ + void __iomem *regs2d; /* 2d remapped registers */ void __iomem *fbmem; /* remapped framebuffer */ size_t fbmem_len; /* length of remapped region */ }; @@ -123,9 +125,9 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info) * This is an attempt to lay out memory for the two framebuffers and * everything else * - * |fbmem_res->start fbmem_res->end| - * | | - * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | + * |fbmem_res->start fbmem_res->end| + * | | + * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| * * The "spare" space is for the 2d engine data @@ -1246,7 +1248,173 @@ static ssize_t sm501fb_debug_show_pnl(struct device *dev, static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); -/* framebuffer ops */ +/* acceleration operations */ +static int sm501fb_sync(struct fb_info *info) +{ + int count = 1000000; + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + + /* wait for the 2d engine to be ready */ + while ((count > 0) && + (readl(fbi->regs + SM501_SYSTEM_CONTROL) & + SM501_SYSCTRL_2D_ENGINE_STATUS) != 0) + count--; + + if (count <= 0) { + dev_err(info->dev, "Timeout waiting for 2d engine sync\n"); + return 1; + } + return 0; +} + +static void sm501fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + int width = area->width; + int height = area->height; + int sx = area->sx; + int sy = area->sy; + int dx = area->dx; + int dy = area->dy; + unsigned long rtl = 0; + + /* source clip */ + if ((sx >= info->var.xres_virtual) || + (sy >= info->var.yres_virtual)) + /* source Area not within virtual screen, skipping */ + return; + if ((sx + width) >= info->var.xres_virtual) + width = info->var.xres_virtual - sx - 1; + if ((sy + height) >= info->var.yres_virtual) + height = info->var.yres_virtual - sy - 1; + + /* dest clip */ + if ((dx >= info->var.xres_virtual) || + (dy >= info->var.yres_virtual)) + /* Destination Area not within virtual screen, skipping */ + return; + if ((dx + width) >= info->var.xres_virtual) + width = info->var.xres_virtual - dx - 1; + if ((dy + height) >= info->var.yres_virtual) + height = info->var.yres_virtual - dy - 1; + + if ((sx < dx) || (sy < dy)) { + rtl = 1 << 27; + sx += width - 1; + dx += width - 1; + sy += height - 1; + dy += height - 1; + } + + if (sm501fb_sync(info)) + return; + + /* set the base addresses */ + writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); + writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE); + + /* set the window width */ + writel((info->var.xres << 16) | info->var.xres, + fbi->regs2d + SM501_2D_WINDOW_WIDTH); + + /* set window stride */ + writel((info->var.xres_virtual << 16) | info->var.xres_virtual, + fbi->regs2d + SM501_2D_PITCH); + + /* set data format */ + switch (info->var.bits_per_pixel) { + case 8: + writel(0, fbi->regs2d + SM501_2D_STRETCH); + break; + case 16: + writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); + break; + case 32: + writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); + break; + } + + /* 2d compare mask */ + writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); + + /* 2d mask */ + writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); + + /* source and destination x y */ + writel((sx << 16) | sy, fbi->regs2d + SM501_2D_SOURCE); + writel((dx << 16) | dy, fbi->regs2d + SM501_2D_DESTINATION); + + /* w/h */ + writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); + + /* do area move */ + writel(0x800000cc | rtl, fbi->regs2d + SM501_2D_CONTROL); +} + +static void sm501fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + int width = rect->width, height = rect->height; + + if ((rect->dx >= info->var.xres_virtual) || + (rect->dy >= info->var.yres_virtual)) + /* Rectangle not within virtual screen, skipping */ + return; + if ((rect->dx + width) >= info->var.xres_virtual) + width = info->var.xres_virtual - rect->dx - 1; + if ((rect->dy + height) >= info->var.yres_virtual) + height = info->var.yres_virtual - rect->dy - 1; + + if (sm501fb_sync(info)) + return; + + /* set the base addresses */ + writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_SOURCE_BASE); + writel(par->screen.sm_addr, fbi->regs2d + SM501_2D_DESTINATION_BASE); + + /* set the window width */ + writel((info->var.xres << 16) | info->var.xres, + fbi->regs2d + SM501_2D_WINDOW_WIDTH); + + /* set window stride */ + writel((info->var.xres_virtual << 16) | info->var.xres_virtual, + fbi->regs2d + SM501_2D_PITCH); + + /* set data format */ + switch (info->var.bits_per_pixel) { + case 8: + writel(0, fbi->regs2d + SM501_2D_STRETCH); + break; + case 16: + writel(0x00100000, fbi->regs2d + SM501_2D_STRETCH); + break; + case 32: + writel(0x00200000, fbi->regs2d + SM501_2D_STRETCH); + break; + } + + /* 2d compare mask */ + writel(0xffffffff, fbi->regs2d + SM501_2D_COLOR_COMPARE_MASK); + + /* 2d mask */ + writel(0xffffffff, fbi->regs2d + SM501_2D_MASK); + + /* colour */ + writel(rect->color, fbi->regs2d + SM501_2D_FOREGROUND); + + /* x y */ + writel((rect->dx << 16) | rect->dy, fbi->regs2d + SM501_2D_DESTINATION); + + /* w/h */ + writel((width << 16) | height, fbi->regs2d + SM501_2D_DIMENSION); + + /* do rectangle fill */ + writel(0x800100cc, fbi->regs2d + SM501_2D_CONTROL); +} + static struct fb_ops sm501fb_ops_crt = { .owner = THIS_MODULE, @@ -1256,9 +1424,10 @@ static struct fb_ops sm501fb_ops_crt = { .fb_setcolreg = sm501fb_setcolreg, .fb_pan_display = sm501fb_pan_crt, .fb_cursor = sm501fb_cursor, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, + .fb_fillrect = sm501fb_fillrect, + .fb_copyarea = sm501fb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_sync = sm501fb_sync, }; static struct fb_ops sm501fb_ops_pnl = { @@ -1269,9 +1438,10 @@ static struct fb_ops sm501fb_ops_pnl = { .fb_blank = sm501fb_blank_pnl, .fb_setcolreg = sm501fb_setcolreg, .fb_cursor = sm501fb_cursor, - .fb_fillrect = cfb_fillrect, - .fb_copyarea = cfb_copyarea, + .fb_fillrect = sm501fb_fillrect, + .fb_copyarea = sm501fb_copyarea, .fb_imageblit = cfb_imageblit, + .fb_sync = sm501fb_sync, }; /* sm501_init_cursor @@ -1329,7 +1499,8 @@ static int sm501fb_start(struct sm501fb_info *info, dev_warn(dev, "no irq for device\n"); } - /* allocate, reserve and remap resources for registers */ + /* allocate, reserve and remap resources for display + * controller registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(dev, "no resource definition for registers\n"); @@ -1338,7 +1509,7 @@ static int sm501fb_start(struct sm501fb_info *info, } info->regs_res = request_mem_region(res->start, - res->end - res->start, + resource_size(res), pdev->name); if (info->regs_res == NULL) { @@ -1347,37 +1518,63 @@ static int sm501fb_start(struct sm501fb_info *info, goto err_release; } - info->regs = ioremap(res->start, (res->end - res->start)+1); + info->regs = ioremap(res->start, resource_size(res)); if (info->regs == NULL) { dev_err(dev, "cannot remap registers\n"); ret = -ENXIO; goto err_regs_res; } + /* allocate, reserve and remap resources for 2d + * controller registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res == NULL) { + dev_err(dev, "no resource definition for 2d registers\n"); + ret = -ENOENT; + goto err_regs_map; + } + + info->regs2d_res = request_mem_region(res->start, + resource_size(res), + pdev->name); + + if (info->regs2d_res == NULL) { + dev_err(dev, "cannot claim registers\n"); + ret = -ENXIO; + goto err_regs_map; + } + + info->regs2d = ioremap(res->start, resource_size(res)); + if (info->regs2d == NULL) { + dev_err(dev, "cannot remap registers\n"); + ret = -ENXIO; + goto err_regs2d_res; + } + /* allocate, reserve resources for framebuffer */ res = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (res == NULL) { dev_err(dev, "no memory resource defined\n"); ret = -ENXIO; - goto err_regs_map; + goto err_regs2d_map; } info->fbmem_res = request_mem_region(res->start, - (res->end - res->start)+1, + resource_size(res), pdev->name); if (info->fbmem_res == NULL) { dev_err(dev, "cannot claim framebuffer\n"); ret = -ENXIO; - goto err_regs_map; + goto err_regs2d_map; } - info->fbmem = ioremap(res->start, (res->end - res->start)+1); + info->fbmem = ioremap(res->start, resource_size(res)); if (info->fbmem == NULL) { dev_err(dev, "cannot remap framebuffer\n"); goto err_mem_res; } - info->fbmem_len = (res->end - res->start)+1; + info->fbmem_len = resource_size(res); /* clear framebuffer memory - avoids garbage data on unused fb */ memset(info->fbmem, 0, info->fbmem_len); @@ -1389,8 +1586,10 @@ static int sm501fb_start(struct sm501fb_info *info, /* enable display controller */ sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); - /* setup cursors */ + /* enable 2d controller */ + sm501_unit_power(dev->parent, SM501_GATE_2D_ENGINE, 1); + /* setup cursors */ sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); @@ -1400,6 +1599,13 @@ static int sm501fb_start(struct sm501fb_info *info, release_resource(info->fbmem_res); kfree(info->fbmem_res); + err_regs2d_map: + iounmap(info->regs2d); + + err_regs2d_res: + release_resource(info->regs2d_res); + kfree(info->regs2d_res); + err_regs_map: iounmap(info->regs); @@ -1420,6 +1626,10 @@ static void sm501fb_stop(struct sm501fb_info *info) release_resource(info->fbmem_res); kfree(info->fbmem_res); + iounmap(info->regs2d); + release_resource(info->regs2d_res); + kfree(info->regs2d_res); + iounmap(info->regs); release_resource(info->regs_res); kfree(info->regs_res); @@ -1486,7 +1696,8 @@ static int sm501fb_init_fb(struct fb_info *fb, par->ops.fb_cursor = NULL; fb->fbops = &par->ops; - fb->flags = FBINFO_FLAG_DEFAULT | + fb->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST | + FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; /* fixed data */ |