/* * 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. * */ #include <linux/aperture.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/init.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 #include <video/vga.h>#include <video/cirrus.h> /***************************************************************** * * debugging and utility macros * */ /* disable runtime assertions? *//* #define CIRRUSFB_NDEBUG */ /* 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, /* GD5434 */ BT_PICCOLO, /* GD5426 */ BT_PICASSO, /* GD5426 or GD5428 */ BT_SPECTRUM, /* GD5426 or GD5428 */ BT_PICASSO4, /* GD5446 */ BT_ALPINE, /* GD543x/4x */ BT_GD5480, BT_LAGUNA, /* GD5462/64 */ BT_LAGUNAB, /* GD5465 */}; /* * 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 */ 135100, 135100, 85500, 85500, 0 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0xF0, .sr07_1bpp = 0xF0, .sr07_1bpp_mux = 0xF6, .sr07_8bpp = 0xF1, .sr07_8bpp_mux = 0xF7, .sr1f = 0x1E }, [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 = 0xA0, .sr07_1bpp = 0xA0, .sr07_1bpp_mux = 0xA6, .sr07_8bpp = 0xA1, .sr07_8bpp_mux = 0xA7, .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 = 0xA0, .sr07_1bpp_mux = 0xA6, .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 = { /* taken from X11 code */ 170000, 170000, 170000, 170000, 135100, }, .init_sr07 = false, .init_sr1f = false, .scrn_start_bit19 = true, }, [BT_LAGUNAB] = { .name = "CL Laguna AGP", .maxclock = { /* taken from X11 code */ 170000, 250000, 170000, 170000, 135100, }, .init_sr07 = false, .init_sr1f = false, .scrn_start_bit19 = true, }}; #ifdef CONFIG_PCI#define CHIP(id, btype) \ { PCI_VDEVICE(CIRRUS, id), .driver_data = (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_SD64), CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64), 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_LAGUNAB), /* CL Laguna 3DA*/ { 0, }};MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);#undef CHIP#endif /* CONFIG_PCI */ #ifdef CONFIG_ZORROstruct zorrocl { enum cirrus_board type; /* Board type */ u32 regoffset; /* Offset of registers in first Zorro device */ u32 ramsize; /* Size of video RAM in first Zorro device */ /* If zero, use autoprobe on RAM device */ u32 ramoffset; /* Offset of video RAM in first Zorro device */ zorro_id ramid; /* Zorro ID of RAM device */ zorro_id ramid2; /* Zorro ID of optional second RAM device */}; static const struct zorrocl zcl_sd64 = { .type = BT_SD64, .ramid = ZORRO_PROD_HELFRICH_SD64_RAM,}; static const struct zorrocl zcl_piccolo = { .type = BT_PICCOLO, .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,}; static const struct zorrocl zcl_picasso = { .type = BT_PICASSO, .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,}; static const struct zorrocl zcl_spectrum = { .type = BT_SPECTRUM, .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,}; static const struct zorrocl zcl_picasso4_z3 = { .type = BT_PICASSO4, .regoffset = 0x00600000, .ramsize = 4 * MB_, .ramoffset = 0x01000000, /* 0x02000000 for 64 MiB boards */}; static const struct zorrocl zcl_picasso4_z2 = { .type = BT_PICASSO4, .regoffset = 0x10000, .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1, .ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2,}; static const struct zorro_device_id cirrusfb_zorro_table[] = { { .id = ZORRO_PROD_HELFRICH_SD64_REG, .driver_data = (unsigned long)&zcl_sd64, }, { .id = ZORRO_PROD_HELFRICH_PICCOLO_REG, .driver_data = (unsigned long)&zcl_piccolo, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG, .driver_data = (unsigned long)&zcl_picasso, }, { .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG, .driver_data = (unsigned long)&zcl_spectrum, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3, .driver_data = (unsigned long)&zcl_picasso4_z3, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG, .driver_data = (unsigned long)&zcl_picasso4_z2, }, { 0 }};MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);#endif /* CONFIG_ZORRO */ #ifdef CIRRUSFB_DEBUGenum cirrusfb_dbg_reg_class { CRT, SEQ};#endif /* CIRRUSFB_DEBUG */ /* info about board */struct cirrusfb_info { u8 __iomem *regbase; u8 __iomem *laguna_mmio; enum cirrus_board btype; unsigned char SFR; /* Shadow of special function register */ int multiplexing; int doubleVCLK; int blank_mode; u32 pseudo_palette[16]; void (*unmap)(struct fb_info *info);}; static bool noaccel;static char *mode_option = "640x480@60"; /****************************************************************************//**** BEGIN PROTOTYPES ******************************************************/ /*--- Interface used by the world ------------------------------------------*/static int cirrusfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); /*--- 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, u32 fg_color, u32 bg_color, u_short line_length, u_char blitmode); static void bestclock(long freq, int *nom, int *den, int *div); #ifdef CIRRUSFB_DEBUGstatic void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);static void cirrusfb_dbg_print_regs(struct fb_info *info, caddr_t regbase, enum cirrusfb_dbg_reg_class reg_class, ...);#endif /* CIRRUSFB_DEBUG */ /*** END PROTOTYPES ********************************************************//*****************************************************************************//*** BEGIN Interface Used by the World ***************************************/ static inline int is_laguna(const struct cirrusfb_info *cinfo){ return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;} 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 fb_info *info, long freq){ struct cirrusfb_info *cinfo = info->par; long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f; /* Read MCLK value */ mclk = (14318 * mclk) >> 3; dev_dbg(info->device, "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) { dev_dbg(info->device, "Using VCLK = MCLK\n"); return 1; } else if (abs(freq - (mclk / 2)) < 250) { dev_dbg(info->device, "Using VCLK = MCLK/2\n"); return 2; } return 0;} static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var, struct fb_info *info){ long freq; long maxclock; struct cirrusfb_info *cinfo = info->par; unsigned maxclockidx = var->bits_per_pixel >> 3; /* convert from ps to kHz */ freq = PICOS2KHZ(var->pixclock ? : 1); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; cinfo->multiplexing = 0; /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ if (freq > maxclock) { var->pixclock = KHZ2PICOS(maxclock); while ((freq = PICOS2KHZ(var->pixclock)) > maxclock) var->pixclock++; } dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); /* * Additional constraint: 8bpp uses DAC clock doubling to allow maximum * pixel clock */Lines 1–500 of 2954. Load more
/* * 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. * */ #include <linux/aperture.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/delay.h> #include <linux/fb.h> #include <linux/init.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 #include <video/vga.h> #include <video/cirrus.h> /***************************************************************** * * debugging and utility macros * */ /* disable runtime assertions? */ /* #define CIRRUSFB_NDEBUG */ /* 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, /* GD5434 */ BT_PICCOLO, /* GD5426 */ BT_PICASSO, /* GD5426 or GD5428 */ BT_SPECTRUM, /* GD5426 or GD5428 */ BT_PICASSO4, /* GD5446 */ BT_ALPINE, /* GD543x/4x */ BT_GD5480, BT_LAGUNA, /* GD5462/64 */ BT_LAGUNAB, /* GD5465 */ }; /* * 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 */ 135100, 135100, 85500, 85500, 0 }, .init_sr07 = true, .init_sr1f = true, .scrn_start_bit19 = true, .sr07 = 0xF0, .sr07_1bpp = 0xF0, .sr07_1bpp_mux = 0xF6, .sr07_8bpp = 0xF1, .sr07_8bpp_mux = 0xF7, .sr1f = 0x1E }, [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 = 0xA0, .sr07_1bpp = 0xA0, .sr07_1bpp_mux = 0xA6, .sr07_8bpp = 0xA1, .sr07_8bpp_mux = 0xA7, .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 = 0xA0, .sr07_1bpp_mux = 0xA6, .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 = { /* taken from X11 code */ 170000, 170000, 170000, 170000, 135100, }, .init_sr07 = false, .init_sr1f = false, .scrn_start_bit19 = true, }, [BT_LAGUNAB] = { .name = "CL Laguna AGP", .maxclock = { /* taken from X11 code */ 170000, 250000, 170000, 170000, 135100, }, .init_sr07 = false, .init_sr1f = false, .scrn_start_bit19 = true, } }; #ifdef CONFIG_PCI #define CHIP(id, btype) \ { PCI_VDEVICE(CIRRUS, id), .driver_data = (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_SD64), CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64), 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_LAGUNAB), /* CL Laguna 3DA*/ { 0, } }; MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table); #undef CHIP #endif /* CONFIG_PCI */ #ifdef CONFIG_ZORRO struct zorrocl { enum cirrus_board type; /* Board type */ u32 regoffset; /* Offset of registers in first Zorro device */ u32 ramsize; /* Size of video RAM in first Zorro device */ /* If zero, use autoprobe on RAM device */ u32 ramoffset; /* Offset of video RAM in first Zorro device */ zorro_id ramid; /* Zorro ID of RAM device */ zorro_id ramid2; /* Zorro ID of optional second RAM device */ }; static const struct zorrocl zcl_sd64 = { .type = BT_SD64, .ramid = ZORRO_PROD_HELFRICH_SD64_RAM, }; static const struct zorrocl zcl_piccolo = { .type = BT_PICCOLO, .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM, }; static const struct zorrocl zcl_picasso = { .type = BT_PICASSO, .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM, }; static const struct zorrocl zcl_spectrum = { .type = BT_SPECTRUM, .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM, }; static const struct zorrocl zcl_picasso4_z3 = { .type = BT_PICASSO4, .regoffset = 0x00600000, .ramsize = 4 * MB_, .ramoffset = 0x01000000, /* 0x02000000 for 64 MiB boards */ }; static const struct zorrocl zcl_picasso4_z2 = { .type = BT_PICASSO4, .regoffset = 0x10000, .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1, .ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2, }; static const struct zorro_device_id cirrusfb_zorro_table[] = { { .id = ZORRO_PROD_HELFRICH_SD64_REG, .driver_data = (unsigned long)&zcl_sd64, }, { .id = ZORRO_PROD_HELFRICH_PICCOLO_REG, .driver_data = (unsigned long)&zcl_piccolo, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG, .driver_data = (unsigned long)&zcl_picasso, }, { .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG, .driver_data = (unsigned long)&zcl_spectrum, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3, .driver_data = (unsigned long)&zcl_picasso4_z3, }, { .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG, .driver_data = (unsigned long)&zcl_picasso4_z2, }, { 0 } }; MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table); #endif /* CONFIG_ZORRO */ #ifdef CIRRUSFB_DEBUG enum cirrusfb_dbg_reg_class { CRT, SEQ }; #endif /* CIRRUSFB_DEBUG */ /* info about board */ struct cirrusfb_info { u8 __iomem *regbase; u8 __iomem *laguna_mmio; enum cirrus_board btype; unsigned char SFR; /* Shadow of special function register */ int multiplexing; int doubleVCLK; int blank_mode; u32 pseudo_palette[16]; void (*unmap)(struct fb_info *info); }; static bool noaccel; static char *mode_option = "640x480@60"; /****************************************************************************/ /**** BEGIN PROTOTYPES ******************************************************/ /*--- Interface used by the world ------------------------------------------*/ static int cirrusfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info); /*--- 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 0 static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red, unsigned char *green, unsigned char *blue); #endif static 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, u32 fg_color, u32 bg_color, u_short line_length, u_char blitmode); static void bestclock(long freq, int *nom, int *den, int *div); #ifdef CIRRUSFB_DEBUG static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase); static void cirrusfb_dbg_print_regs(struct fb_info *info, caddr_t regbase, enum cirrusfb_dbg_reg_class reg_class, ...); #endif /* CIRRUSFB_DEBUG */ /*** END PROTOTYPES ********************************************************/ /*****************************************************************************/ /*** BEGIN Interface Used by the World ***************************************/ static inline int is_laguna(const struct cirrusfb_info *cinfo) { return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB; } 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 fb_info *info, long freq) { struct cirrusfb_info *cinfo = info->par; long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f; /* Read MCLK value */ mclk = (14318 * mclk) >> 3; dev_dbg(info->device, "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) { dev_dbg(info->device, "Using VCLK = MCLK\n"); return 1; } else if (abs(freq - (mclk / 2)) < 250) { dev_dbg(info->device, "Using VCLK = MCLK/2\n"); return 2; } return 0; } static int cirrusfb_check_pixclock(struct fb_var_screeninfo *var, struct fb_info *info) { long freq; long maxclock; struct cirrusfb_info *cinfo = info->par; unsigned maxclockidx = var->bits_per_pixel >> 3; /* convert from ps to kHz */ freq = PICOS2KHZ(var->pixclock ? : 1); maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx]; cinfo->multiplexing = 0; /* If the frequency is greater than we can support, we might be able * to use multiplexing for the video mode */ if (freq > maxclock) { var->pixclock = KHZ2PICOS(maxclock); while ((freq = PICOS2KHZ(var->pixclock)) > maxclock) var->pixclock++; } dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq); /* * Additional constraint: 8bpp uses DAC clock doubling to allow maximum * pixel clock */