1018 lines · 24.7 KB · C
RawDownload
/* *  controlfb.c -- frame buffer device for the PowerMac 'control' display * *  Created 12 July 1998 by Dan Jacobowitz <dan@debian.org> *  Copyright (C) 1998 Dan Jacobowitz *  Copyright (C) 2001 Takashi Oe * *  Mmap code by Michel Lanners <mlan@cpu.lu> * *  Frame buffer structure from: *    drivers/video/chipsfb.c -- frame buffer device for *    Chips & Technologies 65550 chip. * *    Copyright (C) 1998 Paul Mackerras * *    This file is derived from the Powermac "chips" driver: *    Copyright (C) 1997 Fabio Riccardi. *    And from the frame buffer device for Open Firmware-initialized devices: *    Copyright (C) 1997 Geert Uytterhoeven. * *  Hardware information from: *    control.c: Console support for PowerMac "control" display adaptor. *    Copyright (C) 1996 Paul Mackerras * *  Updated to 2.5 framebuffer API by Ben Herrenschmidt *  <benh@kernel.crashing.org>, Paul Mackerras <paulus@samba.org>, *  and James Simmons <jsimmons@infradead.org>. * *  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/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/of.h>#include <linux/of_address.h>#include <linux/fb.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/nvram.h>#include <linux/adb.h>#include <linux/cuda.h>#ifdef CONFIG_BOOTX_TEXT#include <asm/btext.h>#endif #include "macmodes.h"#include "controlfb.h" #if !defined(CONFIG_PPC_PMAC) || !defined(CONFIG_PPC32)#define invalid_vram_cache(addr)#undef in_8#undef out_8#undef in_le32#undef out_le32#define in_8(addr)		0#define out_8(addr, val)	(void)(val)#define in_le32(addr)		0#define out_le32(addr, val)	(void)(val)#ifndef pgprot_cached_wthru#define pgprot_cached_wthru(prot) (prot)#endif#elsestatic void invalid_vram_cache(void __force *addr){	eieio();	dcbf(addr);	mb();	eieio();	dcbf(addr);	mb();}#endif struct fb_par_control {	int	vmode, cmode;	int	xres, yres;	int	vxres, vyres;	int	xoffset, yoffset;	int	pitch;	struct control_regvals	regvals;	unsigned long sync;	unsigned char ctrl;}; #define DIRTY(z) ((x)->z != (y)->z)#define DIRTY_CMAP(z) (memcmp(&((x)->z), &((y)->z), sizeof((y)->z)))static inline int PAR_EQUAL(struct fb_par_control *x, struct fb_par_control *y){	int i, results; 	results = 1;	for (i = 0; i < 3; i++)		results &= !DIRTY(regvals.clock_params[i]);	if (!results)		return 0;	for (i = 0; i < 16; i++)		results &= !DIRTY(regvals.regs[i]);	if (!results)		return 0;	return (!DIRTY(cmode) && !DIRTY(xres) && !DIRTY(yres)		&& !DIRTY(vxres) && !DIRTY(vyres));} struct fb_info_control {	struct fb_info		info;	struct fb_par_control	par;	u32			pseudo_palette[16]; 	struct cmap_regs	__iomem *cmap_regs;	unsigned long		cmap_regs_phys; 	struct control_regs	__iomem *control_regs;	unsigned long		control_regs_phys;	unsigned long		control_regs_size; 	__u8			__iomem *frame_buffer;	unsigned long		frame_buffer_phys;	unsigned long		fb_orig_base;	unsigned long		fb_orig_size; 	int			control_use_bank2;	unsigned long		total_vram;	unsigned char		vram_attr;}; /* control register access macro */#define CNTRL_REG(INFO,REG) (&(((INFO)->control_regs->REG).r))  /************************** Internal variables *******************************/ static struct fb_info_control *control_fb; static int default_vmode __initdata = VMODE_NVRAM;static int default_cmode __initdata = CMODE_NVRAM;  static int controlfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,			     u_int transp, struct fb_info *info){	struct fb_info_control *p =		container_of(info, struct fb_info_control, info);	__u8 r, g, b; 	if (regno > 255)		return 1; 	r = red >> 8;	g = green >> 8;	b = blue >> 8; 	out_8(&p->cmap_regs->addr, regno);	/* tell clut what addr to fill	*/	out_8(&p->cmap_regs->lut, r);		/* send one color channel at	*/	out_8(&p->cmap_regs->lut, g);		/* a time...			*/	out_8(&p->cmap_regs->lut, b); 	if (regno < 16) {		int i;		switch (p->par.cmode) {		case CMODE_16:			p->pseudo_palette[regno] =			    (regno << 10) | (regno << 5) | regno;			break;		case CMODE_32:			i = (regno << 8) | regno;			p->pseudo_palette[regno] = (i << 16) | i;			break;		}	} 	return 0;}  /********************  End of controlfb_ops implementation  ******************/   static void set_control_clock(unsigned char *params){#ifdef CONFIG_ADB_CUDA	struct adb_request req;	int i; 	for (i = 0; i < 3; ++i) {		cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,			     0x50, i + 1, params[i]);		while (!req.complete)			cuda_poll();	}#endif} /* * Set screen start address according to var offset values */static inline void set_screen_start(int xoffset, int yoffset,	struct fb_info_control *p){	struct fb_par_control *par = &p->par; 	par->xoffset = xoffset;	par->yoffset = yoffset;	out_le32(CNTRL_REG(p,start_addr),		 par->yoffset * par->pitch + (par->xoffset << par->cmode));} #define RADACAL_WRITE(a,d) \	out_8(&p->cmap_regs->addr, (a)); \	out_8(&p->cmap_regs->dat,   (d)) /* Now how about actually saying, Make it so! *//* Some things in here probably don't need to be done each time. */static void control_set_hardware(struct fb_info_control *p, struct fb_par_control *par){	struct control_regvals	*r;	volatile struct preg	__iomem *rp;	int			i, cmode; 	if (PAR_EQUAL(&p->par, par)) {		/*		 * check if only xoffset or yoffset differs.		 * this prevents flickers in typical VT switch case.		 */		if (p->par.xoffset != par->xoffset ||		    p->par.yoffset != par->yoffset)			set_screen_start(par->xoffset, par->yoffset, p); 		return;	} 	p->par = *par;	cmode = p->par.cmode;	r = &par->regvals; 	/* Turn off display */	out_le32(CNTRL_REG(p,ctrl), 0x400 | par->ctrl); 	set_control_clock(r->clock_params); 	RADACAL_WRITE(0x20, r->radacal_ctrl);	RADACAL_WRITE(0x21, p->control_use_bank2 ? 0 : 1);	RADACAL_WRITE(0x10, 0);	RADACAL_WRITE(0x11, 0); 	rp = &p->control_regs->vswin;	for (i = 0; i < 16; ++i, ++rp)		out_le32(&rp->r, r->regs[i]); 	out_le32(CNTRL_REG(p,pitch), par->pitch);	out_le32(CNTRL_REG(p,mode), r->mode);	out_le32(CNTRL_REG(p,vram_attr), p->vram_attr);	out_le32(CNTRL_REG(p,start_addr), par->yoffset * par->pitch		 + (par->xoffset << cmode));	out_le32(CNTRL_REG(p,rfrcnt), 0x1e5);	out_le32(CNTRL_REG(p,intr_ena), 0); 	/* Turn on display */	out_le32(CNTRL_REG(p,ctrl), par->ctrl); #ifdef CONFIG_BOOTX_TEXT	btext_update_display(p->frame_buffer_phys + CTRLFB_OFF,			     p->par.xres, p->par.yres,			     (cmode == CMODE_32? 32: cmode == CMODE_16? 16: 8),			     p->par.pitch);#endif /* CONFIG_BOOTX_TEXT */} /* Work out which banks of VRAM we have installed. *//* danj: I guess the card just ignores writes to nonexistant VRAM... */ static void __init find_vram_size(struct fb_info_control *p){	int bank1, bank2; 	/*	 * Set VRAM in 2MB (bank 1) mode	 * VRAM Bank 2 will be accessible through offset 0x600000 if present	 * and VRAM Bank 1 will not respond at that offset even if present	 */	out_le32(CNTRL_REG(p,vram_attr), 0x31); 	out_8(&p->frame_buffer[0x600000], 0xb3);	out_8(&p->frame_buffer[0x600001], 0x71);	invalid_vram_cache(&p->frame_buffer[0x600000]); 	bank2 = (in_8(&p->frame_buffer[0x600000]) == 0xb3)		&& (in_8(&p->frame_buffer[0x600001]) == 0x71); 	/*	 * Set VRAM in 2MB (bank 2) mode	 * VRAM Bank 1 will be accessible through offset 0x000000 if present	 * and VRAM Bank 2 will not respond at that offset even if present	 */	out_le32(CNTRL_REG(p,vram_attr), 0x39); 	out_8(&p->frame_buffer[0], 0x5a);	out_8(&p->frame_buffer[1], 0xc7);	invalid_vram_cache(&p->frame_buffer[0]); 	bank1 = (in_8(&p->frame_buffer[0]) == 0x5a)		&& (in_8(&p->frame_buffer[1]) == 0xc7); 	if (bank2) {		if (!bank1) {			/*			 * vram bank 2 only			 */			p->control_use_bank2 = 1;			p->vram_attr = 0x39;			p->frame_buffer += 0x600000;			p->frame_buffer_phys += 0x600000;		} else {			/*			 * 4 MB vram			 */			p->vram_attr = 0x51;		}	} else {		/*		 * vram bank 1 only		 */		p->vram_attr = 0x31;	}         p->total_vram = (bank1 + bank2) * 0x200000; 	printk(KERN_INFO "controlfb: VRAM Total = %dMB "			"(%dMB @ bank 1, %dMB @ bank 2)\n",			(bank1 + bank2) << 1, bank1 << 1, bank2 << 1);} /* * Get the monitor sense value. * Note that this can be called before calibrate_delay, * so we can't use udelay. */static int read_control_sense(struct fb_info_control *p){	int sense; 	out_le32(CNTRL_REG(p,mon_sense), 7);	/* drive all lines high */	__delay(200);	out_le32(CNTRL_REG(p,mon_sense), 077);	/* turn off drivers */	__delay(2000);	sense = (in_le32(CNTRL_REG(p,mon_sense)) & 0x1c0) << 2; 	/* drive each sense line low in turn and collect the other 2 */	out_le32(CNTRL_REG(p,mon_sense), 033);	/* drive A low */	__delay(2000);	sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0xc0) >> 2;	out_le32(CNTRL_REG(p,mon_sense), 055);	/* drive B low */	__delay(2000);	sense |= ((in_le32(CNTRL_REG(p,mon_sense)) & 0x100) >> 5)		| ((in_le32(CNTRL_REG(p,mon_sense)) & 0x40) >> 4);	out_le32(CNTRL_REG(p,mon_sense), 066);	/* drive C low */	__delay(2000);	sense |= (in_le32(CNTRL_REG(p,mon_sense)) & 0x180) >> 7; 	out_le32(CNTRL_REG(p,mon_sense), 077);	/* turn off drivers */ 	return sense;} /**********************  Various translation functions  **********************/ #define CONTROL_PIXCLOCK_BASE	256016#define CONTROL_PIXCLOCK_MIN	5000	/* ~ 200 MHz dot clock */ /* * calculate the clock parameters to be sent to CUDA according to given * pixclock in pico second. */static int calc_clock_params(unsigned long clk, unsigned char *param){	unsigned long p0, p1, p2, k, l, m, n, min; 	if (clk > (CONTROL_PIXCLOCK_BASE << 3))		return 1; 	p2 = ((clk << 4) < CONTROL_PIXCLOCK_BASE)? 3: 2;	l = clk << p2;	p0 = 0;	p1 = 0;	for (k = 1, min = l; k < 32; k++) {		unsigned long rem; 		m = CONTROL_PIXCLOCK_BASE * k;		n = m / l;		rem = m % l;		if (n && (n < 128) && rem < min) {			p0 = k;			p1 = n;			min = rem;		}	}	if (!p0 || !p1)		return 1; 	param[0] = p0;	param[1] = p1;	param[2] = p2; 	return 0;}  /* * This routine takes a user-supplied var, and picks the best vmode/cmode * from it. */ static int control_var_to_par(struct fb_var_screeninfo *var,	struct fb_par_control *par, const struct fb_info *fb_info){	int cmode, piped_diff, hstep;	unsigned hperiod, hssync, hsblank, hesync, heblank, piped, heq, hlfln,		 hserr, vperiod, vssync, vesync, veblank, vsblank, vswin, vewin;	unsigned long pixclock;	struct fb_info_control *p =		container_of(fb_info, struct fb_info_control, info);	struct control_regvals *r = &par->regvals; 	switch (var->bits_per_pixel) {	case 8:		par->cmode = CMODE_8;		if (p->total_vram > 0x200000) {			r->mode = 3;			r->radacal_ctrl = 0x20;			piped_diff = 13;		} else {			r->mode = 2;			r->radacal_ctrl = 0x10;			piped_diff = 9;		}		break;	case 15:	case 16:		par->cmode = CMODE_16;		if (p->total_vram > 0x200000) {			r->mode = 2;			r->radacal_ctrl = 0x24;			piped_diff = 5;		} else {			r->mode = 1;			r->radacal_ctrl = 0x14;			piped_diff = 3;		}		break;	case 32:		par->cmode = CMODE_32;		if (p->total_vram > 0x200000) {			r->mode = 1;			r->radacal_ctrl = 0x28;		} else {			r->mode = 0;			r->radacal_ctrl = 0x18;		}		piped_diff = 1;		break;	default:		return -EINVAL;	} 	/*	 * adjust xres and vxres so that the corresponding memory widths are	 * 32-byte aligned	 */	hstep = 31 >> par->cmode;	par->xres = (var->xres + hstep) & ~hstep;	par->vxres = (var->xres_virtual + hstep) & ~hstep;	par->xoffset = (var->xoffset + hstep) & ~hstep;	if (par->vxres < par->xres)		par->vxres = par->xres;	par->pitch = par->vxres << par->cmode; 	par->yres = var->yres;	par->vyres = var->yres_virtual;	par->yoffset = var->yoffset;	if (par->vyres < par->yres)		par->vyres = par->yres; 	par->sync = var->sync; 	if (par->pitch * par->vyres + CTRLFB_OFF > p->total_vram)		return -EINVAL; 	if (par->xoffset + par->xres > par->vxres)		par->xoffset = par->vxres - par->xres;	if (par->yoffset + par->yres > par->vyres)		par->yoffset = par->vyres - par->yres; 

Lines 1–500 of 1018.