📄 pm2fb.c
字号:
/* * Permedia2 framebuffer driver. * Copyright (c) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT) * Copyright (c) 1999 Jakub Jelinek (jakub@redhat.com) * Based on linux/drivers/video/skeletonfb.c by Geert Uytterhoeven. * -------------------------------------------------------------------------- * $Id: pm2fb.c,v 1.163 1999/02/21 14:06:49 illo Exp $ * -------------------------------------------------------------------------- * TODO multiple boards support * -------------------------------------------------------------------------- * 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/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/fb.h>#include <linux/selection.h>#include <linux/console.h>#include <linux/init.h>#include <linux/pci.h>#include <asm/system.h>#include <asm/io.h>#include <asm/uaccess.h>#include <video/fbcon.h>#include <video/fbcon-cfb8.h>#include <video/fbcon-cfb16.h>#include <video/fbcon-cfb24.h>#include <video/fbcon-cfb32.h>#include "pm2fb.h"#include "cvisionppc.h"#ifdef __sparc__#include <asm/pbm.h>#include <asm/fbio.h>#endif#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)#error "The endianness of the target host has not been defined."#endif#if defined(__BIG_ENDIAN) && !defined(__sparc__)#define PM2FB_BE_APERTURE#endif/* Need to debug this some more */#undef PM2FB_HW_CURSOR#if defined(CONFIG_FB_PM2_PCI) && !defined(CONFIG_PCI)#undef CONFIG_FB_PM2_PCI#warning "support for Permedia2 PCI boards with no generic PCI support!"#endif#undef PM2FB_MASTER_DEBUG#ifdef PM2FB_MASTER_DEBUG#define DPRINTK(a,b...) printk(KERN_DEBUG "pm2fb: %s: " a, __FUNCTION__ , ## b)#else#define DPRINTK(a,b...)#endif #define PICOS2KHZ(a) (1000000000UL/(a))#define KHZ2PICOS(a) (1000000000UL/(a))/* * The _DEFINITIVE_ memory mapping/unmapping functions. * This is due to the fact that they're changing soooo often... */#define MMAP(a,b) ioremap((unsigned long )(a), b)#define UNMAP(a,b) iounmap(a)/* * The _DEFINITIVE_ memory i/o barrier functions. * This is due to the fact that they're changing soooo often... */#define DEFW() wmb()#define DEFR() rmb()#define DEFRW() mb()#ifndef MIN#define MIN(a,b) ((a)<(b)?(a):(b))#endif#ifndef MAX#define MAX(a,b) ((a)>(b)?(a):(b))#endifstruct pm2fb_par { u32 pixclock; /* pixclock in KHz */ u32 width; /* width of virtual screen */ u32 height; /* height of virtual screen */ u32 hsstart; /* horiz. sync start */ u32 hsend; /* horiz. sync end */ u32 hbend; /* horiz. blank end (also gate end) */ u32 htotal; /* total width (w/ sync & blank) */ u32 vsstart; /* vert. sync start */ u32 vsend; /* vert. sync end */ u32 vbend; /* vert. blank end */ u32 vtotal; /* total height (w/ sync & blank) */ u32 stride; /* screen stride */ u32 base; /* screen base (xoffset+yoffset) */ u32 depth; /* screen depth (8, 16, 24 or 32) */ u32 video; /* video control (hsync,vsync) */};#define OPTF_OLD_MEM (1L<<0)#define OPTF_YPAN (1L<<1)#define OPTF_VIRTUAL (1L<<2)#define OPTF_USER (1L<<3)static struct { char font[40]; u32 flags; struct pm2fb_par user_mode;} pm2fb_options =#ifdef __sparc__ /* For some reason Raptor is not happy with the low-end mode */ {"\0", 0L, {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}};#else {"\0", 0L, {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}};#endifstatic char curblink __initdata = 1;static struct { char name[16]; struct pm2fb_par par;} user_mode[] __initdata = { {"640x480-60", {25174,640,480,4,28,40,199,9,11,45,524,80,0,8,121}}, {"640x480-72", {31199,640,480,6,16,48,207,8,10,39,518,80,0,8,121}}, {"640x480-75", {31499,640,480,4,20,50,209,0,3,20,499,80,0,8,121}}, {"640x480-90", {39909,640,480,8,18,48,207,24,38,53,532,80,0,8,121}}, {"640x480-100", {44899,640,480,8,40,52,211,21,33,51,530,80,0,8,121}}, {"800x600-56", {35999,800,600,6,24,56,255,0,2,25,624,100,0,8,41}}, {"800x600-60", {40000,800,600,10,42,64,263,0,4,28,627,100,0,8,41}}, {"800x600-70", {44899,800,600,6,42,52,251,8,20,36,635,100,0,8,105}}, {"800x600-72", {50000,800,600,14,44,60,259,36,42,66,665,100,0,8,41}}, {"800x600-75", {49497,800,600,4,24,64,263,0,3,25,624,100,0,8,41}}, {"800x600-90", {56637,800,600,2,18,48,247,7,18,35,634,100,0,8,41}}, {"800x600-100", {67499,800,600,0,16,70,269,6,10,25,624,100,0,8,41}}, {"1024x768-60", {64998,1024,768,6,40,80,335,2,8,38,805,128,0,8,121}}, {"1024x768-70", {74996,1024,768,6,40,76,331,2,8,38,805,128,0,8,121}}, {"1024x768-72", {74996,1024,768,6,40,66,321,2,8,38,805,128,0,8,121}}, {"1024x768-75", {78932,1024,768,4,28,72,327,0,3,32,799,128,0,8,41}}, {"1024x768-90", {100000,1024,768,0,24,72,327,20,35,77,844,128,0,8,121}}, {"1024x768-100", {109998,1024,768,0,22,92,347,0,7,24,791,128,0,8,121}}, {"1024x768-illo", {120322,1024,768,12,48,120,375,3,7,32,799,128,0,8,41}}, {"1152x864-60", {80000,1152,864,16,44,76,363,5,10,52,915,144,0,8,41}}, {"1152x864-70", {100000,1152,864,10,48,90,377,12,23,81,944,144,0,8,41}}, {"1152x864-75", {109998,1152,864,6,42,78,365,44,52,138,1001,144,0,8,41}}, {"1152x864-80", {109998,1152,864,4,32,72,359,29,36,94,957,144,0,8,41}}, {"1280x1024-60", {107991,1280,1024,12,40,102,421,0,3,42,1065,160,0,8,41}}, {"1280x1024-70", {125992,1280,1024,20,48,102,421,0,5,42,1065,160,0,8,41}}, {"1280x1024-74", {134989,1280,1024,8,44,108,427,0,29,40,1063,160,0,8,41}}, {"1280x1024-75", {134989,1280,1024,4,40,102,421,0,3,42,1065,160,0,8,41}}, {"1600x1200-60", {155981,1600,1200,8,48,112,511,9,17,70,1269,200,0,8,121}}, {"1600x1200-66", {171998,1600,1200,10,44,120,519,2,5,53,1252,200,0,8,121}}, {"1600x1200-76", {197980,1600,1200,10,44,120,519,2,7,50,1249,200,0,8,121}}, {"\0", },};#ifdef CONFIG_FB_PM2_PCIstruct pm2pci_par { u32 mem_config; u32 mem_control; u32 boot_address; struct pci_dev* dev;};#endif#define DEFAULT_CURSOR_BLINK_RATE (20)#define CURSOR_DRAW_DELAY (2)struct pm2_cursor { int enable; int on; int vbl_cnt; int blink_rate; struct { u16 x, y; } pos, hot, size; u8 color[6]; u8 bits[8][64]; u8 mask[8][64]; struct timer_list *timer;};static const char permedia2_name[16]="Permedia2";static struct pm2fb_info { struct fb_info_gen gen; int board; /* Permedia2 board index (see board_table[] below) */ pm2type_t type; struct { unsigned long fb_base; /* physical framebuffer memory base */ u32 fb_size; /* framebuffer memory size */ unsigned long rg_base; /* physical register memory base */ unsigned long p_fb; /* physical address of frame buffer */ unsigned char* v_fb; /* virtual address of frame buffer */ unsigned long p_regs; /* physical address of registers region, must be rg_base or rg_base+PM2_REGS_SIZE depending on the host endianness */ unsigned char* v_regs; /* virtual address of p_regs */ } regions; union { /* here, the per-board par structs */#ifdef CONFIG_FB_PM2_CVPPC struct cvppc_par cvppc; /* CVisionPPC data */#endif#ifdef CONFIG_FB_PM2_PCI struct pm2pci_par pci; /* Permedia2 PCI boards data */#endif } board_par; struct pm2fb_par current_par; /* displayed screen */ int current_par_valid; u32 memclock; /* memclock (set by the per-board init routine) */ struct display disp; struct { u8 transp; u8 red; u8 green; u8 blue; } palette[256]; union {#ifdef FBCON_HAS_CFB16 u16 cmap16[16];#endif#ifdef FBCON_HAS_CFB24 u32 cmap24[16];#endif#ifdef FBCON_HAS_CFB32 u32 cmap32[16];#endif } cmap; struct pm2_cursor *cursor;} fb_info;#ifdef CONFIG_FB_PM2_CVPPCstatic int cvppc_detect(struct pm2fb_info*);static void cvppc_init(struct pm2fb_info*);#endif#ifdef CONFIG_FB_PM2_PCIstatic int pm2pci_detect(struct pm2fb_info*);static void pm2pci_init(struct pm2fb_info*);#endif#ifdef PM2FB_HW_CURSORstatic void pm2fb_cursor(struct display *p, int mode, int x, int y);static int pm2fb_set_font(struct display *d, int width, int height);static struct pm2_cursor *pm2_init_cursor(struct pm2fb_info *fb);static void pm2v_set_cursor_color(struct pm2fb_info *fb, u8 *red, u8 *green, u8 *blue);static void pm2v_set_cursor_shape(struct pm2fb_info *fb);static u8 cursor_color_map[2] = { 0, 0xff };#else#define pm2fb_cursor NULL#define pm2fb_set_font NULL#endif/* * Table of the supported Permedia2 based boards. * Three hooks are defined for each board: * detect(): should return 1 if the related board has been detected, 0 * otherwise. It should also fill the fields 'regions.fb_base', * 'regions.fb_size', 'regions.rg_base' and 'memclock' in the * passed pm2fb_info structure. * init(): called immediately after the reset of the Permedia2 chip. * It should reset the memory controller if needed (the MClk * is set shortly afterwards by the caller). * cleanup(): called after the driver has been unregistered. * * the init and cleanup pointers can be NULL. */static const struct { int (*detect)(struct pm2fb_info*); void (*init)(struct pm2fb_info*); void (*cleanup)(struct pm2fb_info*); char name[32];} board_table[] = {#ifdef CONFIG_FB_PM2_PCI { pm2pci_detect, pm2pci_init, NULL, "Permedia2 PCI board" },#endif#ifdef CONFIG_FB_PM2_CVPPC { cvppc_detect, cvppc_init, NULL, "CVisionPPC/BVisionPPC" },#endif { NULL, }};/* * partial products for the supported horizontal resolutions. */#define PACKPP(p0,p1,p2) (((p2)<<6)|((p1)<<3)|(p0))static const struct { u16 width; u16 pp;} pp_table[] = { { 32, PACKPP(1, 0, 0) }, { 64, PACKPP(1, 1, 0) }, { 96, PACKPP(1, 1, 1) }, { 128, PACKPP(2, 1, 1) }, { 160, PACKPP(2, 2, 1) }, { 192, PACKPP(2, 2, 2) }, { 224, PACKPP(3, 2, 1) }, { 256, PACKPP(3, 2, 2) }, { 288, PACKPP(3, 3, 1) }, { 320, PACKPP(3, 3, 2) }, { 384, PACKPP(3, 3, 3) }, { 416, PACKPP(4, 3, 1) }, { 448, PACKPP(4, 3, 2) }, { 512, PACKPP(4, 3, 3) }, { 544, PACKPP(4, 4, 1) }, { 576, PACKPP(4, 4, 2) }, { 640, PACKPP(4, 4, 3) }, { 768, PACKPP(4, 4, 4) }, { 800, PACKPP(5, 4, 1) }, { 832, PACKPP(5, 4, 2) }, { 896, PACKPP(5, 4, 3) }, { 1024, PACKPP(5, 4, 4) }, { 1056, PACKPP(5, 5, 1) }, { 1088, PACKPP(5, 5, 2) }, { 1152, PACKPP(5, 5, 3) }, { 1280, PACKPP(5, 5, 4) }, { 1536, PACKPP(5, 5, 5) }, { 1568, PACKPP(6, 5, 1) }, { 1600, PACKPP(6, 5, 2) }, { 1664, PACKPP(6, 5, 3) }, { 1792, PACKPP(6, 5, 4) }, { 2048, PACKPP(6, 5, 5) }, { 0, 0 } };static void pm2fb_detect(void);static int pm2fb_encode_fix(struct fb_fix_screeninfo* fix, const void* par, struct fb_info_gen* info);static int pm2fb_decode_var(const struct fb_var_screeninfo* var, void* par, struct fb_info_gen* info);static int pm2fb_encode_var(struct fb_var_screeninfo* var, const void* par, struct fb_info_gen* info);static void pm2fb_get_par(void* par, struct fb_info_gen* info);static void pm2fb_set_par(const void* par, struct fb_info_gen* info);static int pm2fb_getcolreg(unsigned regno, unsigned* red, unsigned* green, unsigned* blue, unsigned* transp, struct fb_info* info);static int pm2fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info* info);static int pm2fb_blank(int blank_mode, struct fb_info_gen* info);static int pm2fb_pan_display(const struct fb_var_screeninfo* var, struct fb_info_gen* info);static void pm2fb_set_disp(const void* par, struct display* disp, struct fb_info_gen* info);static struct fbgen_hwswitch pm2fb_hwswitch={ pm2fb_detect, pm2fb_encode_fix, pm2fb_decode_var, pm2fb_encode_var, pm2fb_get_par, pm2fb_set_par, pm2fb_getcolreg, pm2fb_setcolreg, pm2fb_pan_display, pm2fb_blank, pm2fb_set_disp};static struct fb_ops pm2fb_ops={ owner: THIS_MODULE, fb_get_fix: fbgen_get_fix, fb_get_var: fbgen_get_var, fb_set_var: fbgen_set_var, fb_get_cmap: fbgen_get_cmap, fb_set_cmap: fbgen_set_cmap, fb_pan_display: fbgen_pan_display,};/*************************************************************************** * Begin of Permedia2 specific functions ***************************************************************************/inline static u32 RD32(unsigned char* base, s32 off) { return readl(base+off);}inline static void WR32(unsigned char* base, s32 off, u32 v) { writel(v, base+off);}inline static u32 pm2_RD(struct pm2fb_info* p, s32 off) { return RD32(p->regions.v_regs, off);}inline static void pm2_WR(struct pm2fb_info* p, s32 off, u32 v) { WR32(p->regions.v_regs, off, v);}inline static u32 pm2_RDAC_RD(struct pm2fb_info* p, s32 idx) { int index = PM2R_RD_INDEXED_DATA; switch (p->type) { case PM2_TYPE_PERMEDIA2: pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); break; case PM2_TYPE_PERMEDIA2V: pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); index = PM2VR_RD_INDEXED_DATA; break; } DEFRW(); return pm2_RD(p, index);}inline static void pm2_RDAC_WR(struct pm2fb_info* p, s32 idx, u32 v) { int index = PM2R_RD_INDEXED_DATA; switch (p->type) { case PM2_TYPE_PERMEDIA2: pm2_WR(p, PM2R_RD_PALETTE_WRITE_ADDRESS, idx); break; case PM2_TYPE_PERMEDIA2V: pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); index = PM2VR_RD_INDEXED_DATA; break; } DEFRW(); pm2_WR(p, index, v);}inline static u32 pm2v_RDAC_RD(struct pm2fb_info* p, s32 idx) { pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); DEFRW(); return pm2_RD(p, PM2VR_RD_INDEXED_DATA);}inline static void pm2v_RDAC_WR(struct pm2fb_info* p, s32 idx, u32 v) { pm2_WR(p, PM2VR_RD_INDEX_LOW, idx & 0xff); DEFRW(); pm2_WR(p, PM2VR_RD_INDEXED_DATA, v);}#ifdef CONFIG_FB_PM2_FIFO_DISCONNECT#define WAIT_FIFO(p,a)#elseinline static void WAIT_FIFO(struct pm2fb_info* p, u32 a) { while(pm2_RD(p, PM2R_IN_FIFO_SPACE)<a); DEFRW();}#endifstatic u32 partprod(u32 xres) { int i; for (i=0; pp_table[i].width && pp_table[i].width!=xres; i++); if (!pp_table[i].width) DPRINTK("invalid width %u\n", xres); return pp_table[i].pp;}static u32 to3264(u32 timing, int bpp, int is64) { switch (bpp) { case 8: timing=timing>>(2+is64); break; case 16: timing=timing>>(1+is64); break; case 24: timing=(timing*3)>>(2+is64); break; case 32: if (is64) timing=timing>>1; break; } return timing;}static u32 from3264(u32 timing, int bpp, int is64) { switch (bpp) { case 8: timing=timing<<(2+is64); break; case 16: timing=timing<<(1+is64); break; case 24: timing=(timing<<(2+is64))/3; break; case 32: if (is64) timing=timing<<1; break; } return timing;}static void pm2_mnp(u32 clk, unsigned char* mm, unsigned char* nn, unsigned char* pp) { unsigned char m; unsigned char n; unsigned char p; u32 f; s32 curr; s32 delta=100000; *mm=*nn=*pp=0; for (n=2; n<15; n++) { for (m=2; m; m++) { f=PM2_REFERENCE_CLOCK*m/n; if (f>=150000 && f<=300000) { for (p=0; p<5; p++, f>>=1) { curr=clk>f?clk-f:f-clk; if (curr<delta) { delta=curr; *mm=m; *nn=n; *pp=p; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -