📄 cirrusfb.c
字号:
/* * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets * * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> * * Contributors (thanks, all!) * * David Eger: * Overhaul for Linux 2.6 * * Jeff Rugen: * Major contributions; Motorola PowerStack (PPC and PCI) support, * GD54xx, 1280x1024 mode support, change MCLK based on VCLK. * * Geert Uytterhoeven: * Excellent code review. * * Lars Hecking: * Amiga updates and testing. * * Original cirrusfb author: Frank Neumann * * Based on retz3fb.c and cirrusfb.c: * Copyright (C) 1997 Jes Sorensen * Copyright (C) 1996 Frank Neumann * *************************************************************** * * Format this code with GNU indent '-kr -i8 -pcs' options. * * 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. * */#define CIRRUSFB_VERSION "2.0-pre2"#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/init.h>#include <asm/pgtable.h>#ifdef CONFIG_ZORRO#include <linux/zorro.h>#endif#ifdef CONFIG_PCI#include <linux/pci.h>#endif#ifdef CONFIG_AMIGA#include <asm/amigahw.h>#endif#ifdef CONFIG_PPC_PREP#include <asm/machdep.h>#define isPReP machine_is(prep)#else#define isPReP 0#endif#include <video/vga.h>#include <video/cirrus.h>/***************************************************************** * * debugging and utility macros * *//* enable debug output? *//* #define CIRRUSFB_DEBUG 1 *//* disable runtime assertions? *//* #define CIRRUSFB_NDEBUG *//* debug output */#ifdef CIRRUSFB_DEBUG#define DPRINTK(fmt, args...) \ printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)#else#define DPRINTK(fmt, args...)#endif/* debugging assertions */#ifndef CIRRUSFB_NDEBUG#define assert(expr) \ if (!(expr)) { \ printk("Assertion failed! %s,%s,%s,line=%d\n", \ #expr, __FILE__, __func__, __LINE__); \ }#else#define assert(expr)#endif#define MB_ (1024 * 1024)/***************************************************************** * * chipset information * *//* board types */enum cirrus_board { BT_NONE = 0, BT_SD64, BT_PICCOLO, BT_PICASSO, BT_SPECTRUM, BT_PICASSO4, /* GD5446 */ BT_ALPINE, /* GD543x/4x */ BT_GD5480, BT_LAGUNA, /* GD546x */};/* * per-board-type information, used for enumerating and abstracting * chip-specific information * NOTE: MUST be in the same order as enum cirrus_board in order to * use direct indexing on this array * NOTE: '__initdata' cannot be used as some of this info * is required at runtime. Maybe separate into an init-only and * a run-time table? */static const struct cirrusfb_board_info_rec { char *name; /* ASCII name of chipset */ long maxclock[5]; /* maximum video clock */ /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */ bool init_sr07 : 1; /* init SR07 during init_vgachip() */ bool init_sr1f : 1; /* write SR1F during init_vgachip() */ /* construct bit 19 of screen start address */ bool scrn_start_bit19 : 1; /* initial SR07 value, then for each mode */ unsigned char sr07; unsigned char sr07_1bpp; unsigned char sr07_1bpp_mux; unsigned char sr07_8bpp; unsigned char sr07_8bpp_mux; unsigned char sr1f; /* SR1F VGA initial register value */} cirrusfb_board_info[] = { [BT_SD64] = { .name = "CL SD64", .maxclock = { /* guess */ /* the SD64/P4 have a higher max. videoclock */ 140000, 140000, 140000, 140000, 140000, }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0xF0, .sr07_1bpp = 0xF0, .sr07_8bpp = 0xF1, .sr1f = 0x20 }, [BT_PICCOLO] = { .name = "CL Piccolo", .maxclock = { /* guess */ 90000, 90000, 90000, 90000, 90000 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = false, .sr07 = 0x80, .sr07_1bpp = 0x80, .sr07_8bpp = 0x81, .sr1f = 0x22 }, [BT_PICASSO] = { .name = "CL Picasso", .maxclock = { /* guess */ 90000, 90000, 90000, 90000, 90000 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = false, .sr07 = 0x20, .sr07_1bpp = 0x20, .sr07_8bpp = 0x21, .sr1f = 0x22 }, [BT_SPECTRUM] = { .name = "CL Spectrum", .maxclock = { /* guess */ 90000, 90000, 90000, 90000, 90000 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = false, .sr07 = 0x80, .sr07_1bpp = 0x80, .sr07_8bpp = 0x81, .sr1f = 0x22 }, [BT_PICASSO4] = { .name = "CL Picasso4", .maxclock = { 135100, 135100, 85500, 85500, 0 }, .init_sr07 = true, .init_sr1f = false, .scrn_start_bit19 = true, .sr07 = 0x20, .sr07_1bpp = 0x20, .sr07_8bpp = 0x21, .sr1f = 0 }, [BT_ALPINE] = { .name = "CL Alpine", .maxclock = { /* for the GD5430. GD5446 can do more... */ 85500, 85500, 50000, 28500, 0 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0xA0, .sr07_1bpp = 0xA1, .sr07_1bpp_mux = 0xA7, .sr07_8bpp = 0xA1, .sr07_8bpp_mux = 0xA7, .sr1f = 0x1C }, [BT_GD5480] = { .name = "CL GD5480", .maxclock = { 135100, 200000, 200000, 135100, 135100 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0x10, .sr07_1bpp = 0x11, .sr07_8bpp = 0x11, .sr1f = 0x1C }, [BT_LAGUNA] = { .name = "CL Laguna", .maxclock = { /* guess */ 135100, 135100, 135100, 135100, 135100, }, .init_sr07 = false, .init_sr1f = false, .scrn_start_bit19 = true, }};#ifdef CONFIG_PCI#define CHIP(id, btype) \ { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }static struct pci_device_id cirrusfb_pci_table[] = { CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */ CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE), CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */ CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */ CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */ CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */ CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/ { 0, }};MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);#undef CHIP#endif /* CONFIG_PCI */#ifdef CONFIG_ZORROstatic const struct zorro_device_id cirrusfb_zorro_table[] = { { .id = ZORRO_PROD_HELFRICH_SD64_RAM, .driver_data = BT_SD64, }, { .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM, .driver_data = BT_PICCOLO, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM, .driver_data = BT_PICASSO, }, { .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM, .driver_data = BT_SPECTRUM, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3, .driver_data = BT_PICASSO4, }, { 0 }};static const struct { zorro_id id2; unsigned long size;} cirrusfb_zorro_table2[] = { [BT_SD64] = { .id2 = ZORRO_PROD_HELFRICH_SD64_REG, .size = 0x400000 }, [BT_PICCOLO] = { .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG, .size = 0x200000 }, [BT_PICASSO] = { .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG, .size = 0x200000 }, [BT_SPECTRUM] = { .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG, .size = 0x200000 }, [BT_PICASSO4] = { .id2 = 0, .size = 0x400000 }};#endif /* CONFIG_ZORRO */struct cirrusfb_regs { int multiplexing;};#ifdef CIRRUSFB_DEBUGenum cirrusfb_dbg_reg_class { CRT, SEQ};#endif /* CIRRUSFB_DEBUG *//* info about board */struct cirrusfb_info { u8 __iomem *regbase; enum cirrus_board btype; unsigned char SFR; /* Shadow of special function register */ struct cirrusfb_regs currentmode; int blank_mode; u32 pseudo_palette[16]; void (*unmap)(struct fb_info *info);};static int noaccel __devinitdata;static char *mode_option __devinitdata = "640x480@60";/****************************************************************************//**** BEGIN PROTOTYPES ******************************************************//*--- Interface used by the world ------------------------------------------*/static int cirrusfb_init(void);#ifndef MODULEstatic int cirrusfb_setup(char *options);#endifstatic int cirrusfb_open(struct fb_info *info, int user);static int cirrusfb_release(struct fb_info *info, int user);static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info);static int cirrusfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info);static int cirrusfb_set_par(struct fb_info *info);static int cirrusfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);static int cirrusfb_blank(int blank_mode, struct fb_info *info);static void cirrusfb_fillrect(struct fb_info *info, const struct fb_fillrect *region);static void cirrusfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);static void cirrusfb_imageblit(struct fb_info *info, const struct fb_image *image);/* function table of the above functions */static struct fb_ops cirrusfb_ops = { .owner = THIS_MODULE, .fb_open = cirrusfb_open, .fb_release = cirrusfb_release, .fb_setcolreg = cirrusfb_setcolreg, .fb_check_var = cirrusfb_check_var, .fb_set_par = cirrusfb_set_par, .fb_pan_display = cirrusfb_pan_display, .fb_blank = cirrusfb_blank, .fb_fillrect = cirrusfb_fillrect, .fb_copyarea = cirrusfb_copyarea, .fb_imageblit = cirrusfb_imageblit,};/*--- Internal routines ----------------------------------------------------*/static void init_vgachip(struct fb_info *info);static void switch_monitor(struct cirrusfb_info *cinfo, int on);static void WGen(const struct cirrusfb_info *cinfo, int regnum, unsigned char val);static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);static void AttrOn(const struct cirrusfb_info *cinfo);static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red, unsigned char green, unsigned char blue);#if 0static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red, unsigned char *green, unsigned char *blue);#endifstatic void cirrusfb_WaitBLT(u8 __iomem *regbase);static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel, u_short curx, u_short cury, u_short destx, u_short desty, u_short width, u_short height, u_short line_length);static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel, u_short x, u_short y, u_short width, u_short height, u_char color, u_short line_length);static void bestclock(long freq, int *nom, int *den, int *div);#ifdef CIRRUSFB_DEBUGstatic void cirrusfb_dump(void);static void cirrusfb_dbg_reg_dump(caddr_t regbase);static void cirrusfb_dbg_print_regs(caddr_t regbase, enum cirrusfb_dbg_reg_class reg_class, ...);static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);#endif /* CIRRUSFB_DEBUG *//*** END PROTOTYPES ********************************************************//*****************************************************************************//*** BEGIN Interface Used by the World ***************************************/static int opencount;/*--- Open /dev/fbx ---------------------------------------------------------*/static int cirrusfb_open(struct fb_info *info, int user){ if (opencount++ == 0) switch_monitor(info->par, 1); return 0;}/*--- Close /dev/fbx --------------------------------------------------------*/static int cirrusfb_release(struct fb_info *info, int user){ if (--opencount == 0) switch_monitor(info->par, 0); return 0;}/**** END Interface used by the World *************************************//****************************************************************************//**** BEGIN Hardware specific Routines **************************************//* Check if the MCLK is not a better clock source */static int cirrusfb_check_mclk(struct cirrusfb_info *cinfo, long freq){ long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f; /* Read MCLK value */ mclk = (14318 * mclk) >> 3; DPRINTK("Read MCLK of %ld kHz\n", mclk); /* Determine if we should use MCLK instead of VCLK, and if so, what we * should divide it by to get VCLK */ if (abs(freq - mclk) < 250) { DPRINTK("Using VCLK = MCLK\n"); return 1; } else if (abs(freq - (mclk / 2)) < 250) { DPRINTK("Using VCLK = MCLK/2\n"); return 2; } return 0;}static int cirrusfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info){ int yres; /* memory size in pixels */ unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; switch (var->bits_per_pixel) { case 1: pixels /= 4; break; /* 8 pixel per byte, only 1/4th of mem usable */ case 8: case 16: case 32: break; /* 1 pixel == 1 byte */ default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -