2954 lines · 75.3 KB · C
RawDownload
/* * 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.