📄 cyber2000fb.c
字号:
/* * linux/drivers/video/cyber2000fb.c * * Copyright (C) 1998-2000 Russell King * * 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. * * Integraphics CyberPro 2000, 2010 and 5000 frame buffer device * * Based on cyberfb.c. * * Note that we now use the new fbcon fix, var and cmap scheme. We do still * have to check which console is the currently displayed one however, since * especially for the colourmap stuff. Once fbcon has been fully migrated, * we can kill the last 5 references to cfb->currcon. * * We also use the new hotplug PCI subsystem. I'm not sure if there are any * such cards, but I'm erring on the side of caution. We don't want to go * pop just because someone does have one. * * Note that this doesn't work fully in the case of multiple CyberPro cards * with grabbers. We currently can only attach to the first CyberPro card * found. */#include <linux/config.h>#include <linux/module.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/delay.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/pgtable.h>#include <asm/system.h>#include <asm/uaccess.h>#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb24.h>/* * Define this if you don't want RGB565, but RGB555 for 16bpp displays. *//*#define CFB16_IS_CFB15*//* * This is the offset of the PCI space in physical memory */#ifdef CONFIG_FOOTBRIDGE#define PCI_PHYS_OFFSET 0x80000000#else#define PCI_PHYS_OFFSET 0x00000000#endifstatic char *CyberRegs;#include "cyber2000fb.h"struct cfb_info { struct fb_info fb; struct display_switch *dispsw; struct pci_dev *dev; signed int currcon; int func_use_count; u_long ref_ps; /* * Clock divisors */ u_int divisors[4]; struct { u8 red, green, blue; } palette[NR_PALETTE]; u_char mem_ctl1; u_char mem_ctl2; u_char mclk_mult; u_char mclk_div;};/* -------------------- Hardware specific routines ------------------------- *//* * Hardware Cyber2000 Acceleration */static void cyber2000_accel_wait(void){ int count = 100000; while (cyber2000_inb(CO_REG_CONTROL) & 0x80) { if (!count--) { debug_printf("accel_wait timed out\n"); cyber2000_outb(0, CO_REG_CONTROL); return; } udelay(1); }}static void cyber2000_accel_setup(struct display *p){ struct cfb_info *cfb = (struct cfb_info *)p->fb_info; cfb->dispsw->setup(p);}static voidcyber2000_accel_bmove(struct display *p, int sy, int sx, int dy, int dx, int height, int width){ struct fb_var_screeninfo *var = &p->fb_info->var; u_long src, dst; u_int fh, fw; int cmd = CO_CMD_L_PATTERN_FGCOL; fw = fontwidth(p); sx *= fw; dx *= fw; width *= fw; width -= 1; if (sx < dx) { sx += width; dx += width; cmd |= CO_CMD_L_INC_LEFT; } fh = fontheight(p); sy *= fh; dy *= fh; height *= fh; height -= 1; if (sy < dy) { sy += height; dy += height; cmd |= CO_CMD_L_INC_UP; } src = sx + sy * var->xres_virtual; dst = dx + dy * var->xres_virtual; cyber2000_accel_wait(); cyber2000_outb(0x00, CO_REG_CONTROL); cyber2000_outb(0x03, CO_REG_FORE_MIX); cyber2000_outw(width, CO_REG_WIDTH); if (var->bits_per_pixel != 24) { cyber2000_outl(dst, CO_REG_DEST_PTR); cyber2000_outl(src, CO_REG_SRC_PTR); } else { cyber2000_outl(dst * 3, CO_REG_DEST_PTR); cyber2000_outb(dst, CO_REG_X_PHASE); cyber2000_outl(src * 3, CO_REG_SRC_PTR); } cyber2000_outw(height, CO_REG_HEIGHT); cyber2000_outw(cmd, CO_REG_CMD_L); cyber2000_outw(0x2800, CO_REG_CMD_H);}static voidcyber2000_accel_clear(struct vc_data *conp, struct display *p, int sy, int sx, int height, int width){ struct fb_var_screeninfo *var = &p->fb_info->var; u_long dst; u_int fw, fh; u32 bgx = attr_bgcol_ec(p, conp); fw = fontwidth(p); fh = fontheight(p); dst = sx * fw + sy * var->xres_virtual * fh; width = width * fw - 1; height = height * fh - 1; cyber2000_accel_wait(); cyber2000_outb(0x00, CO_REG_CONTROL); cyber2000_outb(0x03, CO_REG_FORE_MIX); cyber2000_outw(width, CO_REG_WIDTH); cyber2000_outw(height, CO_REG_HEIGHT); switch (var->bits_per_pixel) { case 15: case 16: bgx = ((u16 *)p->dispsw_data)[bgx]; case 8: cyber2000_outl(dst, CO_REG_DEST_PTR); break; case 24: cyber2000_outl(dst * 3, CO_REG_DEST_PTR); cyber2000_outb(dst, CO_REG_X_PHASE); bgx = ((u32 *)p->dispsw_data)[bgx]; break; } cyber2000_outl(bgx, CO_REG_FOREGROUND); cyber2000_outw(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L); cyber2000_outw(0x0800, CO_REG_CMD_H);}static voidcyber2000_accel_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx){ struct cfb_info *cfb = (struct cfb_info *)p->fb_info; cyber2000_accel_wait(); cfb->dispsw->putc(conp, p, c, yy, xx);}static voidcyber2000_accel_putcs(struct vc_data *conp, struct display *p, const unsigned short *s, int count, int yy, int xx){ struct cfb_info *cfb = (struct cfb_info *)p->fb_info; cyber2000_accel_wait(); cfb->dispsw->putcs(conp, p, s, count, yy, xx);}static void cyber2000_accel_revc(struct display *p, int xx, int yy){ struct cfb_info *cfb = (struct cfb_info *)p->fb_info; cyber2000_accel_wait(); cfb->dispsw->revc(p, xx, yy);}static voidcyber2000_accel_clear_margins(struct vc_data *conp, struct display *p, int bottom_only){ struct cfb_info *cfb = (struct cfb_info *)p->fb_info; cfb->dispsw->clear_margins(conp, p, bottom_only);}static struct display_switch fbcon_cyber_accel = { setup: cyber2000_accel_setup, bmove: cyber2000_accel_bmove, clear: cyber2000_accel_clear, putc: cyber2000_accel_putc, putcs: cyber2000_accel_putcs, revc: cyber2000_accel_revc, clear_margins: cyber2000_accel_clear_margins, fontwidthmask: FONTWIDTH(8)|FONTWIDTH(16)};/* * Set a single color register. Return != 0 for invalid regno. */static intcyber2000_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int transp, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; if (regno >= NR_PALETTE) return 1; red >>= 8; green >>= 8; blue >>= 8; cfb->palette[regno].red = red; cfb->palette[regno].green = green; cfb->palette[regno].blue = blue; switch (cfb->fb.var.bits_per_pixel) {#ifdef FBCON_HAS_CFB8 case 8: cyber2000_outb(regno, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(blue, 0x3c9); break;#endif#ifdef FBCON_HAS_CFB16 case 16:#ifndef CFB16_IS_CFB15 if (regno < 64) { /* write green */ cyber2000_outb(regno << 2, 0x3c8); cyber2000_outb(cfb->palette[regno >> 1].red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(cfb->palette[regno >> 1].blue, 0x3c9); } if (regno < 32) { /* write red,blue */ cyber2000_outb(regno << 3, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(cfb->palette[regno << 1].green, 0x3c9); cyber2000_outb(blue, 0x3c9); } if (regno < 16) ((u16 *)cfb->fb.pseudo_palette)[regno] = regno | regno << 5 | regno << 11; break;#endif case 15: if (regno < 32) { cyber2000_outb(regno << 3, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(blue, 0x3c9); } if (regno < 16) ((u16 *)cfb->fb.pseudo_palette)[regno] = regno | regno << 5 | regno << 10; break;#endif#ifdef FBCON_HAS_CFB24 case 24: cyber2000_outb(regno, 0x3c8); cyber2000_outb(red, 0x3c9); cyber2000_outb(green, 0x3c9); cyber2000_outb(blue, 0x3c9); if (regno < 16) ((u32 *)cfb->fb.pseudo_palette)[regno] = regno | regno << 8 | regno << 16; break;#endif default: return 1; } return 0;}struct par_info { /* * Hardware */ u_char clock_mult; u_char clock_div; u_char visualid; u_char pixformat; u_char crtc_ofl; u_char crtc[19]; u_int width; u_int pitch; u_int fetch; /* * Other */ u_char palette_ctrl;};static const u_char crtc_idx[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18};static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw){ u_int i; /* * Blank palette */ for (i = 0; i < NR_PALETTE; i++) { cyber2000_outb(i, 0x3c8); cyber2000_outb(0, 0x3c9); cyber2000_outb(0, 0x3c9); cyber2000_outb(0, 0x3c9); } cyber2000_outb(0xef, 0x3c2); cyber2000_crtcw(0x11, 0x0b); cyber2000_attrw(0x11, 0x00); cyber2000_seqw(0x00, 0x01); cyber2000_seqw(0x01, 0x01); cyber2000_seqw(0x02, 0x0f); cyber2000_seqw(0x03, 0x00); cyber2000_seqw(0x04, 0x0e); cyber2000_seqw(0x00, 0x03); for (i = 0; i < sizeof(crtc_idx); i++) cyber2000_crtcw(crtc_idx[i], hw->crtc[i]); for (i = 0x0a; i < 0x10; i++) cyber2000_crtcw(i, 0); cyber2000_grphw(0x11, hw->crtc_ofl); cyber2000_grphw(0x00, 0x00); cyber2000_grphw(0x01, 0x00); cyber2000_grphw(0x02, 0x00); cyber2000_grphw(0x03, 0x00); cyber2000_grphw(0x04, 0x00); cyber2000_grphw(0x05, 0x60); cyber2000_grphw(0x06, 0x05); cyber2000_grphw(0x07, 0x0f); cyber2000_grphw(0x08, 0xff); /* Attribute controller registers */ for (i = 0; i < 16; i++) cyber2000_attrw(i, i); cyber2000_attrw(0x10, 0x01); cyber2000_attrw(0x11, 0x00); cyber2000_attrw(0x12, 0x0f); cyber2000_attrw(0x13, 0x00); cyber2000_attrw(0x14, 0x00); /* PLL registers */ cyber2000_grphw(DCLK_MULT, hw->clock_mult); cyber2000_grphw(DCLK_DIV, hw->clock_div); cyber2000_grphw(MCLK_MULT, cfb->mclk_mult); cyber2000_grphw(MCLK_DIV, cfb->mclk_div); cyber2000_grphw(0x90, 0x01); cyber2000_grphw(0xb9, 0x80); cyber2000_grphw(0xb9, 0x00); cyber2000_outb(0x56, 0x3ce); i = cyber2000_inb(0x3cf); cyber2000_outb(i | 4, 0x3cf); cyber2000_outb(hw->palette_ctrl, 0x3c6); cyber2000_outb(i, 0x3cf); cyber2000_outb(0x20, 0x3c0); cyber2000_outb(0xff, 0x3c6); cyber2000_grphw(0x14, hw->fetch); cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) | ((hw->pitch >> 4) & 0x30)); cyber2000_grphw(0x77, hw->visualid); /* make sure we stay in linear mode */ cyber2000_grphw(0x33, 0x0d); /* * Set up accelerator registers */ cyber2000_outw(hw->width, CO_REG_SRC_WIDTH); cyber2000_outw(hw->width, CO_REG_DEST_WIDTH); cyber2000_outb(hw->pixformat, CO_REG_PIX_FORMAT);}static inline intcyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var){ u_int base; base = var->yoffset * var->xres_virtual + var->xoffset; base >>= 2; if (base >= 1 << 20) return -EINVAL; cyber2000_grphw(0x10, base >> 16 | 0x10); cyber2000_crtcw(0x0c, base >> 8); cyber2000_crtcw(0x0d, base); return 0;}/* * Set the Colormap */static intcyber2000fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; struct fb_cmap *dcmap = &fb_display[con].cmap; int err = 0; /* no colormap allocated? */ if (!dcmap->len) { int size; if (cfb->fb.var.bits_per_pixel == 16) size = 32; else size = 256; err = fb_alloc_cmap(dcmap, size, 0); } /* * we should be able to remove this test once fbcon has been * "improved" --rmk */ if (!err && con == cfb->currcon) { err = fb_set_cmap(cmap, kspc, cyber2000_setcolreg, &cfb->fb); dcmap = &cfb->fb.cmap; } if (!err) fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1); return err;}static intcyber2000fb_decode_crtc(struct par_info *hw, struct cfb_info *cfb, struct fb_var_screeninfo *var){ u_int Htotal, Hblankend, Hsyncend; u_int Vtotal, Vdispend, Vblankstart, Vblankend, Vsyncstart, Vsyncend;#define BIT(v,b1,m,b2) (((v >> b1) & m) << b2) hw->crtc[13] = hw->pitch; hw->crtc[17] = 0xe3; hw->crtc[14] = 0; hw->crtc[8] = 0; Htotal = var->xres + var->right_margin + var->hsync_len + var->left_margin; if (Htotal > 2080) return -EINVAL; hw->crtc[0] = (Htotal >> 3) - 5; hw->crtc[1] = (var->xres >> 3) - 1; hw->crtc[2] = var->xres >> 3; hw->crtc[4] = (var->xres + var->right_margin) >> 3; Hblankend = (Htotal - 4*8) >> 3; hw->crtc[3] = BIT(Hblankend, 0, 0x1f, 0) | BIT(1, 0, 0x01, 7); Hsyncend = (var->xres + var->right_margin + var->hsync_len) >> 3; hw->crtc[5] = BIT(Hsyncend, 0, 0x1f, 0) | BIT(Hblankend, 5, 0x01, 7); Vdispend = var->yres - 1; Vsyncstart = var->yres + var->lower_margin; Vsyncend = var->yres + var->lower_margin + var->vsync_len; Vtotal = var->yres + var->lower_margin + var->vsync_len + var->upper_margin - 2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -