883 lines · 21.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* cg6.c: CGSIX (GX, GXplus, TGX) frame buffer driver * * Copyright (C) 2003, 2006 David S. Miller (davem@davemloft.net) * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz) * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be) * * Driver layout based loosely on tgafb.c, see that file for credits. */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/fb.h>#include <linux/mm.h>#include <linux/of.h>#include <linux/platform_device.h> #include <asm/io.h>#include <asm/fbio.h> #include "sbuslib.h" /* * Local functions. */ static int cg6_setcolreg(unsigned, unsigned, unsigned, unsigned,			 unsigned, struct fb_info *);static int cg6_blank(int, struct fb_info *); static void cg6_imageblit(struct fb_info *, const struct fb_image *);static void cg6_fillrect(struct fb_info *, const struct fb_fillrect *);static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area);static int cg6_sync(struct fb_info *);static int cg6_pan_display(struct fb_var_screeninfo *, struct fb_info *); static int cg6_sbusfb_mmap(struct fb_info *info, struct vm_area_struct *vma);static int cg6_sbusfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg); /* *  Frame buffer operations */ static const struct fb_ops cg6_ops = {	.owner			= THIS_MODULE,	__FB_DEFAULT_SBUS_OPS_RDWR(cg6),	.fb_setcolreg		= cg6_setcolreg,	.fb_blank		= cg6_blank,	.fb_pan_display		= cg6_pan_display,	.fb_fillrect		= cg6_fillrect,	.fb_copyarea		= cg6_copyarea,	.fb_imageblit		= cg6_imageblit,	.fb_sync		= cg6_sync,	__FB_DEFAULT_SBUS_OPS_IOCTL(cg6),	__FB_DEFAULT_SBUS_OPS_MMAP(cg6),}; /* Offset of interesting structures in the OBIO space *//* * Brooktree is the video dac and is funny to program on the cg6. * (it's even funnier on the cg3) * The FBC could be the frame buffer control * The FHC could is the frame buffer hardware control. */#define CG6_ROM_OFFSET			0x0UL#define CG6_BROOKTREE_OFFSET		0x200000UL#define CG6_DHC_OFFSET			0x240000UL#define CG6_ALT_OFFSET			0x280000UL#define CG6_FHC_OFFSET			0x300000UL#define CG6_THC_OFFSET			0x301000UL#define CG6_FBC_OFFSET			0x700000UL#define CG6_TEC_OFFSET			0x701000UL#define CG6_RAM_OFFSET			0x800000UL /* FHC definitions */#define CG6_FHC_FBID_SHIFT		24#define CG6_FHC_FBID_MASK		255#define CG6_FHC_REV_SHIFT		20#define CG6_FHC_REV_MASK		15#define CG6_FHC_FROP_DISABLE		(1 << 19)#define CG6_FHC_ROW_DISABLE		(1 << 18)#define CG6_FHC_SRC_DISABLE		(1 << 17)#define CG6_FHC_DST_DISABLE		(1 << 16)#define CG6_FHC_RESET			(1 << 15)#define CG6_FHC_LITTLE_ENDIAN		(1 << 13)#define CG6_FHC_RES_MASK		(3 << 11)#define CG6_FHC_1024			(0 << 11)#define CG6_FHC_1152			(1 << 11)#define CG6_FHC_1280			(2 << 11)#define CG6_FHC_1600			(3 << 11)#define CG6_FHC_CPU_MASK		(3 << 9)#define CG6_FHC_CPU_SPARC		(0 << 9)#define CG6_FHC_CPU_68020		(1 << 9)#define CG6_FHC_CPU_386			(2 << 9)#define CG6_FHC_TEST			(1 << 8)#define CG6_FHC_TEST_X_SHIFT		4#define CG6_FHC_TEST_X_MASK		15#define CG6_FHC_TEST_Y_SHIFT		0#define CG6_FHC_TEST_Y_MASK		15 /* FBC mode definitions */#define CG6_FBC_BLIT_IGNORE		0x00000000#define CG6_FBC_BLIT_NOSRC		0x00100000#define CG6_FBC_BLIT_SRC		0x00200000#define CG6_FBC_BLIT_ILLEGAL		0x00300000#define CG6_FBC_BLIT_MASK		0x00300000 #define CG6_FBC_VBLANK			0x00080000 #define CG6_FBC_MODE_IGNORE		0x00000000#define CG6_FBC_MODE_COLOR8		0x00020000#define CG6_FBC_MODE_COLOR1		0x00040000#define CG6_FBC_MODE_HRMONO		0x00060000#define CG6_FBC_MODE_MASK		0x00060000 #define CG6_FBC_DRAW_IGNORE		0x00000000#define CG6_FBC_DRAW_RENDER		0x00008000#define CG6_FBC_DRAW_PICK		0x00010000#define CG6_FBC_DRAW_ILLEGAL		0x00018000#define CG6_FBC_DRAW_MASK		0x00018000 #define CG6_FBC_BWRITE0_IGNORE		0x00000000#define CG6_FBC_BWRITE0_ENABLE		0x00002000#define CG6_FBC_BWRITE0_DISABLE		0x00004000#define CG6_FBC_BWRITE0_ILLEGAL		0x00006000#define CG6_FBC_BWRITE0_MASK		0x00006000 #define CG6_FBC_BWRITE1_IGNORE		0x00000000#define CG6_FBC_BWRITE1_ENABLE		0x00000800#define CG6_FBC_BWRITE1_DISABLE		0x00001000#define CG6_FBC_BWRITE1_ILLEGAL		0x00001800#define CG6_FBC_BWRITE1_MASK		0x00001800 #define CG6_FBC_BREAD_IGNORE		0x00000000#define CG6_FBC_BREAD_0			0x00000200#define CG6_FBC_BREAD_1			0x00000400#define CG6_FBC_BREAD_ILLEGAL		0x00000600#define CG6_FBC_BREAD_MASK		0x00000600 #define CG6_FBC_BDISP_IGNORE		0x00000000#define CG6_FBC_BDISP_0			0x00000080#define CG6_FBC_BDISP_1			0x00000100#define CG6_FBC_BDISP_ILLEGAL		0x00000180#define CG6_FBC_BDISP_MASK		0x00000180 #define CG6_FBC_INDEX_MOD		0x00000040#define CG6_FBC_INDEX_MASK		0x00000030 /* THC definitions */#define CG6_THC_MISC_REV_SHIFT		16#define CG6_THC_MISC_REV_MASK		15#define CG6_THC_MISC_RESET		(1 << 12)#define CG6_THC_MISC_VIDEO		(1 << 10)#define CG6_THC_MISC_SYNC		(1 << 9)#define CG6_THC_MISC_VSYNC		(1 << 8)#define CG6_THC_MISC_SYNC_ENAB		(1 << 7)#define CG6_THC_MISC_CURS_RES		(1 << 6)#define CG6_THC_MISC_INT_ENAB		(1 << 5)#define CG6_THC_MISC_INT		(1 << 4)#define CG6_THC_MISC_INIT		0x9f#define CG6_THC_CURSOFF			((65536-32) | ((65536-32) << 16)) /* The contents are unknown */struct cg6_tec {	int tec_matrix;	int tec_clip;	int tec_vdc;}; struct cg6_thc {	u32	thc_pad0[512];	u32	thc_hs;		/* hsync timing */	u32	thc_hsdvs;	u32	thc_hd;	u32	thc_vs;		/* vsync timing */	u32	thc_vd;	u32	thc_refresh;	u32	thc_misc;	u32	thc_pad1[56];	u32	thc_cursxy;	/* cursor x,y position (16 bits each) */	u32	thc_cursmask[32];	/* cursor mask bits */	u32	thc_cursbits[32];	/* what to show where mask enabled */}; struct cg6_fbc {	u32	xxx0[1];	u32	mode;	u32	clip;	u32	xxx1[1];	u32	s;	u32	draw;	u32	blit;	u32	font;	u32	xxx2[24];	u32	x0, y0, z0, color0;	u32	x1, y1, z1, color1;	u32	x2, y2, z2, color2;	u32	x3, y3, z3, color3;	u32	offx, offy;	u32	xxx3[2];	u32	incx, incy;	u32	xxx4[2];	u32	clipminx, clipminy;	u32	xxx5[2];	u32	clipmaxx, clipmaxy;	u32	xxx6[2];	u32	fg;	u32	bg;	u32	alu;	u32	pm;	u32	pixelm;	u32	xxx7[2];	u32	patalign;	u32	pattern[8];	u32	xxx8[432];	u32	apointx, apointy, apointz;	u32	xxx9[1];	u32	rpointx, rpointy, rpointz;	u32	xxx10[5];	u32	pointr, pointg, pointb, pointa;	u32	alinex, aliney, alinez;	u32	xxx11[1];	u32	rlinex, rliney, rlinez;	u32	xxx12[5];	u32	liner, lineg, lineb, linea;	u32	atrix, atriy, atriz;	u32	xxx13[1];	u32	rtrix, rtriy, rtriz;	u32	xxx14[5];	u32	trir, trig, trib, tria;	u32	aquadx, aquady, aquadz;	u32	xxx15[1];	u32	rquadx, rquady, rquadz;	u32	xxx16[5];	u32	quadr, quadg, quadb, quada;	u32	arectx, arecty, arectz;	u32	xxx17[1];	u32	rrectx, rrecty, rrectz;	u32	xxx18[5];	u32	rectr, rectg, rectb, recta;}; struct bt_regs {	u32	addr;	u32	color_map;	u32	control;	u32	cursor;}; struct cg6_par {	spinlock_t		lock;	struct bt_regs		__iomem *bt;	struct cg6_fbc		__iomem *fbc;	struct cg6_thc		__iomem *thc;	struct cg6_tec		__iomem *tec;	u32			__iomem *fhc; 	u32			flags;#define CG6_FLAG_BLANKED	0x00000001 	unsigned long		which_io;}; static int cg6_sync(struct fb_info *info){	struct cg6_par *par = (struct cg6_par *)info->par;	struct cg6_fbc __iomem *fbc = par->fbc;	int limit = 10000; 	do {		if (!(sbus_readl(&fbc->s) & 0x10000000))			break;		udelay(10);	} while (--limit > 0); 	return 0;} static void cg6_switch_from_graph(struct cg6_par *par){	struct cg6_thc __iomem *thc = par->thc;	unsigned long flags; 	spin_lock_irqsave(&par->lock, flags); 	/* Hide the cursor. */	sbus_writel(CG6_THC_CURSOFF, &thc->thc_cursxy); 	spin_unlock_irqrestore(&par->lock, flags);} static int cg6_pan_display(struct fb_var_screeninfo *var, struct fb_info *info){	struct cg6_par *par = (struct cg6_par *)info->par; 	/* We just use this to catch switches out of	 * graphics mode.	 */	cg6_switch_from_graph(par); 	if (var->xoffset || var->yoffset || var->vmode)		return -EINVAL;	return 0;} /** *	cg6_fillrect -	Draws a rectangle on the screen. * *	@info: frame buffer structure that represents a single frame buffer *	@rect: structure defining the rectagle and operation. */static void cg6_fillrect(struct fb_info *info, const struct fb_fillrect *rect){	struct cg6_par *par = (struct cg6_par *)info->par;	struct cg6_fbc __iomem *fbc = par->fbc;	unsigned long flags;	s32 val; 	/* CG6 doesn't handle ROP_XOR */ 	spin_lock_irqsave(&par->lock, flags); 	cg6_sync(info); 	sbus_writel(rect->color, &fbc->fg);	sbus_writel(~(u32)0, &fbc->pixelm);	sbus_writel(0xea80ff00, &fbc->alu);	sbus_writel(0, &fbc->s);	sbus_writel(0, &fbc->clip);	sbus_writel(~(u32)0, &fbc->pm);	sbus_writel(rect->dy, &fbc->arecty);	sbus_writel(rect->dx, &fbc->arectx);	sbus_writel(rect->dy + rect->height, &fbc->arecty);	sbus_writel(rect->dx + rect->width, &fbc->arectx);	do {		val = sbus_readl(&fbc->draw);	} while (val < 0 && (val & 0x20000000));	spin_unlock_irqrestore(&par->lock, flags);} /** *	cg6_copyarea - Copies one area of the screen to another area. * *	@info: frame buffer structure that represents a single frame buffer *	@area: Structure providing the data to copy the framebuffer contents *		from one region to another. * *	This drawing operation copies a rectangular area from one area of the *	screen to another area. */static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area){	struct cg6_par *par = (struct cg6_par *)info->par;	struct cg6_fbc __iomem *fbc = par->fbc;	unsigned long flags;	int i; 	spin_lock_irqsave(&par->lock, flags); 	cg6_sync(info); 	sbus_writel(0xff, &fbc->fg);	sbus_writel(0x00, &fbc->bg);	sbus_writel(~0, &fbc->pixelm);	sbus_writel(0xe880cccc, &fbc->alu);	sbus_writel(0, &fbc->s);	sbus_writel(0, &fbc->clip); 	sbus_writel(area->sy, &fbc->y0);	sbus_writel(area->sx, &fbc->x0);	sbus_writel(area->sy + area->height - 1, &fbc->y1);	sbus_writel(area->sx + area->width - 1, &fbc->x1);	sbus_writel(area->dy, &fbc->y2);	sbus_writel(area->dx, &fbc->x2);	sbus_writel(area->dy + area->height - 1, &fbc->y3);	sbus_writel(area->dx + area->width - 1, &fbc->x3);	do {		i = sbus_readl(&fbc->blit);	} while (i < 0 && (i & 0x20000000));	spin_unlock_irqrestore(&par->lock, flags);} /** *	cg6_imageblit -	Copies a image from system memory to the screen. * *	@info: frame buffer structure that represents a single frame buffer *	@image: structure defining the image. */static void cg6_imageblit(struct fb_info *info, const struct fb_image *image){	struct cg6_par *par = (struct cg6_par *)info->par;	struct cg6_fbc __iomem *fbc = par->fbc;	const u8 *data = image->data;	unsigned long flags;	u32 x, y;	int i, width; 	if (image->depth > 1) {		cfb_imageblit(info, image);		return;	} 	spin_lock_irqsave(&par->lock, flags); 	cg6_sync(info); 	sbus_writel(image->fg_color, &fbc->fg);	sbus_writel(image->bg_color, &fbc->bg);	sbus_writel(0x140000, &fbc->mode);	sbus_writel(0xe880fc30, &fbc->alu);	sbus_writel(~(u32)0, &fbc->pixelm);	sbus_writel(0, &fbc->s);	sbus_writel(0, &fbc->clip);	sbus_writel(0xff, &fbc->pm);	sbus_writel(32, &fbc->incx);	sbus_writel(0, &fbc->incy); 	x = image->dx;	y = image->dy;	for (i = 0; i < image->height; i++) {		width = image->width; 		while (width >= 32) {			u32 val; 			sbus_writel(y, &fbc->y0);			sbus_writel(x, &fbc->x0);			sbus_writel(x + 32 - 1, &fbc->x1); 			val = ((u32)data[0] << 24) |			      ((u32)data[1] << 16) |			      ((u32)data[2] <<  8) |			      ((u32)data[3] <<  0);			sbus_writel(val, &fbc->font); 			data += 4;			x += 32;			width -= 32;		}		if (width) {			u32 val; 			sbus_writel(y, &fbc->y0);			sbus_writel(x, &fbc->x0);			sbus_writel(x + width - 1, &fbc->x1);			if (width <= 8) {				val = (u32) data[0] << 24;				data += 1;			} else if (width <= 16) {				val = ((u32) data[0] << 24) |				      ((u32) data[1] << 16);				data += 2;			} else {				val = ((u32) data[0] << 24) |				      ((u32) data[1] << 16) |				      ((u32) data[2] <<  8);				data += 3;			}			sbus_writel(val, &fbc->font);		} 		y += 1;		x = image->dx;	} 	spin_unlock_irqrestore(&par->lock, flags);} /** *	cg6_setcolreg - Sets a color register. * *	@regno: boolean, 0 copy local, 1 get_user() function *	@red: frame buffer colormap structure *	@green: The green value which can be up to 16 bits wide *	@blue:  The blue value which can be up to 16 bits wide. *	@transp: If supported the alpha value which can be up to 16 bits wide. *	@info: frame buffer info structure */static int cg6_setcolreg(unsigned regno,			 unsigned red, unsigned green, unsigned blue,			 unsigned transp, struct fb_info *info){	struct cg6_par *par = (struct cg6_par *)info->par;	struct bt_regs __iomem *bt = par->bt;	unsigned long flags; 	if (regno >= 256)		return 1; 	red >>= 8;	green >>= 8;	blue >>= 8; 	spin_lock_irqsave(&par->lock, flags); 

Lines 1–500 of 883.