diff options
author | Thomas Bogendoerfer <tsbogend@alpha.franken.de> | 1998-08-10 19:27:16 +0000 |
---|---|---|
committer | Thomas Bogendoerfer <tsbogend@alpha.franken.de> | 1998-08-10 19:27:16 +0000 |
commit | 71fb035abc99eb512c16dfac46d4db53fd186b25 (patch) | |
tree | 5dd148d217abb513f10d0c37ce3ae9efb87120b4 /drivers/video | |
parent | e74f7548843424936bab3b8b3fe14f6a14c86ab3 (diff) |
framebuffer driver for G364 based graphiccards
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/g364fb.c | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/drivers/video/g364fb.c b/drivers/video/g364fb.c new file mode 100644 index 000000000..56939ef61 --- /dev/null +++ b/drivers/video/g364fb.c @@ -0,0 +1,514 @@ +/* + * linux/drivers/video/g364fb.c -- Mips Magnum frame buffer device + * + * (C) 1998 Thomas Bogendoerfer + * + * This driver is based on tgafb.c + * + * Copyright (C) 1997 Geert Uytterhoeven + * Copyright (C) 1995 Jay Estabrook + * + * 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/config.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/selection.h> +#include <linux/console.h> +#include <asm/io.h> +#include <asm/jazz.h> + +#include "fbcon.h" +#include "fbcon-cfb8.h" + +/* + * Various defines for the G364 + */ +#define G364_MEM_BASE 0xe4400000 +#define G364_PORT_BASE 0xe4000000 +#define ID_REG 0xe4000000 /* Read only */ +#define BOOT_REG 0xe4080000 +#define TIMING_REG 0xe4080108 /* to 0x080170 - DON'T TOUCH! */ +#define DISPLAY_REG 0xe4080118 +#define VDISPLAY_REG 0xe4080150 +#define MASK_REG 0xe4080200 +#define CTLA_REG 0xe4080300 +#define CURS_TOGGLE 0x800000 +#define BIT_PER_PIX 0x700000 /* bits 22 to 20 of Control A */ +#define DELAY_SAMPLE 0x080000 +#define PORT_INTER 0x040000 +#define PIX_PIPE_DEL 0x030000 /* bits 17 and 16 of Control A */ +#define PIX_PIPE_DEL2 0x008000 /* same as above - don't ask me why */ +#define TR_CYCLE_TOG 0x004000 +#define VRAM_ADR_INC 0x003000 /* bits 13 and 12 of Control A */ +#define BLANK_OFF 0x000800 +#define FORCE_BLANK 0x000400 +#define BLK_FUN_SWTCH 0x000200 +#define BLANK_IO 0x000100 +#define BLANK_LEVEL 0x000080 +#define A_VID_FORM 0x000040 +#define D_SYNC_FORM 0x000020 +#define FRAME_FLY_PAT 0x000010 +#define OP_MODE 0x000008 +#define INTL_STAND 0x000004 +#define SCRN_FORM 0x000002 +#define ENABLE_VTG 0x000001 +#define TOP_REG 0xe4080400 +#define CURS_PAL_REG 0xe4080508 /* to 0x080518 */ +#define CHKSUM_REG 0xe4080600 /* to 0x080610 - unused */ +#define CURS_POS_REG 0xe4080638 +#define CLR_PAL_REG 0xe4080800 /* to 0x080ff8 */ +#define CURS_PAT_REG 0xe4081000 /* to 0x081ff8 */ +#define MON_ID_REG 0xe4100000 /* unused */ +#define RESET_REG 0xe4180000 /* Write only */ + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + +static int currcon = 0; +static struct display disp; +static struct fb_info fb_info; +static struct { u_char red, green, blue, pad; } palette[256]; + +static struct fb_fix_screeninfo fb_fix = { { "G364 8plane", } }; +static struct fb_var_screeninfo fb_var = { 0, }; + + +/* + * Interface used by the world + */ +static int g364fb_open(struct fb_info *info); +static int g364fb_release(struct fb_info *info); +static int g364fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info); +static int g364fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int g364fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int g364fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info); +static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info); +static int g364fb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info); + + +/* + * Interface to the low level console driver + */ +void g364fb_init(void); +static int g364fbcon_switch(int con, struct fb_info *info); +static int g364fbcon_updatevar(int con, struct fb_info *info); +static void g364fbcon_blank(int blank, struct fb_info *info); + + +/* + * Internal routines + */ +static int g364fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info); +static int g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info); +static void do_install_cmap(int con, struct fb_info *info); + + +static struct fb_ops g364fb_ops = { + g364fb_open, g364fb_release, g364fb_get_fix, g364fb_get_var, g364fb_set_var, + g364fb_get_cmap, g364fb_set_cmap, g364fb_pan_display, g364fb_ioctl +}; + + +void fbcon_g364fb_cursor(struct display *p, int mode, int x, int y) +{ + switch (mode) { + case CM_ERASE: + *(unsigned int *) CTLA_REG |= CURS_TOGGLE; + break; + + case CM_MOVE: + case CM_DRAW: + *(unsigned int *) CTLA_REG &= ~CURS_TOGGLE; + *(unsigned int *) CURS_POS_REG = ((x * p->fontwidth) << 12) | ((y * p->fontheight)-p->var.yoffset); + break; + } +} + + +static struct display_switch fbcon_g364cfb8 = { + fbcon_cfb8_setup, fbcon_cfb8_bmove, fbcon_cfb8_clear, fbcon_cfb8_putc, + fbcon_cfb8_putcs, fbcon_cfb8_revc, fbcon_g364fb_cursor +}; + + +/* + * Open/Release the frame buffer device + */ +static int g364fb_open(struct fb_info *info) +{ + /* + * Nothing, only a usage count for the moment + */ + MOD_INC_USE_COUNT; + return(0); +} + +static int g364fb_release(struct fb_info *info) +{ + MOD_DEC_USE_COUNT; + return(0); +} + + +/* + * Get the Fixed Part of the Display + */ +static int g364fb_get_fix(struct fb_fix_screeninfo *fix, int con, + struct fb_info *info) +{ + memcpy(fix, &fb_fix, sizeof(fb_fix)); + return 0; +} + + +/* + * Get the User Defined Part of the Display + */ +static int g364fb_get_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + memcpy(var, &fb_var, sizeof(fb_var)); + return 0; +} + + +/* + * Set the User Defined Part of the Display + */ +static int g364fb_set_var(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + struct display *display; + int oldbpp = -1, err; + + if (con >= 0) + display = &fb_display[con]; + else + display = &disp; /* used during initialization */ + + if (var->xres > fb_var.xres || var->yres > fb_var.yres || + var->xres_virtual > fb_var.xres_virtual || + var->yres_virtual > fb_var.yres_virtual || + var->bits_per_pixel > fb_var.bits_per_pixel || + var->nonstd || + (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) + return -EINVAL; + memcpy(var, &fb_var, sizeof(fb_var)); + + if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { + oldbpp = display->var.bits_per_pixel; + display->var = *var; + *(unsigned int *)TOP_REG = var->yoffset * var->xres; + } + if (oldbpp != var->bits_per_pixel) { + if ((err = fb_alloc_cmap(&display->cmap, 0, 0))) + return err; + do_install_cmap(con, info); + } + return 0; +} + + +/* + * Pan or Wrap the Display + * + * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag + */ +static int g364fb_pan_display(struct fb_var_screeninfo *var, int con, + struct fb_info *info) +{ + if (var->xoffset || var->yoffset+var->yres > var->yres_virtual) + return -EINVAL; + + *(unsigned int *)TOP_REG = var->yoffset * var->xres; + return 0; +} + +/* + * Get the Colormap + */ +static int g364fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + if (con == currcon) /* current console? */ + return fb_get_cmap(cmap, &fb_display[con].var, kspc, g364fb_getcolreg, + info); + else if (fb_display[con].cmap.len) /* non default colormap? */ + fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2); + else + fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + cmap, kspc ? 0 : 2); + return 0; +} + +/* + * Set the Colormap + */ +static int g364fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, + struct fb_info *info) +{ + int err; + + if (!fb_display[con].cmap.len) { /* no colormap allocated? */ + if ((err = fb_alloc_cmap(&fb_display[con].cmap, + 1<<fb_display[con].var.bits_per_pixel, 0))) + return err; + } + if (con == currcon) { /* current console? */ + err = fb_set_cmap(cmap, &fb_display[con].var, kspc, g364fb_setcolreg, + info); + return err; + } else + fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1); + return 0; +} + + +static int g364fb_ioctl(struct inode *inode, struct file *file, u_int cmd, + u_long arg, int con, struct fb_info *info) +{ + return -EINVAL; +} + + +/* + * Initialisation + */ +__initfunc(void g364fb_init(void)) +{ + int err; + int i,j; + volatile unsigned int *pal_ptr = (volatile unsigned int *) CLR_PAL_REG; + volatile unsigned int *curs_pal_ptr = (volatile unsigned int *) CURS_PAL_REG; + unsigned int xres, yres; + int mem; + + /* TBD: G364 detection */ + + /* get the resolution set by ARC console */ + *(volatile unsigned int *)CTLA_REG &= ~ENABLE_VTG; + xres = (*((volatile unsigned int*)DISPLAY_REG) & 0x00ffffff) * 4; + yres = (*((volatile unsigned int*)VDISPLAY_REG) & 0x00ffffff) / 2; + *(volatile unsigned int *)CTLA_REG |= ENABLE_VTG; + + /* initialise color palette */ + for (i = 0; i < 16; i++) { + j = color_table[i]; + palette[i].red=default_red[j]; + palette[i].green=default_grn[j]; + palette[i].blue=default_blu[j]; + pal_ptr[i << 1] = (palette[i].red << 16) | (palette[i].green << 8) | palette[i].blue; + } + + /* setup cursor */ + curs_pal_ptr[0] |= 0x00ffffff; + curs_pal_ptr[2] |= 0x00ffffff; + curs_pal_ptr[4] |= 0x00ffffff; + + /* + * first set the whole cursor to transparent + */ + for (i = 0; i < 512; i++) + *(unsigned short *)(CURS_PAT_REG+i*8) = 0; + + /* + * switch the last two lines to cursor palette 3 + * we assume here, that FONTSIZE_X is 8 + */ + *(unsigned short *)(CURS_PAT_REG + 14*64) = 0xffff; + *(unsigned short *)(CURS_PAT_REG + 15*64) = 0xffff; + + fb_var.bits_per_pixel = 8; + fb_var.xres = fb_var.xres_virtual = xres; + fb_var.yres = yres; + + fb_fix.line_length = (xres / 8) * fb_var.bits_per_pixel; + fb_fix.smem_start = (char *)0x40000000; /* physical address */ + /* get size of video memory; this is special for the JAZZ hardware */ + mem = (r4030_read_reg32(JAZZ_R4030_CONFIG) >> 8) & 3; + fb_fix.smem_len = (1 << (mem*2)) * 512 * 1024; + fb_fix.type = FB_TYPE_PACKED_PIXELS; + fb_fix.type_aux = 0; + fb_fix.visual = FB_VISUAL_PSEUDOCOLOR; + fb_fix.xpanstep = 0; + fb_fix.ypanstep = 1; + fb_fix.ywrapstep = 0; + fb_fix.mmio_start = NULL; + fb_fix.mmio_len = 0; + fb_fix.accel = FB_ACCEL_NONE; + + fb_var.yres_virtual = fb_fix.smem_len / xres; + fb_var.xoffset = fb_var.yoffset = 0; + fb_var.grayscale = 0; + + fb_var.red.offset = 0; + fb_var.green.offset = 0; + fb_var.blue.offset = 0; + + fb_var.red.length = fb_var.green.length = fb_var.blue.length = 8; + fb_var.red.msb_right = fb_var.green.msb_right = fb_var.blue.msb_right = 0; + fb_var.transp.offset = fb_var.transp.length = fb_var.transp.msb_right = 0; + fb_var.nonstd = 0; + fb_var.activate = 0; + fb_var.height = fb_var.width = -1; + fb_var.accel_flags = 0; + fb_var.pixclock = 39722; + fb_var.left_margin = 40; + fb_var.right_margin = 24; + fb_var.upper_margin = 32; + fb_var.lower_margin = 11; + fb_var.hsync_len = 96; + fb_var.vsync_len = 2; + fb_var.sync = 0; + fb_var.vmode = FB_VMODE_NONINTERLACED; + + disp.var = fb_var; + disp.cmap.start = 0; + disp.cmap.len = 0; + disp.cmap.red = disp.cmap.green = disp.cmap.blue = disp.cmap.transp = NULL; + disp.screen_base = G364_MEM_BASE; /* virtual kernel address */ + disp.visual = fb_fix.visual; + disp.type = fb_fix.type; + disp.type_aux = fb_fix.type_aux; + disp.ypanstep = fb_fix.ypanstep; + disp.ywrapstep = fb_fix.ywrapstep; + disp.line_length = fb_fix.line_length; + disp.can_soft_blank = 1; + disp.inverse = 0; + disp.dispsw = &fbcon_g364cfb8; + + strcpy(fb_info.modename, fb_fix.id); + fb_info.node = -1; + fb_info.fbops = &g364fb_ops; + fb_info.disp = &disp; + fb_info.fontname[0] = '\0'; + fb_info.changevar = NULL; + fb_info.switch_con = &g364fbcon_switch; + fb_info.updatevar = &g364fbcon_updatevar; + fb_info.blank = &g364fbcon_blank; + + err = register_framebuffer(&fb_info); + if (err < 0) + return; + + g364fb_set_var(&fb_var, -1, &fb_info); + + printk("fb%d: %s frame buffer device\n", GET_FB_IDX(fb_info.node), + fb_fix.id); +} + + +static int g364fbcon_switch(int con, struct fb_info *info) +{ + /* Do we have to save the colormap? */ + if (fb_display[currcon].cmap.len) + fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, + g364fb_getcolreg, info); + + currcon = con; + /* Install new colormap */ + do_install_cmap(con, info); + g364fbcon_updatevar(con, info); + return 0; +} + +/* + * Update the `var' structure (called by fbcon.c) + */ +static int g364fbcon_updatevar(int con, struct fb_info *info) +{ + if (con == currcon) { + struct fb_var_screeninfo *var = &fb_display[currcon].var; + + /* hardware scrolling */ + *(unsigned int *)TOP_REG = var->yoffset * var->xres; + } + return 0; +} + +/* + * Blank the display. + */ +static void g364fbcon_blank(int blank, struct fb_info *info) +{ + if (blank) + *(unsigned int *) CTLA_REG |= FORCE_BLANK; + else + *(unsigned int *) CTLA_REG &= ~FORCE_BLANK; +} + +/* + * Read a single color register and split it into + * colors/transparent. Return != 0 for invalid regno. + */ +static int g364fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, + u_int *transp, struct fb_info *info) +{ + if (regno > 255) + return 1; + *red = palette[regno].red; + *green = palette[regno].green; + *blue = palette[regno].blue; + 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 g364fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + volatile unsigned int *ptr = (volatile unsigned int *) CLR_PAL_REG; + + if (regno > 255) + return 1; + palette[regno].red = red; + palette[regno].green = green; + palette[regno].blue = blue; + + ptr[regno << 1] = (red << 16) | (green << 8) | blue; + + return 0; +} + + +static void do_install_cmap(int con, struct fb_info *info) +{ + if (con != currcon) + return; + if (fb_display[con].cmap.len) + fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1, + g364fb_setcolreg, info); + else + fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), + &fb_display[con].var, 1, g364fb_setcolreg, + info); +} |