aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/cirrusfb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/cirrusfb.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/video/cirrusfb.c')
-rw-r--r--drivers/video/cirrusfb.c3326
1 files changed, 3326 insertions, 0 deletions
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c
new file mode 100644
index 00000000000..a3040429c27
--- /dev/null
+++ b/drivers/video/cirrusfb.c
@@ -0,0 +1,3326 @@
+/*
+ * 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/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/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/selection.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/processor.h>
+#define isPReP (_machine == _MACH_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, __FUNCTION__ , ## 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__,__FUNCTION__,__LINE__); \
+ }
+#else
+#define assert(expr)
+#endif
+
+#ifdef TRUE
+#undef TRUE
+#endif
+#ifdef FALSE
+#undef FALSE
+#endif
+#define TRUE 1
+#define FALSE 0
+
+#define MB_ (1024*1024)
+#define KB_ (1024)
+
+#define MAX_NUM_BOARDS 7
+
+
+/*****************************************************************
+ *
+ * chipset information
+ *
+ */
+
+/* board types */
+typedef enum {
+ BT_NONE = 0,
+ BT_SD64,
+ BT_PICCOLO,
+ BT_PICASSO,
+ BT_SPECTRUM,
+ BT_PICASSO4, /* GD5446 */
+ BT_ALPINE, /* GD543x/4x */
+ BT_GD5480,
+ BT_LAGUNA, /* GD546x */
+} cirrusfb_board_t;
+
+
+/*
+ * per-board-type information, used for enumerating and abstracting
+ * chip-specific information
+ * NOTE: MUST be in the same order as cirrusfb_board_t 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 */
+ unsigned init_sr07 : 1; /* init SR07 during init_vgachip() */
+ unsigned init_sr1f : 1; /* write SR1F during init_vgachip() */
+ unsigned scrn_start_bit19 : 1; /* construct bit 19 of screen start address */
+
+ /* 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, PCI_DEVICE_ID_##id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
+
+static struct pci_device_id cirrusfb_pci_table[] = {
+ CHIP( CIRRUS_5436, BT_ALPINE ),
+ CHIP( CIRRUS_5434_8, BT_ALPINE ),
+ CHIP( CIRRUS_5434_4, BT_ALPINE ),
+ CHIP( CIRRUS_5430, BT_ALPINE ), /* GD-5440 has identical id */
+ CHIP( CIRRUS_7543, BT_ALPINE ),
+ CHIP( CIRRUS_7548, BT_ALPINE ),
+ CHIP( CIRRUS_5480, BT_GD5480 ), /* MacPicasso probably */
+ CHIP( CIRRUS_5446, BT_PICASSO4 ), /* Picasso 4 is a GD5446 */
+ CHIP( CIRRUS_5462, BT_LAGUNA ), /* CL Laguna */
+ CHIP( CIRRUS_5464, BT_LAGUNA ), /* CL Laguna 3D */
+ CHIP( CIRRUS_5465, BT_LAGUNA ), /* CL Laguna 3DA*/
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
+#undef CHIP
+#endif /* CONFIG_PCI */
+
+
+#ifdef CONFIG_ZORRO
+static 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 {
+ __u32 line_length; /* in BYTES! */
+ __u32 visual;
+ __u32 type;
+
+ long freq;
+ long nom;
+ long den;
+ long div;
+ long multiplexing;
+ long mclk;
+ long divMCLK;
+
+ long HorizRes; /* The x resolution in pixel */
+ long HorizTotal;
+ long HorizDispEnd;
+ long HorizBlankStart;
+ long HorizBlankEnd;
+ long HorizSyncStart;
+ long HorizSyncEnd;
+
+ long VertRes; /* the physical y resolution in scanlines */
+ long VertTotal;
+ long VertDispEnd;
+ long VertSyncStart;
+ long VertSyncEnd;
+ long VertBlankStart;
+ long VertBlankEnd;
+};
+
+
+
+#ifdef CIRRUSFB_DEBUG
+typedef enum {
+ CRT,
+ SEQ
+} cirrusfb_dbg_reg_class_t;
+#endif /* CIRRUSFB_DEBUG */
+
+
+
+
+/* info about board */
+struct cirrusfb_info {
+ struct fb_info *info;
+
+ u8 __iomem *fbmem;
+ u8 __iomem *regbase;
+ u8 __iomem *mem;
+ unsigned long size;
+ cirrusfb_board_t btype;
+ unsigned char SFR; /* Shadow of special function register */
+
+ unsigned long fbmem_phys;
+ unsigned long fbregs_phys;
+
+ struct cirrusfb_regs currentmode;
+ int blank_mode;
+
+ u32 pseudo_palette[17];
+ struct { u8 red, green, blue, pad; } palette[256];
+
+#ifdef CONFIG_ZORRO
+ struct zorro_dev *zdev;
+#endif
+#ifdef CONFIG_PCI
+ struct pci_dev *pdev;
+#endif
+ void (*unmap)(struct cirrusfb_info *cinfo);
+};
+
+
+static unsigned cirrusfb_def_mode = 1;
+static int noaccel = 0;
+
+/*
+ * Predefined Video Modes
+ */
+
+static const struct {
+ const char *name;
+ struct fb_var_screeninfo var;
+} cirrusfb_predefined[] = {
+ {
+ /* autodetect mode */
+ .name = "Autodetect",
+ }, {
+ /* 640x480, 31.25 kHz, 60 Hz, 25 MHz PixClock */
+ .name = "640x480",
+ .var = {
+ .xres = 640,
+ .yres = 480,
+ .xres_virtual = 640,
+ .yres_virtual = 480,
+ .bits_per_pixel = 8,
+ .red = { .length = 8 },
+ .green = { .length = 8 },
+ .blue = { .length = 8 },
+ .width = -1,
+ .height = -1,
+ .pixclock = 40000,
+ .left_margin = 48,
+ .right_margin = 16,
+ .upper_margin = 32,
+ .lower_margin = 8,
+ .hsync_len = 96,
+ .vsync_len = 4,
+ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
+ .vmode = FB_VMODE_NONINTERLACED
+ }
+ }, {
+ /* 800x600, 48 kHz, 76 Hz, 50 MHz PixClock */
+ .name = "800x600",
+ .var = {
+ .xres = 800,
+ .yres = 600,
+ .xres_virtual = 800,
+ .yres_virtual = 600,
+ .bits_per_pixel = 8,
+ .red = { .length = 8 },
+ .green = { .length = 8 },
+ .blue = { .length = 8 },
+ .width = -1,
+ .height = -1,
+ .pixclock = 20000,
+ .left_margin = 128,
+ .right_margin = 16,
+ .upper_margin = 24,
+ .lower_margin = 2,
+ .hsync_len = 96,
+ .vsync_len = 6,
+ .vmode = FB_VMODE_NONINTERLACED
+ }
+ }, {
+ /*
+ * Modeline from XF86Config:
+ * Mode "1024x768" 80 1024 1136 1340 1432 768 770 774 805
+ */
+ /* 1024x768, 55.8 kHz, 70 Hz, 80 MHz PixClock */
+ .name = "1024x768",
+ .var = {
+ .xres = 1024,
+ .yres = 768,
+ .xres_virtual = 1024,
+ .yres_virtual = 768,
+ .bits_per_pixel = 8,
+ .red = { .length = 8 },
+ .green = { .length = 8 },
+ .blue = { .length = 8 },
+ .width = -1,
+ .height = -1,
+ .pixclock = 12500,
+ .left_margin = 144,
+ .right_margin = 32,
+ .upper_margin = 30,
+ .lower_margin = 2,
+ .hsync_len = 192,
+ .vsync_len = 6,
+ .vmode = FB_VMODE_NONINTERLACED
+ }
+ }
+};
+
+#define NUM_TOTAL_MODES ARRAY_SIZE(cirrusfb_predefined)
+
+/****************************************************************************/
+/**** BEGIN PROTOTYPES ******************************************************/
+
+
+/*--- Interface used by the world ------------------------------------------*/
+static int cirrusfb_init (void);
+#ifndef MODULE
+static int cirrusfb_setup (char *options);
+#endif
+
+static 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,
+ .fb_cursor = soft_cursor,
+};
+
+/*--- Hardware Specific Routines -------------------------------------------*/
+static int cirrusfb_decode_var (const struct fb_var_screeninfo *var,
+ struct cirrusfb_regs *regs,
+ const struct fb_info *info);
+/*--- Internal routines ----------------------------------------------------*/
+static void init_vgachip (struct cirrusfb_info *cinfo);
+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,
+ u_char color, u_short line_length);
+
+static void bestclock (long freq, long *best,
+ long *nom, long *den,
+ long *div, long maxfreq);
+
+#ifdef CIRRUSFB_DEBUG
+static void cirrusfb_dump (void);
+static void cirrusfb_dbg_reg_dump (caddr_t regbase);
+static void cirrusfb_dbg_print_regs (caddr_t regbase, cirrusfb_dbg_reg_class_t 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 = 0;
+
+/*--- 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 **************************************/
+
+/* Get a good MCLK value */
+static long cirrusfb_get_mclk (long freq, int bpp, long *div)
+{
+ long mclk;
+
+ assert (div != NULL);
+
+ /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
+ * Assume a 64-bit data path for now. The formula is:
+ * ((B * PCLK * 2)/W) * 1.2
+ * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
+ mclk = ((bpp / 8) * freq * 2) / 4;
+ mclk = (mclk * 12) / 10;
+ if (mclk < 50000)
+ mclk = 50000;
+ DPRINTK ("Use MCLK of %ld kHz\n", mclk);
+
+ /* Calculate value for SR1F. Multiply by 2 so we can round up. */
+ mclk = ((mclk * 16) / 14318);
+ mclk = (mclk + 1) / 2;
+ DPRINTK ("Set SR1F[5:0] to 0x%lx\n", mclk);
+
+ /* Determine if we should use MCLK instead of VCLK, and if so, what we
+ * should divide it by to get VCLK */
+ switch (freq) {
+ case 24751 ... 25249:
+ *div = 2;
+ DPRINTK ("Using VCLK = MCLK/2\n");
+ break;
+ case 49501 ... 50499:
+ *div = 1;
+ DPRINTK ("Using VCLK = MCLK\n");
+ break;
+ default:
+ *div = 0;
+ break;
+ }
+
+ return mclk;
+}
+
+static int cirrusfb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct cirrusfb_info *cinfo = info->par;
+ int nom, den; /* translyting from pixels->bytes */
+ int yres, i;
+ static struct { int xres, yres; } modes[] =
+ { { 1600, 1280 },
+ { 1280, 1024 },
+ { 1024, 768 },
+ { 800, 600 },
+ { 640, 480 },
+ { -1, -1 } };
+
+ switch (var->bits_per_pixel) {
+ case 0 ... 1:
+ var->bits_per_pixel = 1;
+ nom = 4;
+ den = 8;
+ break; /* 8 pixel per byte, only 1/4th of mem usable */
+ case 2 ... 8:
+ var->bits_per_pixel = 8;
+ nom = 1;
+ den = 1;
+ break; /* 1 pixel == 1 byte */
+ case 9 ... 16:
+ var->bits_per_pixel = 16;
+ nom = 2;
+ den = 1;
+ break; /* 2 bytes per pixel */
+ case 17 ... 24:
+ var->bits_per_pixel = 24;
+ nom = 3;
+ den = 1;
+ break; /* 3 bytes per pixel */
+ case 25 ... 32:
+ var->bits_per_pixel = 32;
+ nom = 4;
+ den = 1;
+ break; /* 4 bytes per pixel */
+ default:
+ printk ("cirrusfb: mode %dx%dx%d rejected...color depth not supported.\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ DPRINTK ("EXIT - EINVAL error\n");
+ return -EINVAL;
+ }
+
+ if (var->xres * nom / den * var->yres > cinfo->size) {
+ printk ("cirrusfb: mode %dx%dx%d rejected...resolution too high to fit into video memory!\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ DPRINTK ("EXIT - EINVAL error\n");
+ return -EINVAL;
+ }
+
+ /* use highest possible virtual resolution */
+ if (var->xres_virtual == -1 &&
+ var->yres_virtual == -1) {
+ printk ("cirrusfb: using maximum available virtual resolution\n");
+ for (i = 0; modes[i].xres != -1; i++) {
+ if (modes[i].xres * nom / den * modes[i].yres < cinfo->size / 2)
+ break;
+ }
+ if (modes[i].xres == -1) {
+ printk ("cirrusfb: could not find a virtual resolution that fits into video memory!!\n");
+ DPRINTK ("EXIT - EINVAL error\n");
+ return -EINVAL;
+ }
+ var->xres_virtual = modes[i].xres;
+ var->yres_virtual = modes[i].yres;
+
+ printk ("cirrusfb: virtual resolution set to maximum of %dx%d\n",
+ var->xres_virtual, var->yres_virtual);
+ }
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (var->xoffset < 0)
+ var->xoffset = 0;
+ if (var->yoffset < 0)
+ var->yoffset = 0;
+
+ /* truncate xoffset and yoffset to maximum if too high */
+ if (var->xoffset > var->xres_virtual - var->xres)
+ var->xoffset = var->xres_virtual - var->xres - 1;
+ if (var->yoffset > var->yres_virtual - var->yres)
+ var->yoffset = var->yres_virtual - var->yres - 1;
+
+ switch (var->bits_per_pixel) {
+ case 1:
+ var->red.offset = 0;
+ var->red.length = 1;
+ var->green.offset = 0;
+ var->green.length = 1;
+ var->blue.offset = 0;
+ var->blue.length = 1;
+ break;
+
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 6;
+ var->green.offset = 0;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 6;
+ break;
+
+ case 16:
+ if(isPReP) {
+ var->red.offset = 2;
+ var->green.offset = -3;
+ var->blue.offset = 8;
+ } else {
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ }
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ break;
+
+ case 24:
+ if(isPReP) {
+ var->red.offset = 8;
+ var->green.offset = 16;
+ var->blue.offset = 24;
+ } else {
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ }
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+
+ case 32:
+ if(isPReP) {
+ var->red.offset = 8;
+ var->green.offset = 16;
+ var->blue.offset = 24;
+ } else {
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ }
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ break;
+
+ default:
+ DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
+ assert (FALSE);
+ /* should never occur */
+ break;
+ }
+
+ var->red.msb_right =
+ var->green.msb_right =
+ var->blue.msb_right =
+ var->transp.offset =
+ var->transp.length =
+ var->transp.msb_right = 0;
+
+ yres = var->yres;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ yres *= 2;
+ else if (var->vmode & FB_VMODE_INTERLACED)
+ yres = (yres + 1) / 2;
+
+ if (yres >= 1280) {
+ printk (KERN_WARNING "cirrusfb: ERROR: VerticalTotal >= 1280; special treatment required! (TODO)\n");
+ DPRINTK ("EXIT - EINVAL error\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cirrusfb_decode_var (const struct fb_var_screeninfo *var,
+ struct cirrusfb_regs *regs,
+ const struct fb_info *info)
+{
+ long freq;
+ long maxclock;
+ int maxclockidx = 0;
+ struct cirrusfb_info *cinfo = info->par;
+ int xres, hfront, hsync, hback;
+ int yres, vfront, vsync, vback;
+
+ switch(var->bits_per_pixel) {
+ case 1:
+ regs->line_length = var->xres_virtual / 8;
+ regs->visual = FB_VISUAL_MONO10;
+ maxclockidx = 0;
+ break;
+
+ case 8:
+ regs->line_length = var->xres_virtual;
+ regs->visual = FB_VISUAL_PSEUDOCOLOR;
+ maxclockidx = 1;
+ break;
+
+ case 16:
+ regs->line_length = var->xres_virtual * 2;
+ regs->visual = FB_VISUAL_DIRECTCOLOR;
+ maxclockidx = 2;
+ break;
+
+ case 24:
+ regs->line_length = var->xres_virtual * 3;
+ regs->visual = FB_VISUAL_DIRECTCOLOR;
+ maxclockidx = 3;
+ break;
+
+ case 32:
+ regs->line_length = var->xres_virtual * 4;
+ regs->visual = FB_VISUAL_DIRECTCOLOR;
+ maxclockidx = 4;
+ break;
+
+ default:
+ DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
+ assert (FALSE);
+ /* should never occur */
+ break;
+ }
+
+ regs->type = FB_TYPE_PACKED_PIXELS;
+
+ /* convert from ps to kHz */
+ freq = 1000000000 / var->pixclock;
+
+ DPRINTK ("desired pixclock: %ld kHz\n", freq);
+
+ maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
+ regs->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) {
+ switch (cinfo->btype) {
+ case BT_ALPINE:
+ case BT_GD5480:
+ regs->multiplexing = 1;
+ break;
+
+ default:
+ printk (KERN_WARNING "cirrusfb: ERROR: Frequency greater than maxclock (%ld kHz)\n", maxclock);
+ DPRINTK ("EXIT - return -EINVAL\n");
+ return -EINVAL;
+ }
+ }
+#if 0
+ /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
+ * the VCLK is double the pixel clock. */
+ switch (var->bits_per_pixel) {
+ case 16:
+ case 32:
+ if (regs->HorizRes <= 800)
+ freq /= 2; /* Xbh has this type of clock for 32-bit */
+ break;
+ }
+#endif
+
+ bestclock (freq, &regs->freq, &regs->nom, &regs->den, &regs->div,
+ maxclock);
+ regs->mclk = cirrusfb_get_mclk (freq, var->bits_per_pixel, &regs->divMCLK);
+
+ xres = var->xres;
+ hfront = var->right_margin;
+ hsync = var->hsync_len;
+ hback = var->left_margin;
+
+ yres = var->yres;
+ vfront = var->lower_margin;
+ vsync = var->vsync_len;
+ vback = var->upper_margin;
+
+ if (var->vmode & FB_VMODE_DOUBLE) {
+ yres *= 2;
+ vfront *= 2;
+ vsync *= 2;
+ vback *= 2;
+ } else if (var->vmode & FB_VMODE_INTERLACED) {
+ yres = (yres + 1) / 2;
+ vfront = (vfront + 1) / 2;
+ vsync = (vsync + 1) / 2;
+ vback = (vback + 1) / 2;
+ }
+ regs->HorizRes = xres;
+ regs->HorizTotal = (xres + hfront + hsync + hback) / 8 - 5;
+ regs->HorizDispEnd = xres / 8 - 1;
+ regs->HorizBlankStart = xres / 8;
+ regs->HorizBlankEnd = regs->HorizTotal + 5; /* does not count with "-5" */
+ regs->HorizSyncStart = (xres + hfront) / 8 + 1;
+ regs->HorizSyncEnd = (xres + hfront + hsync) / 8 + 1;
+
+ regs->VertRes = yres;
+ regs->VertTotal = yres + vfront + vsync + vback - 2;
+ regs->VertDispEnd = yres - 1;
+ regs->VertBlankStart = yres;
+ regs->VertBlankEnd = regs->VertTotal;
+ regs->VertSyncStart = yres + vfront - 1;
+ regs->VertSyncEnd = yres + vfront + vsync - 1;
+
+ if (regs->VertRes >= 1024) {
+ regs->VertTotal /= 2;
+ regs->VertSyncStart /= 2;
+ regs->VertSyncEnd /= 2;
+ regs->VertDispEnd /= 2;
+ }
+ if (regs->multiplexing) {
+ regs->HorizTotal /= 2;
+ regs->HorizSyncStart /= 2;
+ regs->HorizSyncEnd /= 2;
+ regs->HorizDispEnd /= 2;
+ }
+
+ return 0;
+}
+
+
+static void cirrusfb_set_mclk (const struct cirrusfb_info *cinfo, int val, int div)
+{
+ assert (cinfo != NULL);
+
+ if (div == 2) {
+ /* VCLK = MCLK/2 */
+ unsigned char old = vga_rseq (cinfo->regbase, CL_SEQR1E);
+ vga_wseq (cinfo->regbase, CL_SEQR1E, old | 0x1);
+ vga_wseq (cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
+ } else if (div == 1) {
+ /* VCLK = MCLK */
+ unsigned char old = vga_rseq (cinfo->regbase, CL_SEQR1E);
+ vga_wseq (cinfo->regbase, CL_SEQR1E, old & ~0x1);
+ vga_wseq (cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
+ } else {
+ vga_wseq (cinfo->regbase, CL_SEQR1F, val & 0x3f);
+ }
+}
+
+/*************************************************************************
+ cirrusfb_set_par_foo()
+
+ actually writes the values for a new video mode into the hardware,
+**************************************************************************/
+static int cirrusfb_set_par_foo (struct fb_info *info)
+{
+ struct cirrusfb_info *cinfo = info->par;
+ struct fb_var_screeninfo *var = &info->var;
+ struct cirrusfb_regs regs;
+ u8 __iomem *regbase = cinfo->regbase;
+ unsigned char tmp;
+ int offset = 0, err;
+ const struct cirrusfb_board_info_rec *bi;
+
+ DPRINTK ("ENTER\n");
+ DPRINTK ("Requested mode: %dx%dx%d\n",
+ var->xres, var->yres, var->bits_per_pixel);
+ DPRINTK ("pixclock: %d\n", var->pixclock);
+
+ init_vgachip (cinfo);
+
+ err = cirrusfb_decode_var(var, &regs, info);
+ if(err) {
+ /* should never happen */
+ DPRINTK("mode change aborted. invalid var.\n");
+ return -EINVAL;
+ }
+
+ bi = &cirrusfb_board_info[cinfo->btype];
+
+
+ /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
+ vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
+
+ /* if debugging is enabled, all parameters get output before writing */
+ DPRINTK ("CRT0: %ld\n", regs.HorizTotal);
+ vga_wcrt (regbase, VGA_CRTC_H_TOTAL, regs.HorizTotal);
+
+ DPRINTK ("CRT1: %ld\n", regs.HorizDispEnd);
+ vga_wcrt (regbase, VGA_CRTC_H_DISP, regs.HorizDispEnd);
+
+ DPRINTK ("CRT2: %ld\n", regs.HorizBlankStart);
+ vga_wcrt (regbase, VGA_CRTC_H_BLANK_START, regs.HorizBlankStart);
+
+ DPRINTK ("CRT3: 128+%ld\n", regs.HorizBlankEnd % 32); /* + 128: Compatible read */
+ vga_wcrt (regbase, VGA_CRTC_H_BLANK_END, 128 + (regs.HorizBlankEnd % 32));
+
+ DPRINTK ("CRT4: %ld\n", regs.HorizSyncStart);
+ vga_wcrt (regbase, VGA_CRTC_H_SYNC_START, regs.HorizSyncStart);
+
+ tmp = regs.HorizSyncEnd % 32;
+ if (regs.HorizBlankEnd & 32)
+ tmp += 128;
+ DPRINTK ("CRT5: %d\n", tmp);
+ vga_wcrt (regbase, VGA_CRTC_H_SYNC_END, tmp);
+
+ DPRINTK ("CRT6: %ld\n", regs.VertTotal & 0xff);
+ vga_wcrt (regbase, VGA_CRTC_V_TOTAL, (regs.VertTotal & 0xff));
+
+ tmp = 16; /* LineCompare bit #9 */
+ if (regs.VertTotal & 256)
+ tmp |= 1;
+ if (regs.VertDispEnd & 256)
+ tmp |= 2;
+ if (regs.VertSyncStart & 256)
+ tmp |= 4;
+ if (regs.VertBlankStart & 256)
+ tmp |= 8;
+ if (regs.VertTotal & 512)
+ tmp |= 32;
+ if (regs.VertDispEnd & 512)
+ tmp |= 64;
+ if (regs.VertSyncStart & 512)
+ tmp |= 128;
+ DPRINTK ("CRT7: %d\n", tmp);
+ vga_wcrt (regbase, VGA_CRTC_OVERFLOW, tmp);
+
+ tmp = 0x40; /* LineCompare bit #8 */
+ if (regs.VertBlankStart & 512)
+ tmp |= 0x20;
+ if (var->vmode & FB_VMODE_DOUBLE)
+ tmp |= 0x80;
+ DPRINTK ("CRT9: %d\n", tmp);
+ vga_wcrt (regbase, VGA_CRTC_MAX_SCAN, tmp);
+
+ DPRINTK ("CRT10: %ld\n", regs.VertSyncStart & 0xff);
+ vga_wcrt (regbase, VGA_CRTC_V_SYNC_START, (regs.VertSyncStart & 0xff));
+
+ DPRINTK ("CRT11: 64+32+%ld\n", regs.VertSyncEnd % 16);
+ vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, (regs.VertSyncEnd % 16 + 64 + 32));
+
+ DPRINTK ("CRT12: %ld\n", regs.VertDispEnd & 0xff);
+ vga_wcrt (regbase, VGA_CRTC_V_DISP_END, (regs.VertDispEnd & 0xff));
+
+ DPRINTK ("CRT15: %ld\n", regs.VertBlankStart & 0xff);
+ vga_wcrt (regbase, VGA_CRTC_V_BLANK_START, (regs.VertBlankStart & 0xff));
+
+ DPRINTK ("CRT16: %ld\n", regs.VertBlankEnd & 0xff);
+ vga_wcrt (regbase, VGA_CRTC_V_BLANK_END, (regs.VertBlankEnd & 0xff));
+
+ DPRINTK ("CRT18: 0xff\n");
+ vga_wcrt (regbase, VGA_CRTC_LINE_COMPARE, 0xff);
+
+ tmp = 0;
+ if (var->vmode & FB_VMODE_INTERLACED)
+ tmp |= 1;
+ if (regs.HorizBlankEnd & 64)
+ tmp |= 16;
+ if (regs.HorizBlankEnd & 128)
+ tmp |= 32;
+ if (regs.VertBlankEnd & 256)
+ tmp |= 64;
+ if (regs.VertBlankEnd & 512)
+ tmp |= 128;
+
+ DPRINTK ("CRT1a: %d\n", tmp);
+ vga_wcrt (regbase, CL_CRT1A, tmp);
+
+ /* set VCLK0 */
+ /* hardware RefClock: 14.31818 MHz */
+ /* formula: VClk = (OSC * N) / (D * (1+P)) */
+ /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
+
+ vga_wseq (regbase, CL_SEQRB, regs.nom);
+ tmp = regs.den << 1;
+ if (regs.div != 0)
+ tmp |= 1;
+
+ if ((cinfo->btype == BT_SD64) ||
+ (cinfo->btype == BT_ALPINE) ||
+ (cinfo->btype == BT_GD5480))
+ tmp |= 0x80; /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
+
+ DPRINTK ("CL_SEQR1B: %ld\n", (long) tmp);
+ vga_wseq (regbase, CL_SEQR1B, tmp);
+
+ if (regs.VertRes >= 1024)
+ /* 1280x1024 */
+ vga_wcrt (regbase, VGA_CRTC_MODE, 0xc7);
+ else
+ /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
+ * address wrap, no compat. */
+ vga_wcrt (regbase, VGA_CRTC_MODE, 0xc3);
+
+/* HAEH? vga_wcrt (regbase, VGA_CRTC_V_SYNC_END, 0x20); * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
+
+ /* don't know if it would hurt to also program this if no interlaced */
+ /* mode is used, but I feel better this way.. :-) */
+ if (var->vmode & FB_VMODE_INTERLACED)
+ vga_wcrt (regbase, VGA_CRTC_REGS, regs.HorizTotal / 2);
+ else
+ vga_wcrt (regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
+
+ vga_wseq (regbase, VGA_SEQ_CHARACTER_MAP, 0);
+
+ /* adjust horizontal/vertical sync type (low/high) */
+ tmp = 0x03; /* enable display memory & CRTC I/O address for color mode */
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ tmp |= 0x40;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ tmp |= 0x80;
+ WGen (cinfo, VGA_MIS_W, tmp);
+
+ vga_wcrt (regbase, VGA_CRTC_PRESET_ROW, 0); /* Screen A Preset Row-Scan register */
+ vga_wcrt (regbase, VGA_CRTC_CURSOR_START, 0); /* t