📄 cyber2000fb.c
字号:
/* * linux/drivers/video/cyber2000fb.c * * Copyright (C) 1998-2002 Russell King * * MIPS and 50xx clock support * Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com> * * 32 bit support, text color and panning fixes for modes != 8 bit * Copyright (C) 2002 Denis Oliver Kropp <dok@directfb.org> * * 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, especially for the colourmap stuff. * * 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. * * When we're in truecolour mode, we power down the LUT RAM as a power * saving feature. Also, when we enter any of the powersaving modes * (except soft blanking) we power down the RAMDACs. This saves about * 1W, which is roughly 8% of the power consumption of a NetWinder * (which, incidentally, is about the same saving as a 2.5in hard disk * entering standby mode.) */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/init.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/system.h>#ifdef __arm__#include <asm/mach-types.h>#endif#include "cyber2000fb.h"struct cfb_info { struct fb_info fb; struct display_switch *dispsw; struct display *display; struct pci_dev *dev; unsigned char __iomem *region; unsigned char __iomem *regs; u_int id; 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; /* * RAMDAC control register is both of these or'ed together */ u_char ramdac_ctrl; u_char ramdac_powerdown; u32 pseudo_palette[16];};static char *default_font = "Acorn8x8";module_param(default_font, charp, 0);MODULE_PARM_DESC(default_font, "Default font name");/* * Our access methods. */#define cyber2000fb_writel(val, reg, cfb) writel(val, (cfb)->regs + (reg))#define cyber2000fb_writew(val, reg, cfb) writew(val, (cfb)->regs + (reg))#define cyber2000fb_writeb(val, reg, cfb) writeb(val, (cfb)->regs + (reg))#define cyber2000fb_readb(reg, cfb) readb((cfb)->regs + (reg))static inline voidcyber2000_crtcw(unsigned int reg, unsigned int val, struct cfb_info *cfb){ cyber2000fb_writew((reg & 255) | val << 8, 0x3d4, cfb);}static inline voidcyber2000_grphw(unsigned int reg, unsigned int val, struct cfb_info *cfb){ cyber2000fb_writew((reg & 255) | val << 8, 0x3ce, cfb);}static inline unsigned intcyber2000_grphr(unsigned int reg, struct cfb_info *cfb){ cyber2000fb_writeb(reg, 0x3ce, cfb); return cyber2000fb_readb(0x3cf, cfb);}static inline voidcyber2000_attrw(unsigned int reg, unsigned int val, struct cfb_info *cfb){ cyber2000fb_readb(0x3da, cfb); cyber2000fb_writeb(reg, 0x3c0, cfb); cyber2000fb_readb(0x3c1, cfb); cyber2000fb_writeb(val, 0x3c0, cfb);}static inline voidcyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb){ cyber2000fb_writew((reg & 255) | val << 8, 0x3c4, cfb);}/* -------------------- Hardware specific routines ------------------------- *//* * Hardware Cyber2000 Acceleration */static voidcyber2000fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect){ struct cfb_info *cfb = (struct cfb_info *)info; unsigned long dst, col; if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { cfb_fillrect(info, rect); return; } cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); cyber2000fb_writew(rect->width - 1, CO_REG_PIXWIDTH, cfb); cyber2000fb_writew(rect->height - 1, CO_REG_PIXHEIGHT, cfb); col = rect->color; if (cfb->fb.var.bits_per_pixel > 8) col = ((u32 *)cfb->fb.pseudo_palette)[col]; cyber2000fb_writel(col, CO_REG_FGCOLOUR, cfb); dst = rect->dx + rect->dy * cfb->fb.var.xres_virtual; if (cfb->fb.var.bits_per_pixel == 24) { cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); dst *= 3; } cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb); cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb); cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb); cyber2000fb_writew(CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);}static voidcyber2000fb_copyarea(struct fb_info *info, const struct fb_copyarea *region){ struct cfb_info *cfb = (struct cfb_info *)info; unsigned int cmd = CO_CMD_L_PATTERN_FGCOL; unsigned long src, dst; if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) { cfb_copyarea(info, region); return; } cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb); cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); src = region->sx + region->sy * cfb->fb.var.xres_virtual; dst = region->dx + region->dy * cfb->fb.var.xres_virtual; if (region->sx < region->dx) { src += region->width - 1; dst += region->width - 1; cmd |= CO_CMD_L_INC_LEFT; } if (region->sy < region->dy) { src += (region->height - 1) * cfb->fb.var.xres_virtual; dst += (region->height - 1) * cfb->fb.var.xres_virtual; cmd |= CO_CMD_L_INC_UP; } if (cfb->fb.var.bits_per_pixel == 24) { cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb); src *= 3; dst *= 3; } cyber2000fb_writel(src, CO_REG_SRC1_PTR, cfb); cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb); cyber2000fb_writew(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb); cyber2000fb_writew(cmd, CO_REG_CMD_L, cfb); cyber2000fb_writew(CO_CMD_H_FGSRCMAP | CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);}static voidcyber2000fb_imageblit(struct fb_info *info, const struct fb_image *image){ cfb_imageblit(info, image); return;}static int cyber2000fb_sync(struct fb_info *info){ struct cfb_info *cfb = (struct cfb_info *)info; int count = 100000; if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) return 0; while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & CO_CTRL_BUSY) { if (!count--) { debug_printf("accel_wait timed out\n"); cyber2000fb_writeb(0, CO_REG_CONTROL, cfb); break; } udelay(1); } return 0;}/* * =========================================================================== */static inline u32 convert_bitfield(u_int val, struct fb_bitfield *bf){ u_int mask = (1 << bf->length) - 1; return (val >> (16 - bf->length) & mask) << bf->offset;}/* * Set a single color register. Return != 0 for invalid regno. */static intcyber2000fb_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; struct fb_var_screeninfo *var = &cfb->fb.var; u32 pseudo_val; int ret = 1; switch (cfb->fb.fix.visual) { default: return 1; /* * Pseudocolour: * 8 8 * pixel --/--+--/--> red lut --> red dac * | 8 * +--/--> green lut --> green dac * | 8 * +--/--> blue lut --> blue dac */ case FB_VISUAL_PSEUDOCOLOR: 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; cyber2000fb_writeb(regno, 0x3c8, cfb); cyber2000fb_writeb(red, 0x3c9, cfb); cyber2000fb_writeb(green, 0x3c9, cfb); cyber2000fb_writeb(blue, 0x3c9, cfb); return 0; /* * Direct colour: * n rl * pixel --/--+--/--> red lut --> red dac * | gl * +--/--> green lut --> green dac * | bl * +--/--> blue lut --> blue dac * n = bpp, rl = red length, gl = green length, bl = blue length */ case FB_VISUAL_DIRECTCOLOR: red >>= 8; green >>= 8; blue >>= 8; if (var->green.length == 6 && regno < 64) { cfb->palette[regno << 2].green = green; /* * The 6 bits of the green component are applied * to the high 6 bits of the LUT. */ cyber2000fb_writeb(regno << 2, 0x3c8, cfb); cyber2000fb_writeb(cfb->palette[regno >> 1].red, 0x3c9, cfb); cyber2000fb_writeb(green, 0x3c9, cfb); cyber2000fb_writeb(cfb->palette[regno >> 1].blue, 0x3c9, cfb); green = cfb->palette[regno << 3].green; ret = 0; } if (var->green.length >= 5 && regno < 32) { cfb->palette[regno << 3].red = red; cfb->palette[regno << 3].green = green; cfb->palette[regno << 3].blue = blue; /* * The 5 bits of each colour component are * applied to the high 5 bits of the LUT. */ cyber2000fb_writeb(regno << 3, 0x3c8, cfb); cyber2000fb_writeb(red, 0x3c9, cfb); cyber2000fb_writeb(green, 0x3c9, cfb); cyber2000fb_writeb(blue, 0x3c9, cfb); ret = 0; } if (var->green.length == 4 && regno < 16) { cfb->palette[regno << 4].red = red; cfb->palette[regno << 4].green = green; cfb->palette[regno << 4].blue = blue; /* * The 5 bits of each colour component are * applied to the high 5 bits of the LUT. */ cyber2000fb_writeb(regno << 4, 0x3c8, cfb); cyber2000fb_writeb(red, 0x3c9, cfb); cyber2000fb_writeb(green, 0x3c9, cfb); cyber2000fb_writeb(blue, 0x3c9, cfb); ret = 0; } /* * Since this is only used for the first 16 colours, we * don't have to care about overflowing for regno >= 32 */ pseudo_val = regno << var->red.offset | regno << var->green.offset | regno << var->blue.offset; break; /* * True colour: * n rl * pixel --/--+--/--> red dac * | gl * +--/--> green dac * | bl * +--/--> blue dac * n = bpp, rl = red length, gl = green length, bl = blue length */ case FB_VISUAL_TRUECOLOR: pseudo_val = convert_bitfield(transp ^ 0xffff, &var->transp); pseudo_val |= convert_bitfield(red, &var->red); pseudo_val |= convert_bitfield(green, &var->green); pseudo_val |= convert_bitfield(blue, &var->blue); break; } /* * Now set our pseudo palette for the CFB16/24/32 drivers. */ if (regno < 16) ((u32 *)cfb->fb.pseudo_palette)[regno] = pseudo_val; return ret;}struct par_info { /* * Hardware */ u_char clock_mult; u_char clock_div; u_char extseqmisc; u_char co_pixfmt; u_char crtc_ofl; u_char crtc[19]; u_int width; u_int pitch; u_int fetch; /* * Other */ u_char ramdac;};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_write_ramdac_ctrl(struct cfb_info *cfb){ unsigned int i; unsigned int val = cfb->ramdac_ctrl | cfb->ramdac_powerdown; cyber2000fb_writeb(0x56, 0x3ce, cfb); i = cyber2000fb_readb(0x3cf, cfb); cyber2000fb_writeb(i | 4, 0x3cf, cfb); cyber2000fb_writeb(val, 0x3c6, cfb); cyber2000fb_writeb(i, 0x3cf, cfb);}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++) { cyber2000fb_writeb(i, 0x3c8, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); cyber2000fb_writeb(0, 0x3c9, cfb); } cyber2000fb_writeb(0xef, 0x3c2, cfb); cyber2000_crtcw(0x11, 0x0b, cfb); cyber2000_attrw(0x11, 0x00, cfb); cyber2000_seqw(0x00, 0x01, cfb); cyber2000_seqw(0x01, 0x01, cfb); cyber2000_seqw(0x02, 0x0f, cfb); cyber2000_seqw(0x03, 0x00, cfb); cyber2000_seqw(0x04, 0x0e, cfb); cyber2000_seqw(0x00, 0x03, cfb); for (i = 0; i < sizeof(crtc_idx); i++) cyber2000_crtcw(crtc_idx[i], hw->crtc[i], cfb); for (i = 0x0a; i < 0x10; i++) cyber2000_crtcw(i, 0, cfb); cyber2000_grphw(EXT_CRT_VRTOFL, hw->crtc_ofl, cfb); cyber2000_grphw(0x00, 0x00, cfb); cyber2000_grphw(0x01, 0x00, cfb); cyber2000_grphw(0x02, 0x00, cfb); cyber2000_grphw(0x03, 0x00, cfb); cyber2000_grphw(0x04, 0x00, cfb); cyber2000_grphw(0x05, 0x60, cfb); cyber2000_grphw(0x06, 0x05, cfb); cyber2000_grphw(0x07, 0x0f, cfb); cyber2000_grphw(0x08, 0xff, cfb); /* Attribute controller registers */ for (i = 0; i < 16; i++) cyber2000_attrw(i, i, cfb); cyber2000_attrw(0x10, 0x01, cfb); cyber2000_attrw(0x11, 0x00, cfb); cyber2000_attrw(0x12, 0x0f, cfb); cyber2000_attrw(0x13, 0x00, cfb); cyber2000_attrw(0x14, 0x00, cfb); /* PLL registers */ cyber2000_grphw(EXT_DCLK_MULT, hw->clock_mult, cfb); cyber2000_grphw(EXT_DCLK_DIV, hw->clock_div, cfb); cyber2000_grphw(EXT_MCLK_MULT, cfb->mclk_mult, cfb); cyber2000_grphw(EXT_MCLK_DIV, cfb->mclk_div, cfb); cyber2000_grphw(0x90, 0x01, cfb); cyber2000_grphw(0xb9, 0x80, cfb); cyber2000_grphw(0xb9, 0x00, cfb); cfb->ramdac_ctrl = hw->ramdac; cyber2000fb_write_ramdac_ctrl(cfb); cyber2000fb_writeb(0x20, 0x3c0, cfb); cyber2000fb_writeb(0xff, 0x3c6, cfb); cyber2000_grphw(0x14, hw->fetch, cfb); cyber2000_grphw(0x15, ((hw->fetch >> 8) & 0x03) | ((hw->pitch >> 4) & 0x30), cfb); cyber2000_grphw(EXT_SEQ_MISC, hw->extseqmisc, cfb); /* * Set up accelerator registers */ cyber2000fb_writew(hw->width, CO_REG_SRC_WIDTH, cfb); cyber2000fb_writew(hw->width, CO_REG_DEST_WIDTH, cfb); cyber2000fb_writeb(hw->co_pixfmt, CO_REG_PIXFMT, cfb);}static inline intcyber2000fb_update_start(struct cfb_info *cfb, struct fb_var_screeninfo *var){ u_int base = var->yoffset * var->xres_virtual + var->xoffset; base *= var->bits_per_pixel; /* * Convert to bytes and shift two extra bits because DAC * can only start on 4 byte aligned data. */ base >>= 5; if (base >= 1 << 20) return -EINVAL; cyber2000_grphw(0x10, base >> 16 | 0x10, cfb); cyber2000_crtcw(0x0c, base >> 8, cfb); cyber2000_crtcw(0x0d, base, cfb); return 0;}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 ENCODE_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] = ENCODE_BIT(Hblankend, 0, 0x1f, 0) | ENCODE_BIT(1, 0, 0x01, 7); Hsyncend = (var->xres + var->right_margin + var->hsync_len) >> 3; hw->crtc[5] = ENCODE_BIT(Hsyncend, 0, 0x1f, 0) | ENCODE_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; if (Vtotal > 2047) return -EINVAL; Vblankstart = var->yres + 6; Vblankend = Vtotal - 10;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -