1871 lines · 44.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  linux/drivers/video/cyber2000fb.c * *  Copyright (C) 1998-2002 Russell King * *  MIPS and 50xx clock support *  Copyright (C) 2001 Bradley D. LaRonde <brad@ltc.com> * *  32 bit support, text color and panning fixes for modes != 8 bit *  Copyright (C) 2002 Denis Oliver Kropp <dok@directfb.org> * * Integraphics CyberPro 2000, 2010 and 5000 frame buffer device * * Based on cyberfb.c. * * Note that we now use the new fbcon fix, var and cmap scheme.  We do * still have to check which console is the currently displayed one * however, especially for the colourmap stuff. * * We also use the new hotplug PCI subsystem.  I'm not sure if there * are any such cards, but I'm erring on the side of caution.  We don't * want to go pop just because someone does have one. * * Note that this doesn't work fully in the case of multiple CyberPro * cards with grabbers.  We currently can only attach to the first * CyberPro card found. * * When we're in truecolour mode, we power down the LUT RAM as a power * saving feature.  Also, when we enter any of the powersaving modes * (except soft blanking) we power down the RAMDACs.  This saves about * 1W, which is roughly 8% of the power consumption of a NetWinder * (which, incidentally, is about the same saving as a 2.5in hard disk * entering standby mode.) */#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/slab.h>#include <linux/delay.h>#include <linux/fb.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/io.h>#include <linux/i2c.h>#include <linux/i2c-algo-bit.h> #ifdef __arm__#include <asm/mach-types.h>#endif #include "cyber2000fb.h" struct cfb_info {	struct fb_info		fb;	struct display_switch	*dispsw;	unsigned char		__iomem *region;	unsigned char		__iomem *regs;	u_int			id;	u_int			irq;	int			func_use_count;	u_long			ref_ps; 	/*	 * Clock divisors	 */	u_int			divisors[4]; 	struct {		u8 red, green, blue;	} palette[NR_PALETTE]; 	u_char			mem_ctl1;	u_char			mem_ctl2;	u_char			mclk_mult;	u_char			mclk_div;	/*	 * RAMDAC control register is both of these or'ed together	 */	u_char			ramdac_ctrl;	u_char			ramdac_powerdown; 	u32			pseudo_palette[16]; 	spinlock_t		reg_b0_lock; #ifdef CONFIG_FB_CYBER2000_DDC	bool			ddc_registered;	struct i2c_adapter	ddc_adapter;	struct i2c_algo_bit_data	ddc_algo;#endif #ifdef CONFIG_FB_CYBER2000_I2C	struct i2c_adapter	i2c_adapter;	struct i2c_algo_bit_data i2c_algo;#endif}; static char *default_font = "Acorn8x8";module_param(default_font, charp, 0);MODULE_PARM_DESC(default_font, "Default font name"); /* * Our access methods. */#define cyber2000fb_writel(val, reg, cfb)	writel(val, (cfb)->regs + (reg))#define cyber2000fb_writew(val, reg, cfb)	writew(val, (cfb)->regs + (reg))#define cyber2000fb_writeb(val, reg, cfb)	writeb(val, (cfb)->regs + (reg)) #define cyber2000fb_readb(reg, cfb)		readb((cfb)->regs + (reg)) static inline voidcyber2000_crtcw(unsigned int reg, unsigned int val, struct cfb_info *cfb){	cyber2000fb_writew((reg & 255) | val << 8, 0x3d4, cfb);} static inline voidcyber2000_grphw(unsigned int reg, unsigned int val, struct cfb_info *cfb){	cyber2000fb_writew((reg & 255) | val << 8, 0x3ce, cfb);} static inline unsigned intcyber2000_grphr(unsigned int reg, struct cfb_info *cfb){	cyber2000fb_writeb(reg, 0x3ce, cfb);	return cyber2000fb_readb(0x3cf, cfb);} static inline voidcyber2000_attrw(unsigned int reg, unsigned int val, struct cfb_info *cfb){	cyber2000fb_readb(0x3da, cfb);	cyber2000fb_writeb(reg, 0x3c0, cfb);	cyber2000fb_readb(0x3c1, cfb);	cyber2000fb_writeb(val, 0x3c0, cfb);} static inline voidcyber2000_seqw(unsigned int reg, unsigned int val, struct cfb_info *cfb){	cyber2000fb_writew((reg & 255) | val << 8, 0x3c4, cfb);} /* -------------------- Hardware specific routines ------------------------- */ /* * Hardware Cyber2000 Acceleration */static voidcyber2000fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect){	struct cfb_info *cfb = container_of(info, struct cfb_info, fb);	unsigned long dst, col; 	if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) {		cfb_fillrect(info, rect);		return;	} 	cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);	cyber2000fb_writew(rect->width - 1, CO_REG_PIXWIDTH, cfb);	cyber2000fb_writew(rect->height - 1, CO_REG_PIXHEIGHT, cfb); 	col = rect->color;	if (cfb->fb.var.bits_per_pixel > 8)		col = ((u32 *)cfb->fb.pseudo_palette)[col];	cyber2000fb_writel(col, CO_REG_FGCOLOUR, cfb); 	dst = rect->dx + rect->dy * cfb->fb.var.xres_virtual;	if (cfb->fb.var.bits_per_pixel == 24) {		cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);		dst *= 3;	} 	cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);	cyber2000fb_writeb(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);	cyber2000fb_writew(CO_CMD_L_PATTERN_FGCOL, CO_REG_CMD_L, cfb);	cyber2000fb_writew(CO_CMD_H_BLITTER, CO_REG_CMD_H, cfb);} static voidcyber2000fb_copyarea(struct fb_info *info, const struct fb_copyarea *region){	struct cfb_info *cfb = container_of(info, struct cfb_info, fb);	unsigned int cmd = CO_CMD_L_PATTERN_FGCOL;	unsigned long src, dst; 	if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT)) {		cfb_copyarea(info, region);		return;	} 	cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);	cyber2000fb_writew(region->width - 1, CO_REG_PIXWIDTH, cfb);	cyber2000fb_writew(region->height - 1, CO_REG_PIXHEIGHT, cfb); 	src = region->sx + region->sy * cfb->fb.var.xres_virtual;	dst = region->dx + region->dy * cfb->fb.var.xres_virtual; 	if (region->sx < region->dx) {		src += region->width - 1;		dst += region->width - 1;		cmd |= CO_CMD_L_INC_LEFT;	} 	if (region->sy < region->dy) {		src += (region->height - 1) * cfb->fb.var.xres_virtual;		dst += (region->height - 1) * cfb->fb.var.xres_virtual;		cmd |= CO_CMD_L_INC_UP;	} 	if (cfb->fb.var.bits_per_pixel == 24) {		cyber2000fb_writeb(dst, CO_REG_X_PHASE, cfb);		src *= 3;		dst *= 3;	}	cyber2000fb_writel(src, CO_REG_SRC1_PTR, cfb);	cyber2000fb_writel(dst, CO_REG_DEST_PTR, cfb);	cyber2000fb_writew(CO_FG_MIX_SRC, CO_REG_FGMIX, cfb);	cyber2000fb_writew(cmd, CO_REG_CMD_L, cfb);	cyber2000fb_writew(CO_CMD_H_FGSRCMAP | CO_CMD_H_BLITTER,			   CO_REG_CMD_H, cfb);} static int cyber2000fb_sync(struct fb_info *info){	struct cfb_info *cfb = container_of(info, struct cfb_info, fb);	int count = 100000; 	if (!(cfb->fb.var.accel_flags & FB_ACCELF_TEXT))		return 0; 	while (cyber2000fb_readb(CO_REG_CONTROL, cfb) & CO_CTRL_BUSY) {		if (!count--) {			debug_printf("accel_wait timed out\n");			cyber2000fb_writeb(0, CO_REG_CONTROL, cfb);			break;		}		udelay(1);	}	return 0;} /* * =========================================================================== */ static inline u32 convert_bitfield(u_int val, struct fb_bitfield *bf){	u_int mask = (1 << bf->length) - 1; 	return (val >> (16 - bf->length) & mask) << bf->offset;} /* *    Set a single color register. Return != 0 for invalid regno. */static intcyber2000fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,		      u_int transp, struct fb_info *info){	struct cfb_info *cfb = container_of(info, struct cfb_info, fb);	struct fb_var_screeninfo *var = &cfb->fb.var;	u32 pseudo_val;	int ret = 1; 	switch (cfb->fb.fix.visual) {	default:		return 1; 	/*	 * Pseudocolour:	 *	   8     8	 * pixel --/--+--/-->  red lut  --> red dac	 *	      |  8	 *	      +--/--> green lut --> green dac	 *	      |  8	 *	      +--/-->  blue lut --> blue dac	 */	case FB_VISUAL_PSEUDOCOLOR:		if (regno >= NR_PALETTE)			return 1; 		red >>= 8;		green >>= 8;		blue >>= 8; 		cfb->palette[regno].red = red;		cfb->palette[regno].green = green;		cfb->palette[regno].blue = blue; 		cyber2000fb_writeb(regno, 0x3c8, cfb);		cyber2000fb_writeb(red, 0x3c9, cfb);		cyber2000fb_writeb(green, 0x3c9, cfb);		cyber2000fb_writeb(blue, 0x3c9, cfb);		return 0; 	/*	 * Direct colour:	 *	   n     rl	 * pixel --/--+--/-->  red lut  --> red dac	 *	      |  gl	 *	      +--/--> green lut --> green dac	 *	      |  bl	 *	      +--/-->  blue lut --> blue dac	 * n = bpp, rl = red length, gl = green length, bl = blue length	 */	case FB_VISUAL_DIRECTCOLOR:		red >>= 8;		green >>= 8;		blue >>= 8; 		if (var->green.length == 6 && regno < 64) {			cfb->palette[regno << 2].green = green; 			/*			 * The 6 bits of the green component are applied			 * to the high 6 bits of the LUT.			 */			cyber2000fb_writeb(regno << 2, 0x3c8, cfb);			cyber2000fb_writeb(cfb->palette[regno >> 1].red,					   0x3c9, cfb);			cyber2000fb_writeb(green, 0x3c9, cfb);			cyber2000fb_writeb(cfb->palette[regno >> 1].blue,					   0x3c9, cfb); 			green = cfb->palette[regno << 3].green; 			ret = 0;		} 		if (var->green.length >= 5 && regno < 32) {			cfb->palette[regno << 3].red = red;			cfb->palette[regno << 3].green = green;			cfb->palette[regno << 3].blue = blue; 			/*			 * The 5 bits of each colour component are			 * applied to the high 5 bits of the LUT.			 */			cyber2000fb_writeb(regno << 3, 0x3c8, cfb);			cyber2000fb_writeb(red, 0x3c9, cfb);			cyber2000fb_writeb(green, 0x3c9, cfb);			cyber2000fb_writeb(blue, 0x3c9, cfb);			ret = 0;		} 		if (var->green.length == 4 && regno < 16) {			cfb->palette[regno << 4].red = red;			cfb->palette[regno << 4].green = green;			cfb->palette[regno << 4].blue = blue; 			/*			 * The 5 bits of each colour component are			 * applied to the high 5 bits of the LUT.			 */			cyber2000fb_writeb(regno << 4, 0x3c8, cfb);			cyber2000fb_writeb(red, 0x3c9, cfb);			cyber2000fb_writeb(green, 0x3c9, cfb);			cyber2000fb_writeb(blue, 0x3c9, cfb);			ret = 0;		} 		/*		 * Since this is only used for the first 16 colours, we		 * don't have to care about overflowing for regno >= 32		 */		pseudo_val = regno << var->red.offset |			     regno << var->green.offset |			     regno << var->blue.offset;		break; 	/*	 * True colour:	 *	   n     rl	 * pixel --/--+--/--> red dac	 *	      |  gl	 *	      +--/--> green dac	 *	      |  bl	 *	      +--/--> blue dac	 * n = bpp, rl = red length, gl = green length, bl = blue length	 */	case FB_VISUAL_TRUECOLOR:		pseudo_val = convert_bitfield(transp ^ 0xffff, &var->transp);		pseudo_val |= convert_bitfield(red, &var->red);		pseudo_val |= convert_bitfield(green, &var->green);		pseudo_val |= convert_bitfield(blue, &var->blue);		ret = 0;		break;	} 	/*	 * Now set our pseudo palette for the CFB16/24/32 drivers.	 */	if (regno < 16)		((u32 *)cfb->fb.pseudo_palette)[regno] = pseudo_val; 	return ret;} struct par_info {	/*	 * Hardware	 */	u_char	clock_mult;	u_char	clock_div;	u_char	extseqmisc;	u_char	co_pixfmt;	u_char	crtc_ofl;	u_char	crtc[19];	u_int	width;	u_int	pitch;	u_int	fetch; 	/*	 * Other	 */	u_char	ramdac;}; static const u_char crtc_idx[] = {	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,	0x08, 0x09,	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18}; static void cyber2000fb_write_ramdac_ctrl(struct cfb_info *cfb){	unsigned int i;	unsigned int val = cfb->ramdac_ctrl | cfb->ramdac_powerdown; 	cyber2000fb_writeb(0x56, 0x3ce, cfb);	i = cyber2000fb_readb(0x3cf, cfb);	cyber2000fb_writeb(i | 4, 0x3cf, cfb);	cyber2000fb_writeb(val, 0x3c6, cfb);	cyber2000fb_writeb(i, 0x3cf, cfb);	/* prevent card lock-up observed on x86 with CyberPro 2000 */	cyber2000fb_readb(0x3cf, cfb);} static void cyber2000fb_set_timing(struct cfb_info *cfb, struct par_info *hw){	u_int i; 	/*	 * Blank palette	 */	for (i = 0; i < NR_PALETTE; i++) {		cyber2000fb_writeb(i, 0x3c8, cfb);		cyber2000fb_writeb(0, 0x3c9, cfb);		cyber2000fb_writeb(0, 0x3c9, cfb);		cyber2000fb_writeb(0, 0x3c9, cfb);	} 	cyber2000fb_writeb(0xef, 0x3c2, cfb);	cyber2000_crtcw(0x11, 0x0b, cfb);	cyber2000_attrw(0x11, 0x00, cfb); 	cyber2000_seqw(0x00, 0x01, cfb);	cyber2000_seqw(0x01, 0x01, cfb);	cyber2000_seqw(0x02, 0x0f, cfb);	cyber2000_seqw(0x03, 0x00, cfb);	cyber2000_seqw(0x04, 0x0e, cfb);	cyber2000_seqw(0x00, 0x03, cfb); 	for (i = 0; i < sizeof(crtc_idx); i++)		cyber2000_crtcw(crtc_idx[i], hw->crtc[i], cfb); 	for (i = 0x0a; i < 0x10; i++)		cyber2000_crtcw(i, 0, cfb); 	cyber2000_grphw(EXT_CRT_VRTOFL, hw->crtc_ofl, cfb);	cyber2000_grphw(0x00, 0x00, cfb);	cyber2000_grphw(0x01, 0x00, cfb);	cyber2000_grphw(0x02, 0x00, cfb);	cyber2000_grphw(0x03, 0x00, cfb);	cyber2000_grphw(0x04, 0x00, cfb);	cyber2000_grphw(0x05, 0x60, cfb);	cyber2000_grphw(0x06, 0x05, cfb);	cyber2000_grphw(0x07, 0x0f, cfb);	cyber2000_grphw(0x08, 0xff, cfb); 	/* Attribute controller registers */	for (i = 0; i < 16; i++)		cyber2000_attrw(i, i, cfb); 	cyber2000_attrw(0x10, 0x01, cfb);	cyber2000_attrw(0x11, 0x00, cfb);	cyber2000_attrw(0x12, 0x0f, cfb);	cyber2000_attrw(0x13, 0x00, cfb);	cyber2000_attrw(0x14, 0x00, cfb); 	/* PLL registers */	spin_lock(&cfb->reg_b0_lock);	cyber2000_grphw(EXT_DCLK_MULT, hw->clock_mult, cfb);

Lines 1–500 of 1871.