From: Ladislav M. <la...@li...> - 2004-10-28 20:10:47
|
Hi, I've playing with idea to implement faked framebufer driver fox SGI Newport graphics based on REX3 engine (documentation is here: ftp://ftp.linux-mips.org/pub/linux/mips/doc/indy/) and fbcon changes accepted to 2.6.10-rc1 kicked me to write this post :-) Now stop reading and see patch below (Note: I didn't test it and even tried to compile. It worked with old interface and few tweaks in fbcon, but I do not want to spent too much time before some consensus is reached)... See drivers/video/console/newport_con.c for putcs implementation. Some video devices act as write-only command pipe. There is no memory you can read from, there is even no mapable memory at all. However they still can utilize fbdev interface to change resolution and for DRM and fbcon to handle fonts settings. Currently there are two bliting modes: bitblit and tileblit. Extending struct fbcon_ops with logo drawing op and providing way to register blit operations to fb driver itself would allow fb_fillrect, fb_copyarea, fb_imageblit and fb_cursor become optional, making wider range of hardware happy. Also having flag for supported font widths would be nice, because REX3 cannot handle drawing 1bpp images other that 8*x width efectively (zpattern line is misused for that). These modifications would make supporting SGI graphics options easier, providing way to change resolution in defined way. Comments and ideas are more that welcome. Thanks, ladis Index: drivers/video/Kconfig =================================================================== RCS file: /home/cvs/linux/drivers/video/Kconfig,v retrieving revision 1.29 diff -u -r1.29 Kconfig --- drivers/video/Kconfig 25 Oct 2004 20:44:39 -0000 1.29 +++ drivers/video/Kconfig 28 Oct 2004 17:43:00 -0000 @@ -373,6 +373,14 @@ help SGI Visual Workstation support for framebuffer graphics. +config FB_NEWPORT + bool "SGI Newport" + depends on FB && SGI_IP22 + help + This is the fake frame buffer device driver for SGI Newport + graphics. Since Newport has no mapable video memory, this driver + is good only to run console on the top. + config FB_GBE bool "SGI Graphics Backend frame buffer support" depends on FB && (SGI_IP32 || X86_VISWS) Index: drivers/video/Makefile =================================================================== RCS file: /home/cvs/linux/drivers/video/Makefile,v retrieving revision 1.84 diff -u -r1.84 Makefile --- drivers/video/Makefile 12 Oct 2004 01:45:47 -0000 1.84 +++ drivers/video/Makefile 28 Oct 2004 17:43:01 -0000 @@ -90,7 +90,7 @@ obj-$(CONFIG_FB_MAXINE) += maxinefb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_TX3912) += tx3912fb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_AU1100) += au1100fb.o fbgen.o - +obj-$(CONFIG_FB_NEWPORT) += newportfb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o Index: drivers/video/console/fbcon.c =================================================================== RCS file: /home/cvs/linux/drivers/video/console/fbcon.c,v retrieving revision 1.20 diff -u -r1.20 fbcon.c --- drivers/video/console/fbcon.c 25 Oct 2004 20:44:39 -0000 1.20 +++ drivers/video/console/fbcon.c 28 Oct 2004 17:43:31 -0000 @@ -456,6 +456,7 @@ } } +#if 0 #ifdef CONFIG_FB_TILEBLITTING static void set_blitting_type(struct vc_data *vc, struct fb_info *info, struct display *p) @@ -477,6 +478,14 @@ fbcon_set_bitops(ops); } #endif /* CONFIG_MISC_TILEBLITTING */ +#endif +static void set_blitting_type(struct vc_data *vc, struct fb_info *info, + struct display *p) +{ + struct fbcon_ops *ops = (struct fbcon_ops *) info->fbcon_par; + + newport_set_bitops(ops); +} /** * set_con2fb_map - map console to frame buffer device Binary files /dev/zero and drivers/video/newportfb.c differ --- /dev/null 2004-10-28 12:38:40.000000000 +0200 +++ drivers/video/newportfb.c 2004-10-28 14:14:13.000000000 +0200 @@ -0,0 +1,534 @@ +/* + * SGI Newport XL fake framebuffer driver + * + * Derived from drivers/video/console/newport_con.c + * + * Copyright (C) 2004 Ladislav Michl <la...@li...> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> + +#include <video/newport.h> + +extern unsigned long sgi_gfxaddr; + +/* default mode */ +static struct fb_var_screeninfo default_var_high __initdata = { + /* 1280x1024, 8bpp */ + .xres = 1280, + .yres = 1024, + .xres_virtual = 1280, + .yres_virtual = 1024, + .xoffset = 0, + .yoffset = 0, + .bits_per_pixel = 8, + .grayscale = 0, + .red = { 0, 8, 0 }, + .green = { 0, 8, 0 }, + .blue = { 0, 8, 0 }, + .transp = { 0, 0, 0 }, + .nonstd = 0, + .activate = 0, + .height = -1, + .width = -1, + .accel_flags = 0, + .pixclock = 9353, + .left_margin = 20, + .right_margin = 30, + .upper_margin = 37, + .lower_margin = 3, + .hsync_len = 20, + .vsync_len = 3, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED +}; + +static struct fb_videomode default_mode_high __initdata = { + /* 1280x1024, 8bpp */ + .xres = 1280, + .yres = 1024, + .pixclock = 9353, + .left_margin = 20, + .right_margin = 30, + .upper_margin = 37, + .lower_margin = 3, + .hsync_len = 20, + .vsync_len = 3, + .vmode = FB_VMODE_NONINTERLACED, +}; + +struct fb_videomode *default_mode = &default_mode_high; +struct fb_var_screeninfo *default_var = &default_var_high; + +struct newportfb_par { + struct newport_regs *npregs; + struct fb_var_screeninfo var; + int valid; +}; + +static unsigned int pseudo_palette[256]; + +static int newportfb_blank(int blank, struct fb_info *info) +{ + unsigned short treg; + struct newportfb_par *par = info->par; + struct newport_regs *npregs = par->npregs; + + /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ + switch (blank) { + case 0: /* unblank */ + treg = newport_vc2_get(npregs, VC2_IREG_CONTROL); + newport_vc2_set(npregs, VC2_IREG_CONTROL, + (treg | VC2_CTRL_EDISP)); + break; + + case 1: /* blank */ + treg = newport_vc2_get(npregs, VC2_IREG_CONTROL); + newport_vc2_set(npregs, VC2_IREG_CONTROL, + (treg & ~(VC2_CTRL_EDISP))); + break; + + default: + /* Nothing */ + break; + } + return 0; +} + +/* + * Set the hardware according to 'par'. + */ +static int newportfb_set_par(struct fb_info *info) +{ + return 0; +} + +static void newportfb_encode_fix(struct fb_fix_screeninfo *fix, + struct fb_var_screeninfo *var) +{ + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "SGI Newport"); + fix->smem_start = 0; + fix->smem_len = 0; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->type_aux = 0; + fix->accel = FB_ACCEL_NONE; + switch (var->bits_per_pixel) { + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + default: + fix->visual = FB_VISUAL_TRUECOLOR; + break; + } + fix->ywrapstep = 0; + fix->xpanstep = 0; + fix->ypanstep = 0; + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + fix->mmio_start = sgi_gfxaddr; + fix->mmio_len = sizeof(struct newport_regs); +} + +/* + * Check video mode validity, eventually modify var to best match. + */ +static int newportfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return 0; +} + +void newport_reset(struct newport_regs *npregs) +{ + unsigned short treg; + int i; + + newport_wait(npregs); + treg = newport_vc2_get(npregs, VC2_IREG_CONTROL); + newport_vc2_set(npregs, VC2_IREG_CONTROL, + (treg | VC2_CTRL_EVIDEO)); + + treg = newport_vc2_get(npregs, VC2_IREG_CENTRY); + newport_vc2_set(npregs, VC2_IREG_RADDR, treg); + npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM | + NPORT_DMODE_W2 | VC2_PROTOCOL); + for (i = 0; i < 128; i++) { + newport_bfwait(npregs); + if (i == 92 || i == 94) + npregs->set.dcbdata0.byshort.s1 = 0xff00; + else + npregs->set.dcbdata0.byshort.s1 = 0x0000; + } + + /* turn off popup plane */ + npregs->set.dcbmode = (DCB_XMAP0 | R_DCB_XMAP9_PROTOCOL | + XM9_CRS_CONFIG | NPORT_DMODE_W1); + npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE; + npregs->set.dcbmode = (DCB_XMAP1 | R_DCB_XMAP9_PROTOCOL | + XM9_CRS_CONFIG | NPORT_DMODE_W1); + npregs->set.dcbdata0.bybytes.b3 &= ~XM9_PUPMODE; + + npregs->cset.topscan = 0x3ff; + npregs->cset.xywin = (4096 << 16) | 4096; +} + +static inline void newport_fillrect(struct newport_regs *npregs, + int x1, int y1, int x2, int y2, int ci) +{ + newport_wait(npregs); + npregs->set.wrmask = 0xffffffff; + npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK | + NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX | + NPORT_DMODE0_STOPY); + npregs->set.colori = ci; + npregs->set.xystarti = (x1 << 16) | y1; + npregs->go.xyendi = (x2 << 16) | y2; +} + +static inline void newport_copyrect(struct newport_regs *npregs, + int x1, int y1, int x2, int y2, + int w, int h) +{ + newport_wait(npregs); + npregs->set.drawmode0 = (NPORT_DMODE0_S2S | NPORT_DMODE0_BLOCK | + NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX | + NPORT_DMODE0_STOPY); + npregs->set.xystarti = (x1 << 16) | y1; + npregs->set.xyendi = (x2 << 16) | y2; + npregs->go.xymove = ((w - x1) << 16) | (h - y1); +} + +/* + * Draw 8 pixel width 1bpp image (character) + */ +static void newport_imageblit_1bpp_8(struct newport_regs *npregs, + unsigned char *src, int x, int y, + int h, int fg, int bg) +{ + int x2 = x + 8 - 1; + int y2 = y + h - 1; + + /* Fill backgroung */ + newport_fillrect(npregs, x, y, x2, y2, bg); + + /* Set the color and drawing mode. */ + newport_wait(npregs); + npregs->set.colori = fg; + npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK | + NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB | + NPORT_DMODE0_L32); + /* Set coordinates for bitmap operation. */ + npregs->set.xystarti = (x << 16) | y; + npregs->set.xyendi = (x2 << 16) | y2; + newport_wait(npregs); + + /* Go, baby, go... */ + while (h--) + npregs->go.zpattern = *src++ << 24; + +} +static void newport_imageblit_8bpp(struct newport_regs *npregs, + unsigned char *src, int x, int y, + int w, int h) +{ + int x2, y2, dx, dy; + uint32_t *img = (uint32_t *) src; + + newport_wait(npregs); + + npregs->set.drawmode1 = DM1_RGBPLANES | + NPORT_DMODE1_CCLT | + NPORT_DMODE1_CCEQ | + NPORT_DMODE1_CCGT | + NPORT_DMODE1_LOSRC | + NPORT_DMODE1_DD8 | + NPORT_DMODE1_HD8 | + NPORT_DMODE1_RWPCKD; + + npregs->set.drawmode0 = NPORT_DMODE0_DRAW | + NPORT_DMODE0_BLOCK | + NPORT_DMODE0_CHOST; + x2 = x + w; + y2 = y + h; + npregs->set.xystarti = (x << 16) | y; + npregs->set.xyendi = ((x2 - 1) << 16) | (y2 - 1); + + for (dy = y; dy < y2; dy++) { + for (dx = x; dx < x2; dx += 4) { + npregs->go.hostrw0 = *img; + img++; + } + } +} + +static int newportfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *p) +{ + struct newportfb_par *par = p->par; + struct newport_regs *npregs = par->npregs; + + newport_cmap_setaddr(npregs, regno); + newport_cmap_setrgb(npregs, red, green, blue); + + return 0; +} + +static void newportfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) +{ +} + +static void newportfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) +{ +} + +static void newportfb_imageblit(struct fb_info *p, const struct fb_image *image) +{ +} + +/* + * Newport has no mapable memory + */ +static ssize_t newportfb_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t newportfb_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static int newportfb_mmap(struct fb_info *info, struct file *file, + struct vm_area_struct *vma) +{ + return -EINVAL; +} + +static void newport_op_bmove(struct vc_data *vc, struct fb_info *info, int sy, + int sx, int dy, int dx, int height, int width) +{ + struct newportfb_par *par = info->par; + struct newport_regs *npregs = par->npregs; + int x1 = sx << 3; + int y1 = sy * vc->vc_font.height; + int x2 = dx << 3; + int y2 = dy * vc->vc_font.height; + int w = width << 3; + int h = height * vc->vc_font.height; + newport_copyrect(npregs, x1, y1, x2, y2, w, h); +} + +static void newport_op_clear(struct vc_data *vc, struct fb_info *info, + int sy, int sx, int height, int width) +{ + struct newportfb_par *par = info->par; + struct newport_regs *npregs = par->npregs; + int x1 = sx << 3; + int y1 = sy * vc->vc_font.height; + int x2 = (sx + width) << 3; + int y2 = (sy + height) * vc->vc_font.height; + /* FIXME: Use correct background color */ + newport_fillrect(npregs, x1, y1, x2, y2, 0); +} + +static void newport_op_putcs(struct vc_data *vc, struct fb_info *info, + const unsigned short *s, int count, int yy, int xx, + int fg, int bg) +{ + struct newportfb_par *par = info->par; + struct newport_regs *npregs = par->npregs; + +} + +static void newport_op_clear_margins(struct vc_data *vc, struct fb_info *info, + int bottom_only) +{ + return; +} + +static void newport_op_cursor(struct vc_data *vc, struct fb_info *info, + struct display *p, int mode, int fg, int bg) +{ +} + +void newport_set_ops(struct vc_data *vc, struct fb_info *info, + struct display *p, struct fbcon_ops *ops) +{ + ops->bmove = newport_op_bmove; + ops->clear = newport_op_clear; + ops->putcs = newport_op_putcs; + ops->clear_margins = newport_op_clear_margins; + ops->cursor = newport_op_cursor; +} + +EXPORT_SYMBOL(newport_set_ops); + +static struct fb_ops newportfb_ops = { + .owner = THIS_MODULE, + .fb_read = newportfb_read, + .fb_write = newportfb_write, + .fb_check_var = newportfb_check_var, + .fb_set_par = newportfb_set_par, + .fb_setcolreg = newportfb_setcolreg, + .fb_blank = newportfb_blank, + .fb_fillrect = newportfb_fillrect, + .fb_copyarea = newportfb_copyarea, + .fb_imageblit = newportfb_imageblit, + .fb_cursor = newportfb_cursor, + .fb_mmap = newportfb_mmap, +}; + +int __init newportfb_setup(char *options) +{ + return 0; +} + +#define TESTVAL 0xdeadbeef +#define XSTI_TO_FXSTART(val) (((val) & 0xffff) << 11) + +static int __init newportfb_probe(struct device *dev) +{ + int ret; + struct fb_info *info; + struct newportfb_par *par; + struct newport_regs *npregs; + struct platform_device *p_dev = to_platform_device(dev); + + if (!sgi_gfxaddr) + return -ENODEV; + + info = framebuffer_alloc(sizeof(struct newportfb_par), &p_dev->dev); + if (!info) + return -ENOMEM; + + if (!request_mem_region(sgi_gfxaddr, sizeof(struct newport_regs), + "Newport")) { + printk(KERN_ERR "newportfb: couldn't reserve mmio region\n"); + ret = -EBUSY; + goto out_free_fbinfo; + } + + npregs = (struct newport_regs*) + ioremap(sgi_gfxaddr, sizeof(struct newport_regs)); + if (!npregs) { + printk(KERN_ERR "newportfb: couldn't map mmio region\n"); + ret = -ENXIO; + goto out_release_mem_region; + } + + npregs->cset.config = NPORT_CFG_GD0; + if (newport_wait(npregs)) { + ret = -ENODEV; + goto out_unmap; + } + + npregs->set.xstarti = TESTVAL; + if (npregs->set._xstart.word != XSTI_TO_FXSTART(TESTVAL)) { + ret = -ENODEV; + goto out_unmap; + } + + newport_reset(npregs); +/* + newport_get_revisions(); + newport_get_screensize(); +*/ + par = info->par; + par->npregs = npregs; + + info->currcon = -1; + info->fbops = &newportfb_ops; + info->pseudo_palette = pseudo_palette; + info->flags = FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_IMAGEBLIT; + fb_alloc_cmap(&info->cmap, 256, 0); + + /* turn on default video mode */ +// if (fb_find_mode(&par->var, info, NULL, NULL, 0, +// default_mode, 8) == 0) + par->var = *default_var; + info->var = par->var; + newportfb_check_var(&par->var, info); + newportfb_encode_fix(&info->fix, &info->var); + + if (register_framebuffer(info) < 0) { + ret = -ENXIO; + printk(KERN_ERR "newportfb: couldn't register framebuffer\n"); + goto out_unmap; + } + + dev_set_drvdata(&p_dev->dev, info); + + return 0; + +out_unmap: + iounmap((void *)npregs); +out_release_mem_region: + release_mem_region(sgi_gfxaddr, sizeof(struct newport_regs)); +out_free_fbinfo: + framebuffer_release(info); + return ret; +} + +static int newportfb_remove(struct device* dev) +{ + struct platform_device *p_dev = to_platform_device(dev); + struct fb_info *info = dev_get_drvdata(&p_dev->dev); + struct newportfb_par *par = info->par; + + unregister_framebuffer(info); + framebuffer_release(info); + iounmap((void *)par->npregs); + release_mem_region(sgi_gfxaddr, sizeof(struct newport_regs)); + + return 0; +} + +static struct device_driver newportfb_driver = { + .name = "newportfb", + .bus = &platform_bus_type, + .probe = newportfb_probe, + .remove = newportfb_remove, +}; + +static struct platform_device newportfb_device = { + .name = "newportfb", +}; + +int __init newportfb_init(void) +{ + int ret = driver_register(&newportfb_driver); + if (!ret) { + ret = platform_device_register(&newportfb_device); + if (ret) + driver_unregister(&newportfb_driver); + } + return ret; +} + +void __exit newportfb_exit(void) +{ + driver_unregister(&newportfb_driver); +} + +module_init(newportfb_init); +module_exit(newportfb_exit); + +MODULE_AUTHOR("Ladislav Michl <la...@li...>"); +MODULE_DESCRIPTION("Newport fake framebuffer"); +MODULE_LICENSE("GPL"); |