162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *  linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl>
762306a36Sopenharmony_ci *	based on pm2fb.c
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Based on code written by:
1062306a36Sopenharmony_ci *	   Sven Luther, <luther@dpt-info.u-strasbg.fr>
1162306a36Sopenharmony_ci *	   Alan Hourihane, <alanh@fairlite.demon.co.uk>
1262306a36Sopenharmony_ci *	   Russell King, <rmk@arm.linux.org.uk>
1362306a36Sopenharmony_ci *  Based on linux/drivers/video/skeletonfb.c:
1462306a36Sopenharmony_ci *	Copyright (C) 1997 Geert Uytterhoeven
1562306a36Sopenharmony_ci *  Based on linux/driver/video/pm2fb.c:
1662306a36Sopenharmony_ci *	Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
1762306a36Sopenharmony_ci *	Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
2062306a36Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
2162306a36Sopenharmony_ci *  more details.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/aperture.h>
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/errno.h>
2962306a36Sopenharmony_ci#include <linux/string.h>
3062306a36Sopenharmony_ci#include <linux/mm.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/delay.h>
3362306a36Sopenharmony_ci#include <linux/fb.h>
3462306a36Sopenharmony_ci#include <linux/init.h>
3562306a36Sopenharmony_ci#include <linux/pci.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include <video/pm3fb.h>
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#if !defined(CONFIG_PCI)
4062306a36Sopenharmony_ci#error "Only generic PCI cards supported."
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#undef PM3FB_MASTER_DEBUG
4462306a36Sopenharmony_ci#ifdef PM3FB_MASTER_DEBUG
4562306a36Sopenharmony_ci#define DPRINTK(a, b...)	\
4662306a36Sopenharmony_ci	printk(KERN_DEBUG "pm3fb: %s: " a, __func__ , ## b)
4762306a36Sopenharmony_ci#else
4862306a36Sopenharmony_ci#define DPRINTK(a, b...)	no_printk(a, ##b)
4962306a36Sopenharmony_ci#endif
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define PM3_PIXMAP_SIZE	(2048 * 4)
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/*
5462306a36Sopenharmony_ci * Driver data
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_cistatic int hwcursor = 1;
5762306a36Sopenharmony_cistatic char *mode_option;
5862306a36Sopenharmony_cistatic bool noaccel;
5962306a36Sopenharmony_cistatic bool nomtrr;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * This structure defines the hardware state of the graphics card. Normally
6362306a36Sopenharmony_ci * you place this in a header file in linux/include/video. This file usually
6462306a36Sopenharmony_ci * also includes register information. That allows other driver subsystems
6562306a36Sopenharmony_ci * and userland applications the ability to use the same header file to
6662306a36Sopenharmony_ci * avoid duplicate work and easy porting of software.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistruct pm3_par {
6962306a36Sopenharmony_ci	unsigned char	__iomem *v_regs;/* virtual address of p_regs */
7062306a36Sopenharmony_ci	u32		video;		/* video flags before blanking */
7162306a36Sopenharmony_ci	u32		base;		/* screen base in 128 bits unit */
7262306a36Sopenharmony_ci	u32		palette[16];
7362306a36Sopenharmony_ci	int		wc_cookie;
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
7862306a36Sopenharmony_ci * if we don't use modedb. If we do use modedb see pm3fb_init how to use it
7962306a36Sopenharmony_ci * to get a fb_var_screeninfo. Otherwise define a default var as well.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic struct fb_fix_screeninfo pm3fb_fix = {
8262306a36Sopenharmony_ci	.id =		"Permedia3",
8362306a36Sopenharmony_ci	.type =		FB_TYPE_PACKED_PIXELS,
8462306a36Sopenharmony_ci	.visual =	FB_VISUAL_PSEUDOCOLOR,
8562306a36Sopenharmony_ci	.xpanstep =	1,
8662306a36Sopenharmony_ci	.ypanstep =	1,
8762306a36Sopenharmony_ci	.ywrapstep =	0,
8862306a36Sopenharmony_ci	.accel =	FB_ACCEL_3DLABS_PERMEDIA3,
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/*
9262306a36Sopenharmony_ci * Utility functions
9362306a36Sopenharmony_ci */
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline u32 PM3_READ_REG(struct pm3_par *par, s32 off)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	return fb_readl(par->v_regs + off);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	fb_writel(v, par->v_regs + off);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic inline void PM3_WAIT(struct pm3_par *par, u32 n)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	while (PM3_READ_REG(par, PM3InFIFOSpace) < n)
10862306a36Sopenharmony_ci		cpu_relax();
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	PM3_WAIT(par, 3);
11462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_IndexHigh, (r >> 8) & 0xff);
11562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_IndexLow, r & 0xff);
11662306a36Sopenharmony_ci	wmb();
11762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_IndexedData, v);
11862306a36Sopenharmony_ci	wmb();
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno,
12262306a36Sopenharmony_ci			unsigned char r, unsigned char g, unsigned char b)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	PM3_WAIT(par, 4);
12562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno);
12662306a36Sopenharmony_ci	wmb();
12762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_PaletteData, r);
12862306a36Sopenharmony_ci	wmb();
12962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_PaletteData, g);
13062306a36Sopenharmony_ci	wmb();
13162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RD_PaletteData, b);
13262306a36Sopenharmony_ci	wmb();
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic void pm3fb_clear_colormap(struct pm3_par *par,
13662306a36Sopenharmony_ci			unsigned char r, unsigned char g, unsigned char b)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	int i;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (i = 0; i < 256 ; i++)
14162306a36Sopenharmony_ci		pm3fb_set_color(par, i, r, g, b);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/* Calculating various clock parameters */
14662306a36Sopenharmony_cistatic void pm3fb_calculate_clock(unsigned long reqclock,
14762306a36Sopenharmony_ci				unsigned char *prescale,
14862306a36Sopenharmony_ci				unsigned char *feedback,
14962306a36Sopenharmony_ci				unsigned char *postscale)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int f, pre, post;
15262306a36Sopenharmony_ci	unsigned long freq;
15362306a36Sopenharmony_ci	long freqerr = 1000;
15462306a36Sopenharmony_ci	long currerr;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	for (f = 1; f < 256; f++) {
15762306a36Sopenharmony_ci		for (pre = 1; pre < 256; pre++) {
15862306a36Sopenharmony_ci			for (post = 0; post < 5; post++) {
15962306a36Sopenharmony_ci				freq = ((2*PM3_REF_CLOCK * f) >> post) / pre;
16062306a36Sopenharmony_ci				currerr = (reqclock > freq)
16162306a36Sopenharmony_ci					? reqclock - freq
16262306a36Sopenharmony_ci					: freq - reqclock;
16362306a36Sopenharmony_ci				if (currerr < freqerr) {
16462306a36Sopenharmony_ci					freqerr = currerr;
16562306a36Sopenharmony_ci					*feedback = f;
16662306a36Sopenharmony_ci					*prescale = pre;
16762306a36Sopenharmony_ci					*postscale = post;
16862306a36Sopenharmony_ci				}
16962306a36Sopenharmony_ci			}
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic inline int pm3fb_depth(const struct fb_var_screeninfo *var)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	if (var->bits_per_pixel == 16)
17762306a36Sopenharmony_ci		return var->red.length + var->green.length
17862306a36Sopenharmony_ci			+ var->blue.length;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return var->bits_per_pixel;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic inline int pm3fb_shift_bpp(unsigned bpp, int v)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	switch (bpp) {
18662306a36Sopenharmony_ci	case 8:
18762306a36Sopenharmony_ci		return (v >> 4);
18862306a36Sopenharmony_ci	case 16:
18962306a36Sopenharmony_ci		return (v >> 3);
19062306a36Sopenharmony_ci	case 32:
19162306a36Sopenharmony_ci		return (v >> 2);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci	DPRINTK("Unsupported depth %u\n", bpp);
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/* acceleration */
19862306a36Sopenharmony_cistatic int pm3fb_sync(struct fb_info *info)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct pm3_par *par = info->par;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	PM3_WAIT(par, 2);
20362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
20462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Sync, 0);
20562306a36Sopenharmony_ci	mb();
20662306a36Sopenharmony_ci	do {
20762306a36Sopenharmony_ci		while ((PM3_READ_REG(par, PM3OutFIFOWords)) == 0)
20862306a36Sopenharmony_ci			cpu_relax();
20962306a36Sopenharmony_ci	} while ((PM3_READ_REG(par, PM3OutputFifo)) != PM3Sync_Tag);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	return 0;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cistatic void pm3fb_init_engine(struct fb_info *info)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct pm3_par *par = info->par;
21762306a36Sopenharmony_ci	const u32 width = (info->var.xres_virtual + 7) & ~7;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	PM3_WAIT(par, 50);
22062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
22162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StatisticMode, 0x0);
22262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3DeltaMode, 0x0);
22362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RasterizerMode, 0x0);
22462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScissorMode, 0x0);
22562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LineStippleMode, 0x0);
22662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3AreaStippleMode, 0x0);
22762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3GIDMode, 0x0);
22862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3DepthMode, 0x0);
22962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StencilMode, 0x0);
23062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StencilData, 0x0);
23162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ColorDDAMode, 0x0);
23262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCoordMode, 0x0);
23362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureIndexMode0, 0x0);
23462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureIndexMode1, 0x0);
23562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureReadMode, 0x0);
23662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LUTMode, 0x0);
23762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureFilterMode, 0x0);
23862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCompositeMode, 0x0);
23962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureApplicationMode, 0x0);
24062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCompositeColorMode1, 0x0);
24162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode1, 0x0);
24262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCompositeColorMode0, 0x0);
24362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode0, 0x0);
24462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FogMode, 0x0);
24562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ChromaTestMode, 0x0);
24662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3AlphaTestMode, 0x0);
24762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3AntialiasMode, 0x0);
24862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3YUVMode, 0x0);
24962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3AlphaBlendColorMode, 0x0);
25062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3AlphaBlendAlphaMode, 0x0);
25162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3DitherMode, 0x0);
25262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LogicalOpMode, 0x0);
25362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RouterMode, 0x0);
25462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Window, 0x0);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Config2D, 0x0);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3SpanColorMask, 0xffffffff);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3XBias, 0x0);
26162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3YBias, 0x0);
26262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3DeltaControl, 0x0);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3BitMaskPattern, 0xffffffff);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBDestReadEnables,
26762306a36Sopenharmony_ci			   PM3FBDestReadEnables_E(0xff) |
26862306a36Sopenharmony_ci			   PM3FBDestReadEnables_R(0xff) |
26962306a36Sopenharmony_ci			   PM3FBDestReadEnables_ReferenceAlpha(0xff));
27062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBDestReadBufferAddr0, 0x0);
27162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBDestReadBufferOffset0, 0x0);
27262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBDestReadBufferWidth0,
27362306a36Sopenharmony_ci			   PM3FBDestReadBufferWidth_Width(width));
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBDestReadMode,
27662306a36Sopenharmony_ci			   PM3FBDestReadMode_ReadEnable |
27762306a36Sopenharmony_ci			   PM3FBDestReadMode_Enable0);
27862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSourceReadBufferAddr, 0x0);
27962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset, 0x0);
28062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSourceReadBufferWidth,
28162306a36Sopenharmony_ci			   PM3FBSourceReadBufferWidth_Width(width));
28262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSourceReadMode,
28362306a36Sopenharmony_ci			   PM3FBSourceReadMode_Blocking |
28462306a36Sopenharmony_ci			   PM3FBSourceReadMode_ReadEnable);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	PM3_WAIT(par, 2);
28762306a36Sopenharmony_ci	{
28862306a36Sopenharmony_ci		/* invert bits in bitmask */
28962306a36Sopenharmony_ci		unsigned long rm = 1 | (3 << 7);
29062306a36Sopenharmony_ci		switch (info->var.bits_per_pixel) {
29162306a36Sopenharmony_ci		case 8:
29262306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3PixelSize,
29362306a36Sopenharmony_ci					   PM3PixelSize_GLOBAL_8BIT);
29462306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
29562306a36Sopenharmony_ci			rm |= 3 << 15;
29662306a36Sopenharmony_ci#endif
29762306a36Sopenharmony_ci			break;
29862306a36Sopenharmony_ci		case 16:
29962306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3PixelSize,
30062306a36Sopenharmony_ci					   PM3PixelSize_GLOBAL_16BIT);
30162306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
30262306a36Sopenharmony_ci			rm |= 2 << 15;
30362306a36Sopenharmony_ci#endif
30462306a36Sopenharmony_ci			break;
30562306a36Sopenharmony_ci		case 32:
30662306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3PixelSize,
30762306a36Sopenharmony_ci					   PM3PixelSize_GLOBAL_32BIT);
30862306a36Sopenharmony_ci			break;
30962306a36Sopenharmony_ci		default:
31062306a36Sopenharmony_ci			DPRINTK("Unsupported depth %d\n",
31162306a36Sopenharmony_ci				info->var.bits_per_pixel);
31262306a36Sopenharmony_ci			break;
31362306a36Sopenharmony_ci		}
31462306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3RasterizerMode, rm);
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	PM3_WAIT(par, 20);
31862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSoftwareWriteMask, 0xffffffff);
31962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBHardwareWriteMask, 0xffffffff);
32062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBWriteMode,
32162306a36Sopenharmony_ci			   PM3FBWriteMode_WriteEnable |
32262306a36Sopenharmony_ci			   PM3FBWriteMode_OpaqueSpan |
32362306a36Sopenharmony_ci			   PM3FBWriteMode_Enable0);
32462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBWriteBufferAddr0, 0x0);
32562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBWriteBufferOffset0, 0x0);
32662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBWriteBufferWidth0,
32762306a36Sopenharmony_ci			   PM3FBWriteBufferWidth_Width(width));
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 0x0);
33062306a36Sopenharmony_ci	{
33162306a36Sopenharmony_ci		/* size in lines of FB */
33262306a36Sopenharmony_ci		unsigned long sofb = info->screen_size /
33362306a36Sopenharmony_ci			info->fix.line_length;
33462306a36Sopenharmony_ci		if (sofb > 4095)
33562306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 4095);
33662306a36Sopenharmony_ci		else
33762306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3SizeOfFramebuffer, sofb);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci		switch (info->var.bits_per_pixel) {
34062306a36Sopenharmony_ci		case 8:
34162306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3DitherMode,
34262306a36Sopenharmony_ci					   (1 << 10) | (2 << 3));
34362306a36Sopenharmony_ci			break;
34462306a36Sopenharmony_ci		case 16:
34562306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3DitherMode,
34662306a36Sopenharmony_ci					   (1 << 10) | (1 << 3));
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci		case 32:
34962306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3DitherMode,
35062306a36Sopenharmony_ci					   (1 << 10) | (0 << 3));
35162306a36Sopenharmony_ci			break;
35262306a36Sopenharmony_ci		default:
35362306a36Sopenharmony_ci			DPRINTK("Unsupported depth %d\n",
35462306a36Sopenharmony_ci				info->var.bits_per_pixel);
35562306a36Sopenharmony_ci			break;
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3dXDom, 0x0);
36062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3dXSub, 0x0);
36162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3dY, 1 << 16);
36262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StartXDom, 0x0);
36362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StartXSub, 0x0);
36462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3StartY, 0x0);
36562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Count, 0x0);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/* Disable LocalBuffer. better safe than sorry */
36862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LBDestReadMode, 0x0);
36962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LBDestReadEnables, 0x0);
37062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LBSourceReadMode, 0x0);
37162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3LBWriteMode, 0x0);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	pm3fb_sync(info);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void pm3fb_fillrect(struct fb_info *info,
37762306a36Sopenharmony_ci				const struct fb_fillrect *region)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct pm3_par *par = info->par;
38062306a36Sopenharmony_ci	struct fb_fillrect modded;
38162306a36Sopenharmony_ci	int vxres, vyres;
38262306a36Sopenharmony_ci	int rop;
38362306a36Sopenharmony_ci	u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
38462306a36Sopenharmony_ci		((u32 *)info->pseudo_palette)[region->color] : region->color;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (info->state != FBINFO_STATE_RUNNING)
38762306a36Sopenharmony_ci		return;
38862306a36Sopenharmony_ci	if (info->flags & FBINFO_HWACCEL_DISABLED) {
38962306a36Sopenharmony_ci		cfb_fillrect(info, region);
39062306a36Sopenharmony_ci		return;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	if (region->rop == ROP_COPY )
39362306a36Sopenharmony_ci		rop = PM3Config2D_ForegroundROP(0x3); /* GXcopy */
39462306a36Sopenharmony_ci	else
39562306a36Sopenharmony_ci		rop = PM3Config2D_ForegroundROP(0x6) | /* GXxor */
39662306a36Sopenharmony_ci			PM3Config2D_FBDestReadEnable;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	vxres = info->var.xres_virtual;
39962306a36Sopenharmony_ci	vyres = info->var.yres_virtual;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	memcpy(&modded, region, sizeof(struct fb_fillrect));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	if (!modded.width || !modded.height ||
40462306a36Sopenharmony_ci	    modded.dx >= vxres || modded.dy >= vyres)
40562306a36Sopenharmony_ci		return;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (modded.dx + modded.width  > vxres)
40862306a36Sopenharmony_ci		modded.width  = vxres - modded.dx;
40962306a36Sopenharmony_ci	if (modded.dy + modded.height > vyres)
41062306a36Sopenharmony_ci		modded.height = vyres - modded.dy;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (info->var.bits_per_pixel == 8)
41362306a36Sopenharmony_ci		color |= color << 8;
41462306a36Sopenharmony_ci	if (info->var.bits_per_pixel <= 16)
41562306a36Sopenharmony_ci		color |= color << 16;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	PM3_WAIT(par, 4);
41862306a36Sopenharmony_ci	/* ROP Ox3 is GXcopy */
41962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Config2D,
42062306a36Sopenharmony_ci			PM3Config2D_UseConstantSource |
42162306a36Sopenharmony_ci			PM3Config2D_ForegroundROPEnable |
42262306a36Sopenharmony_ci			rop |
42362306a36Sopenharmony_ci			PM3Config2D_FBWriteEnable);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ForegroundColor, color);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RectanglePosition,
42862306a36Sopenharmony_ci			PM3RectanglePosition_XOffset(modded.dx) |
42962306a36Sopenharmony_ci			PM3RectanglePosition_YOffset(modded.dy));
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Render2D,
43262306a36Sopenharmony_ci		      PM3Render2D_XPositive |
43362306a36Sopenharmony_ci		      PM3Render2D_YPositive |
43462306a36Sopenharmony_ci		      PM3Render2D_Operation_Normal |
43562306a36Sopenharmony_ci		      PM3Render2D_SpanOperation |
43662306a36Sopenharmony_ci		      PM3Render2D_Width(modded.width) |
43762306a36Sopenharmony_ci		      PM3Render2D_Height(modded.height));
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic void pm3fb_copyarea(struct fb_info *info,
44162306a36Sopenharmony_ci				const struct fb_copyarea *area)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	struct pm3_par *par = info->par;
44462306a36Sopenharmony_ci	struct fb_copyarea modded;
44562306a36Sopenharmony_ci	u32 vxres, vyres;
44662306a36Sopenharmony_ci	int x_align, o_x, o_y;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	if (info->state != FBINFO_STATE_RUNNING)
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci	if (info->flags & FBINFO_HWACCEL_DISABLED) {
45162306a36Sopenharmony_ci		cfb_copyarea(info, area);
45262306a36Sopenharmony_ci		return;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	memcpy(&modded, area, sizeof(struct fb_copyarea));
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	vxres = info->var.xres_virtual;
45862306a36Sopenharmony_ci	vyres = info->var.yres_virtual;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (!modded.width || !modded.height ||
46162306a36Sopenharmony_ci	    modded.sx >= vxres || modded.sy >= vyres ||
46262306a36Sopenharmony_ci	    modded.dx >= vxres || modded.dy >= vyres)
46362306a36Sopenharmony_ci		return;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	if (modded.sx + modded.width > vxres)
46662306a36Sopenharmony_ci		modded.width = vxres - modded.sx;
46762306a36Sopenharmony_ci	if (modded.dx + modded.width > vxres)
46862306a36Sopenharmony_ci		modded.width = vxres - modded.dx;
46962306a36Sopenharmony_ci	if (modded.sy + modded.height > vyres)
47062306a36Sopenharmony_ci		modded.height = vyres - modded.sy;
47162306a36Sopenharmony_ci	if (modded.dy + modded.height > vyres)
47262306a36Sopenharmony_ci		modded.height = vyres - modded.dy;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	o_x = modded.sx - modded.dx;	/*(sx > dx ) ? (sx - dx) : (dx - sx); */
47562306a36Sopenharmony_ci	o_y = modded.sy - modded.dy;	/*(sy > dy ) ? (sy - dy) : (dy - sy); */
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	x_align = (modded.sx & 0x1f);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	PM3_WAIT(par, 6);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Config2D,
48262306a36Sopenharmony_ci			PM3Config2D_UserScissorEnable |
48362306a36Sopenharmony_ci			PM3Config2D_ForegroundROPEnable |
48462306a36Sopenharmony_ci			PM3Config2D_Blocking |
48562306a36Sopenharmony_ci			PM3Config2D_ForegroundROP(0x3) | /* Ox3 is GXcopy */
48662306a36Sopenharmony_ci			PM3Config2D_FBWriteEnable);
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScissorMinXY,
48962306a36Sopenharmony_ci			((modded.dy & 0x0fff) << 16) | (modded.dx & 0x0fff));
49062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScissorMaxXY,
49162306a36Sopenharmony_ci			(((modded.dy + modded.height) & 0x0fff) << 16) |
49262306a36Sopenharmony_ci			((modded.dx + modded.width) & 0x0fff));
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset,
49562306a36Sopenharmony_ci			PM3FBSourceReadBufferOffset_XOffset(o_x) |
49662306a36Sopenharmony_ci			PM3FBSourceReadBufferOffset_YOffset(o_y));
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RectanglePosition,
49962306a36Sopenharmony_ci			PM3RectanglePosition_XOffset(modded.dx - x_align) |
50062306a36Sopenharmony_ci			PM3RectanglePosition_YOffset(modded.dy));
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Render2D,
50362306a36Sopenharmony_ci			((modded.sx > modded.dx) ? PM3Render2D_XPositive : 0) |
50462306a36Sopenharmony_ci			((modded.sy > modded.dy) ? PM3Render2D_YPositive : 0) |
50562306a36Sopenharmony_ci			PM3Render2D_Operation_Normal |
50662306a36Sopenharmony_ci			PM3Render2D_SpanOperation |
50762306a36Sopenharmony_ci			PM3Render2D_FBSourceReadEnable |
50862306a36Sopenharmony_ci			PM3Render2D_Width(modded.width + x_align) |
50962306a36Sopenharmony_ci			PM3Render2D_Height(modded.height));
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic void pm3fb_imageblit(struct fb_info *info, const struct fb_image *image)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct pm3_par *par = info->par;
51562306a36Sopenharmony_ci	u32 height = image->height;
51662306a36Sopenharmony_ci	u32 fgx, bgx;
51762306a36Sopenharmony_ci	const u32 *src = (const u32 *)image->data;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if (info->state != FBINFO_STATE_RUNNING)
52062306a36Sopenharmony_ci		return;
52162306a36Sopenharmony_ci	if (info->flags & FBINFO_HWACCEL_DISABLED) {
52262306a36Sopenharmony_ci		cfb_imageblit(info, image);
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci	switch (info->fix.visual) {
52662306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
52762306a36Sopenharmony_ci		fgx = image->fg_color;
52862306a36Sopenharmony_ci		bgx = image->bg_color;
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
53162306a36Sopenharmony_ci	default:
53262306a36Sopenharmony_ci		fgx = par->palette[image->fg_color];
53362306a36Sopenharmony_ci		bgx = par->palette[image->bg_color];
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci	if (image->depth != 1) {
53762306a36Sopenharmony_ci		cfb_imageblit(info, image);
53862306a36Sopenharmony_ci		return;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	if (info->var.bits_per_pixel == 8) {
54262306a36Sopenharmony_ci		fgx |= fgx << 8;
54362306a36Sopenharmony_ci		bgx |= bgx << 8;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	if (info->var.bits_per_pixel <= 16) {
54662306a36Sopenharmony_ci		fgx |= fgx << 16;
54762306a36Sopenharmony_ci		bgx |= bgx << 16;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	PM3_WAIT(par, 7);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ForegroundColor, fgx);
55362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3BackgroundColor, bgx);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* ROP Ox3 is GXcopy */
55662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Config2D,
55762306a36Sopenharmony_ci			PM3Config2D_UserScissorEnable |
55862306a36Sopenharmony_ci			PM3Config2D_UseConstantSource |
55962306a36Sopenharmony_ci			PM3Config2D_ForegroundROPEnable |
56062306a36Sopenharmony_ci			PM3Config2D_ForegroundROP(0x3) |
56162306a36Sopenharmony_ci			PM3Config2D_OpaqueSpan |
56262306a36Sopenharmony_ci			PM3Config2D_FBWriteEnable);
56362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScissorMinXY,
56462306a36Sopenharmony_ci			((image->dy & 0x0fff) << 16) | (image->dx & 0x0fff));
56562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScissorMaxXY,
56662306a36Sopenharmony_ci			(((image->dy + image->height) & 0x0fff) << 16) |
56762306a36Sopenharmony_ci			((image->dx + image->width) & 0x0fff));
56862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3RectanglePosition,
56962306a36Sopenharmony_ci			PM3RectanglePosition_XOffset(image->dx) |
57062306a36Sopenharmony_ci			PM3RectanglePosition_YOffset(image->dy));
57162306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Render2D,
57262306a36Sopenharmony_ci			PM3Render2D_XPositive |
57362306a36Sopenharmony_ci			PM3Render2D_YPositive |
57462306a36Sopenharmony_ci			PM3Render2D_Operation_SyncOnBitMask |
57562306a36Sopenharmony_ci			PM3Render2D_SpanOperation |
57662306a36Sopenharmony_ci			PM3Render2D_Width(image->width) |
57762306a36Sopenharmony_ci			PM3Render2D_Height(image->height));
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	while (height--) {
58162306a36Sopenharmony_ci		int width = ((image->width + 7) >> 3)
58262306a36Sopenharmony_ci				+ info->pixmap.scan_align - 1;
58362306a36Sopenharmony_ci		width >>= 2;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		while (width >= PM3_FIFO_SIZE) {
58662306a36Sopenharmony_ci			int i = PM3_FIFO_SIZE - 1;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci			PM3_WAIT(par, PM3_FIFO_SIZE);
58962306a36Sopenharmony_ci			while (i--) {
59062306a36Sopenharmony_ci				PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
59162306a36Sopenharmony_ci				src++;
59262306a36Sopenharmony_ci			}
59362306a36Sopenharmony_ci			width -= PM3_FIFO_SIZE - 1;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		PM3_WAIT(par, width + 1);
59762306a36Sopenharmony_ci		while (width--) {
59862306a36Sopenharmony_ci			PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
59962306a36Sopenharmony_ci			src++;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci/* end of acceleration functions */
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci/*
60662306a36Sopenharmony_ci *	Hardware Cursor support.
60762306a36Sopenharmony_ci */
60862306a36Sopenharmony_cistatic const u8 cursor_bits_lookup[16] = {
60962306a36Sopenharmony_ci	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
61062306a36Sopenharmony_ci	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
61162306a36Sopenharmony_ci};
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic int pm3fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	struct pm3_par *par = info->par;
61662306a36Sopenharmony_ci	u8 mode;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if (!hwcursor)
61962306a36Sopenharmony_ci		return -EINVAL;	/* just to force soft_cursor() call */
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* Too large of a cursor or wrong bpp :-( */
62262306a36Sopenharmony_ci	if (cursor->image.width > 64 ||
62362306a36Sopenharmony_ci	    cursor->image.height > 64 ||
62462306a36Sopenharmony_ci	    cursor->image.depth > 1)
62562306a36Sopenharmony_ci		return -EINVAL;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	mode = PM3RD_CursorMode_TYPE_X;
62862306a36Sopenharmony_ci	if (cursor->enable)
62962306a36Sopenharmony_ci		 mode |= PM3RD_CursorMode_CURSOR_ENABLE;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, mode);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/*
63462306a36Sopenharmony_ci	 * If the cursor is not be changed this means either we want the
63562306a36Sopenharmony_ci	 * current cursor state (if enable is set) or we want to query what
63662306a36Sopenharmony_ci	 * we can do with the cursor (if enable is not set)
63762306a36Sopenharmony_ci	 */
63862306a36Sopenharmony_ci	if (!cursor->set)
63962306a36Sopenharmony_ci		return 0;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETPOS) {
64262306a36Sopenharmony_ci		int x = cursor->image.dx - info->var.xoffset;
64362306a36Sopenharmony_ci		int y = cursor->image.dy - info->var.yoffset;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorXLow, x & 0xff);
64662306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorXHigh, (x >> 8) & 0xf);
64762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorYLow, y & 0xff);
64862306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorYHigh, (y >> 8) & 0xf);
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETHOT) {
65262306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorHotSpotX,
65362306a36Sopenharmony_ci				  cursor->hot.x & 0x3f);
65462306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorHotSpotY,
65562306a36Sopenharmony_ci				  cursor->hot.y & 0x3f);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETCMAP) {
65962306a36Sopenharmony_ci		u32 fg_idx = cursor->image.fg_color;
66062306a36Sopenharmony_ci		u32 bg_idx = cursor->image.bg_color;
66162306a36Sopenharmony_ci		struct fb_cmap cmap = info->cmap;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci		/* the X11 driver says one should use these color registers */
66462306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(39),
66562306a36Sopenharmony_ci				  cmap.red[fg_idx] >> 8 );
66662306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(40),
66762306a36Sopenharmony_ci				  cmap.green[fg_idx] >> 8 );
66862306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(41),
66962306a36Sopenharmony_ci				  cmap.blue[fg_idx] >> 8 );
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(42),
67262306a36Sopenharmony_ci				  cmap.red[bg_idx] >> 8 );
67362306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(43),
67462306a36Sopenharmony_ci				  cmap.green[bg_idx] >> 8 );
67562306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(44),
67662306a36Sopenharmony_ci				  cmap.blue[bg_idx] >> 8 );
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
68062306a36Sopenharmony_ci		u8 *bitmap = (u8 *)cursor->image.data;
68162306a36Sopenharmony_ci		u8 *mask = (u8 *)cursor->mask;
68262306a36Sopenharmony_ci		int i;
68362306a36Sopenharmony_ci		int pos = PM3RD_CursorPattern(0);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		for (i = 0; i < cursor->image.height; i++) {
68662306a36Sopenharmony_ci			int j = (cursor->image.width + 7) >> 3;
68762306a36Sopenharmony_ci			int k = 8 - j;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci			for (; j > 0; j--) {
69062306a36Sopenharmony_ci				u8 data = *bitmap ^ *mask;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci				if (cursor->rop == ROP_COPY)
69362306a36Sopenharmony_ci					data = *mask & *bitmap;
69462306a36Sopenharmony_ci				/* Upper 4 bits of bitmap data */
69562306a36Sopenharmony_ci				PM3_WRITE_DAC_REG(par, pos++,
69662306a36Sopenharmony_ci					cursor_bits_lookup[data >> 4] |
69762306a36Sopenharmony_ci					(cursor_bits_lookup[*mask >> 4] << 1));
69862306a36Sopenharmony_ci				/* Lower 4 bits of bitmap */
69962306a36Sopenharmony_ci				PM3_WRITE_DAC_REG(par, pos++,
70062306a36Sopenharmony_ci					cursor_bits_lookup[data & 0xf] |
70162306a36Sopenharmony_ci					(cursor_bits_lookup[*mask & 0xf] << 1));
70262306a36Sopenharmony_ci				bitmap++;
70362306a36Sopenharmony_ci				mask++;
70462306a36Sopenharmony_ci			}
70562306a36Sopenharmony_ci			for (; k > 0; k--) {
70662306a36Sopenharmony_ci				PM3_WRITE_DAC_REG(par, pos++, 0);
70762306a36Sopenharmony_ci				PM3_WRITE_DAC_REG(par, pos++, 0);
70862306a36Sopenharmony_ci			}
70962306a36Sopenharmony_ci		}
71062306a36Sopenharmony_ci		while (pos < PM3RD_CursorPattern(1024))
71162306a36Sopenharmony_ci			PM3_WRITE_DAC_REG(par, pos++, 0);
71262306a36Sopenharmony_ci	}
71362306a36Sopenharmony_ci	return 0;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/* write the mode to registers */
71762306a36Sopenharmony_cistatic void pm3fb_write_mode(struct fb_info *info)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct pm3_par *par = info->par;
72062306a36Sopenharmony_ci	char tempsync = 0x00;
72162306a36Sopenharmony_ci	char tempmisc = 0x00;
72262306a36Sopenharmony_ci	const u32 hsstart = info->var.right_margin;
72362306a36Sopenharmony_ci	const u32 hsend = hsstart + info->var.hsync_len;
72462306a36Sopenharmony_ci	const u32 hbend = hsend + info->var.left_margin;
72562306a36Sopenharmony_ci	const u32 xres = (info->var.xres + 31) & ~31;
72662306a36Sopenharmony_ci	const u32 htotal = xres + hbend;
72762306a36Sopenharmony_ci	const u32 vsstart = info->var.lower_margin;
72862306a36Sopenharmony_ci	const u32 vsend = vsstart + info->var.vsync_len;
72962306a36Sopenharmony_ci	const u32 vbend = vsend + info->var.upper_margin;
73062306a36Sopenharmony_ci	const u32 vtotal = info->var.yres + vbend;
73162306a36Sopenharmony_ci	const u32 width = (info->var.xres_virtual + 7) & ~7;
73262306a36Sopenharmony_ci	const unsigned bpp = info->var.bits_per_pixel;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	PM3_WAIT(par, 20);
73562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff);
73662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Aperture0, 0x00000000);
73762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3Aperture1, 0x00000000);
73862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3FIFODis, 0x00000007);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3HTotal,
74162306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, htotal - 1));
74262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3HsEnd,
74362306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, hsend));
74462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3HsStart,
74562306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, hsstart));
74662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3HbEnd,
74762306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, hbend));
74862306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3HgEnd,
74962306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, hbend));
75062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScreenStride,
75162306a36Sopenharmony_ci			   pm3fb_shift_bpp(bpp, width));
75262306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VTotal, vtotal - 1);
75362306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VsEnd, vsend - 1);
75462306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VsStart, vsstart - 1);
75562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VbEnd, vbend);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	switch (bpp) {
75862306a36Sopenharmony_ci	case 8:
75962306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture1Mode,
76062306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_8BIT);
76162306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture2Mode,
76262306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_8BIT);
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	case 16:
76662306a36Sopenharmony_ci#ifndef __BIG_ENDIAN
76762306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture1Mode,
76862306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_16BIT);
76962306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture2Mode,
77062306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_16BIT);
77162306a36Sopenharmony_ci#else
77262306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture1Mode,
77362306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_16BIT |
77462306a36Sopenharmony_ci				   PM3ByApertureMode_BYTESWAP_BADC);
77562306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture2Mode,
77662306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_16BIT |
77762306a36Sopenharmony_ci				   PM3ByApertureMode_BYTESWAP_BADC);
77862306a36Sopenharmony_ci#endif /* ! __BIG_ENDIAN */
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	case 32:
78262306a36Sopenharmony_ci#ifndef __BIG_ENDIAN
78362306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture1Mode,
78462306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_32BIT);
78562306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture2Mode,
78662306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_32BIT);
78762306a36Sopenharmony_ci#else
78862306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture1Mode,
78962306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_32BIT |
79062306a36Sopenharmony_ci				   PM3ByApertureMode_BYTESWAP_DCBA);
79162306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3ByAperture2Mode,
79262306a36Sopenharmony_ci				   PM3ByApertureMode_PIXELSIZE_32BIT |
79362306a36Sopenharmony_ci				   PM3ByApertureMode_BYTESWAP_DCBA);
79462306a36Sopenharmony_ci#endif /* ! __BIG_ENDIAN */
79562306a36Sopenharmony_ci		break;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	default:
79862306a36Sopenharmony_ci		DPRINTK("Unsupported depth %d\n", bpp);
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	/*
80362306a36Sopenharmony_ci	 * Oxygen VX1 - it appears that setting PM3VideoControl and
80462306a36Sopenharmony_ci	 * then PM3RD_SyncControl to the same SYNC settings undoes
80562306a36Sopenharmony_ci	 * any net change - they seem to xor together.  Only set the
80662306a36Sopenharmony_ci	 * sync options in PM3RD_SyncControl.  --rmk
80762306a36Sopenharmony_ci	 */
80862306a36Sopenharmony_ci	{
80962306a36Sopenharmony_ci		unsigned int video = par->video;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci		video &= ~(PM3VideoControl_HSYNC_MASK |
81262306a36Sopenharmony_ci			   PM3VideoControl_VSYNC_MASK);
81362306a36Sopenharmony_ci		video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
81462306a36Sopenharmony_ci			 PM3VideoControl_VSYNC_ACTIVE_HIGH;
81562306a36Sopenharmony_ci		PM3_WRITE_REG(par, PM3VideoControl, video);
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VClkCtl,
81862306a36Sopenharmony_ci			   (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC));
81962306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScreenBase, par->base);
82062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ChipConfig,
82162306a36Sopenharmony_ci			   (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD));
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	wmb();
82462306a36Sopenharmony_ci	{
82562306a36Sopenharmony_ci		unsigned char m;	/* ClkPreScale */
82662306a36Sopenharmony_ci		unsigned char n;	/* ClkFeedBackScale */
82762306a36Sopenharmony_ci		unsigned char p;	/* ClkPostScale */
82862306a36Sopenharmony_ci		unsigned long pixclock = PICOS2KHZ(info->var.pixclock);
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci		(void)pm3fb_calculate_clock(pixclock, &m, &n, &p);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n",
83362306a36Sopenharmony_ci			pixclock, (int) m, (int) n, (int) p);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m);
83662306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n);
83762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p);
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci	/*
84062306a36Sopenharmony_ci	   PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00);
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci	/*
84362306a36Sopenharmony_ci	   PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00);
84462306a36Sopenharmony_ci	 */
84562306a36Sopenharmony_ci	if ((par->video & PM3VideoControl_HSYNC_MASK) ==
84662306a36Sopenharmony_ci	    PM3VideoControl_HSYNC_ACTIVE_HIGH)
84762306a36Sopenharmony_ci		tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
84862306a36Sopenharmony_ci	if ((par->video & PM3VideoControl_VSYNC_MASK) ==
84962306a36Sopenharmony_ci	    PM3VideoControl_VSYNC_ACTIVE_HIGH)
85062306a36Sopenharmony_ci		tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync);
85362306a36Sopenharmony_ci	DPRINTK("PM3RD_SyncControl: %d\n", tempsync);
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	switch (pm3fb_depth(&info->var)) {
85862306a36Sopenharmony_ci	case 8:
85962306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
86062306a36Sopenharmony_ci				  PM3RD_PixelSize_8_BIT_PIXELS);
86162306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
86262306a36Sopenharmony_ci				  PM3RD_ColorFormat_CI8_COLOR |
86362306a36Sopenharmony_ci				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
86462306a36Sopenharmony_ci		tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci	case 12:
86762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
86862306a36Sopenharmony_ci				  PM3RD_PixelSize_16_BIT_PIXELS);
86962306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
87062306a36Sopenharmony_ci				  PM3RD_ColorFormat_4444_COLOR |
87162306a36Sopenharmony_ci				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
87262306a36Sopenharmony_ci				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
87362306a36Sopenharmony_ci		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
87462306a36Sopenharmony_ci			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
87562306a36Sopenharmony_ci		break;
87662306a36Sopenharmony_ci	case 15:
87762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
87862306a36Sopenharmony_ci				  PM3RD_PixelSize_16_BIT_PIXELS);
87962306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
88062306a36Sopenharmony_ci				  PM3RD_ColorFormat_5551_FRONT_COLOR |
88162306a36Sopenharmony_ci				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
88262306a36Sopenharmony_ci				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
88362306a36Sopenharmony_ci		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
88462306a36Sopenharmony_ci			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
88562306a36Sopenharmony_ci		break;
88662306a36Sopenharmony_ci	case 16:
88762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
88862306a36Sopenharmony_ci				  PM3RD_PixelSize_16_BIT_PIXELS);
88962306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
89062306a36Sopenharmony_ci				  PM3RD_ColorFormat_565_FRONT_COLOR |
89162306a36Sopenharmony_ci				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
89262306a36Sopenharmony_ci				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
89362306a36Sopenharmony_ci		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
89462306a36Sopenharmony_ci			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
89562306a36Sopenharmony_ci		break;
89662306a36Sopenharmony_ci	case 32:
89762306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
89862306a36Sopenharmony_ci				  PM3RD_PixelSize_32_BIT_PIXELS);
89962306a36Sopenharmony_ci		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
90062306a36Sopenharmony_ci				  PM3RD_ColorFormat_8888_COLOR |
90162306a36Sopenharmony_ci				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
90262306a36Sopenharmony_ci		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
90362306a36Sopenharmony_ci			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
90462306a36Sopenharmony_ci		break;
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci	PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc);
90762306a36Sopenharmony_ci}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci/*
91062306a36Sopenharmony_ci * hardware independent functions
91162306a36Sopenharmony_ci */
91262306a36Sopenharmony_cistatic int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	u32 lpitch;
91562306a36Sopenharmony_ci	unsigned bpp = var->red.length + var->green.length
91662306a36Sopenharmony_ci			+ var->blue.length + var->transp.length;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (bpp != var->bits_per_pixel) {
91962306a36Sopenharmony_ci		/* set predefined mode for bits_per_pixel settings */
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		switch (var->bits_per_pixel) {
92262306a36Sopenharmony_ci		case 8:
92362306a36Sopenharmony_ci			var->red.length = 8;
92462306a36Sopenharmony_ci			var->green.length = 8;
92562306a36Sopenharmony_ci			var->blue.length = 8;
92662306a36Sopenharmony_ci			var->red.offset = 0;
92762306a36Sopenharmony_ci			var->green.offset = 0;
92862306a36Sopenharmony_ci			var->blue.offset = 0;
92962306a36Sopenharmony_ci			var->transp.offset = 0;
93062306a36Sopenharmony_ci			var->transp.length = 0;
93162306a36Sopenharmony_ci			break;
93262306a36Sopenharmony_ci		case 16:
93362306a36Sopenharmony_ci			var->red.length = 5;
93462306a36Sopenharmony_ci			var->blue.length = 5;
93562306a36Sopenharmony_ci			var->green.length = 6;
93662306a36Sopenharmony_ci			var->transp.length = 0;
93762306a36Sopenharmony_ci			break;
93862306a36Sopenharmony_ci		case 32:
93962306a36Sopenharmony_ci			var->red.length = 8;
94062306a36Sopenharmony_ci			var->green.length = 8;
94162306a36Sopenharmony_ci			var->blue.length = 8;
94262306a36Sopenharmony_ci			var->transp.length = 8;
94362306a36Sopenharmony_ci			break;
94462306a36Sopenharmony_ci		default:
94562306a36Sopenharmony_ci			DPRINTK("depth not supported: %u\n",
94662306a36Sopenharmony_ci				var->bits_per_pixel);
94762306a36Sopenharmony_ci			return -EINVAL;
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci	/* it is assumed BGRA order */
95162306a36Sopenharmony_ci	if (var->bits_per_pixel > 8 ) {
95262306a36Sopenharmony_ci		var->blue.offset = 0;
95362306a36Sopenharmony_ci		var->green.offset = var->blue.length;
95462306a36Sopenharmony_ci		var->red.offset = var->green.offset + var->green.length;
95562306a36Sopenharmony_ci		var->transp.offset = var->red.offset + var->red.length;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	var->height = -1;
95862306a36Sopenharmony_ci	var->width = -1;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	if (var->xres != var->xres_virtual) {
96162306a36Sopenharmony_ci		DPRINTK("virtual x resolution != "
96262306a36Sopenharmony_ci			"physical x resolution not supported\n");
96362306a36Sopenharmony_ci		return -EINVAL;
96462306a36Sopenharmony_ci	}
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (var->yres > var->yres_virtual) {
96762306a36Sopenharmony_ci		DPRINTK("virtual y resolution < "
96862306a36Sopenharmony_ci			"physical y resolution not possible\n");
96962306a36Sopenharmony_ci		return -EINVAL;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	if (var->xoffset) {
97362306a36Sopenharmony_ci		DPRINTK("xoffset not supported\n");
97462306a36Sopenharmony_ci		return -EINVAL;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
97862306a36Sopenharmony_ci		DPRINTK("interlace not supported\n");
97962306a36Sopenharmony_ci		return -EINVAL;
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */
98362306a36Sopenharmony_ci	lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (var->xres < 200 || var->xres > 2048) {
98662306a36Sopenharmony_ci		DPRINTK("width not supported: %u\n", var->xres);
98762306a36Sopenharmony_ci		return -EINVAL;
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (var->yres < 200 || var->yres > 4095) {
99162306a36Sopenharmony_ci		DPRINTK("height not supported: %u\n", var->yres);
99262306a36Sopenharmony_ci		return -EINVAL;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (lpitch * var->yres_virtual > info->fix.smem_len) {
99662306a36Sopenharmony_ci		DPRINTK("no memory for screen (%ux%ux%u)\n",
99762306a36Sopenharmony_ci			var->xres, var->yres_virtual, var->bits_per_pixel);
99862306a36Sopenharmony_ci		return -EINVAL;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) {
100262306a36Sopenharmony_ci		DPRINTK("pixclock too high (%ldKHz)\n",
100362306a36Sopenharmony_ci			PICOS2KHZ(var->pixclock));
100462306a36Sopenharmony_ci		return -EINVAL;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	var->accel_flags = 0;	/* Can't mmap if this is on */
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	DPRINTK("Checking graphics mode at %dx%d depth %d\n",
101062306a36Sopenharmony_ci		var->xres, var->yres, var->bits_per_pixel);
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic int pm3fb_set_par(struct fb_info *info)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct pm3_par *par = info->par;
101762306a36Sopenharmony_ci	const u32 xres = (info->var.xres + 31) & ~31;
101862306a36Sopenharmony_ci	const unsigned bpp = info->var.bits_per_pixel;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	par->base = pm3fb_shift_bpp(bpp, (info->var.yoffset * xres)
102162306a36Sopenharmony_ci					+ info->var.xoffset);
102262306a36Sopenharmony_ci	par->video = 0;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
102562306a36Sopenharmony_ci		par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH;
102662306a36Sopenharmony_ci	else
102762306a36Sopenharmony_ci		par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
103062306a36Sopenharmony_ci		par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH;
103162306a36Sopenharmony_ci	else
103262306a36Sopenharmony_ci		par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
103562306a36Sopenharmony_ci		par->video |= PM3VideoControl_LINE_DOUBLE_ON;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
103862306a36Sopenharmony_ci		par->video |= PM3VideoControl_ENABLE;
103962306a36Sopenharmony_ci	else
104062306a36Sopenharmony_ci		DPRINTK("PM3Video disabled\n");
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	switch (bpp) {
104362306a36Sopenharmony_ci	case 8:
104462306a36Sopenharmony_ci		par->video |= PM3VideoControl_PIXELSIZE_8BIT;
104562306a36Sopenharmony_ci		break;
104662306a36Sopenharmony_ci	case 16:
104762306a36Sopenharmony_ci		par->video |= PM3VideoControl_PIXELSIZE_16BIT;
104862306a36Sopenharmony_ci		break;
104962306a36Sopenharmony_ci	case 32:
105062306a36Sopenharmony_ci		par->video |= PM3VideoControl_PIXELSIZE_32BIT;
105162306a36Sopenharmony_ci		break;
105262306a36Sopenharmony_ci	default:
105362306a36Sopenharmony_ci		DPRINTK("Unsupported depth\n");
105462306a36Sopenharmony_ci		break;
105562306a36Sopenharmony_ci	}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	info->fix.visual =
105862306a36Sopenharmony_ci		(bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
105962306a36Sopenharmony_ci	info->fix.line_length = ((info->var.xres_virtual + 7)  >> 3) * bpp;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci/*	pm3fb_clear_memory(info, 0);*/
106262306a36Sopenharmony_ci	pm3fb_clear_colormap(par, 0, 0, 0);
106362306a36Sopenharmony_ci	PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, 0);
106462306a36Sopenharmony_ci	pm3fb_init_engine(info);
106562306a36Sopenharmony_ci	pm3fb_write_mode(info);
106662306a36Sopenharmony_ci	return 0;
106762306a36Sopenharmony_ci}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green,
107062306a36Sopenharmony_ci			   unsigned blue, unsigned transp,
107162306a36Sopenharmony_ci			   struct fb_info *info)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	struct pm3_par *par = info->par;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	if (regno >= 256)  /* no. of hw registers */
107662306a36Sopenharmony_ci	   return -EINVAL;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* grayscale works only partially under directcolor */
107962306a36Sopenharmony_ci	/* grayscale = 0.30*R + 0.59*G + 0.11*B */
108062306a36Sopenharmony_ci	if (info->var.grayscale)
108162306a36Sopenharmony_ci	   red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* Directcolor:
108462306a36Sopenharmony_ci	 *   var->{color}.offset contains start of bitfield
108562306a36Sopenharmony_ci	 *   var->{color}.length contains length of bitfield
108662306a36Sopenharmony_ci	 *   {hardwarespecific} contains width of DAC
108762306a36Sopenharmony_ci	 *   pseudo_palette[X] is programmed to (X << red.offset) |
108862306a36Sopenharmony_ci	 *					(X << green.offset) |
108962306a36Sopenharmony_ci	 *					(X << blue.offset)
109062306a36Sopenharmony_ci	 *   RAMDAC[X] is programmed to (red, green, blue)
109162306a36Sopenharmony_ci	 *   color depth = SUM(var->{color}.length)
109262306a36Sopenharmony_ci	 *
109362306a36Sopenharmony_ci	 * Pseudocolor:
109462306a36Sopenharmony_ci	 *	var->{color}.offset is 0
109562306a36Sopenharmony_ci	 *	var->{color}.length contains width of DAC or the number
109662306a36Sopenharmony_ci	 *			of unique colors available (color depth)
109762306a36Sopenharmony_ci	 *	pseudo_palette is not used
109862306a36Sopenharmony_ci	 *	RAMDAC[X] is programmed to (red, green, blue)
109962306a36Sopenharmony_ci	 *	color depth = var->{color}.length
110062306a36Sopenharmony_ci	 */
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/*
110362306a36Sopenharmony_ci	 * This is the point where the color is converted to something that
110462306a36Sopenharmony_ci	 * is acceptable by the hardware.
110562306a36Sopenharmony_ci	 */
110662306a36Sopenharmony_ci#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
110762306a36Sopenharmony_ci	red = CNVT_TOHW(red, info->var.red.length);
110862306a36Sopenharmony_ci	green = CNVT_TOHW(green, info->var.green.length);
110962306a36Sopenharmony_ci	blue = CNVT_TOHW(blue, info->var.blue.length);
111062306a36Sopenharmony_ci	transp = CNVT_TOHW(transp, info->var.transp.length);
111162306a36Sopenharmony_ci#undef CNVT_TOHW
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
111462306a36Sopenharmony_ci	info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
111562306a36Sopenharmony_ci		u32 v;
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		if (regno >= 16)
111862306a36Sopenharmony_ci			return -EINVAL;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		v = (red << info->var.red.offset) |
112162306a36Sopenharmony_ci			(green << info->var.green.offset) |
112262306a36Sopenharmony_ci			(blue << info->var.blue.offset) |
112362306a36Sopenharmony_ci			(transp << info->var.transp.offset);
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		switch (info->var.bits_per_pixel) {
112662306a36Sopenharmony_ci		case 8:
112762306a36Sopenharmony_ci			break;
112862306a36Sopenharmony_ci		case 16:
112962306a36Sopenharmony_ci		case 32:
113062306a36Sopenharmony_ci			((u32 *)(info->pseudo_palette))[regno] = v;
113162306a36Sopenharmony_ci			break;
113262306a36Sopenharmony_ci		}
113362306a36Sopenharmony_ci		return 0;
113462306a36Sopenharmony_ci	} else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
113562306a36Sopenharmony_ci		pm3fb_set_color(par, regno, red, green, blue);
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	return 0;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int pm3fb_pan_display(struct fb_var_screeninfo *var,
114162306a36Sopenharmony_ci				 struct fb_info *info)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	struct pm3_par *par = info->par;
114462306a36Sopenharmony_ci	const u32 xres = (info->var.xres + 31) & ~31;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	par->base = pm3fb_shift_bpp(info->var.bits_per_pixel,
114762306a36Sopenharmony_ci					(var->yoffset * xres)
114862306a36Sopenharmony_ci					+ var->xoffset);
114962306a36Sopenharmony_ci	PM3_WAIT(par, 1);
115062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3ScreenBase, par->base);
115162306a36Sopenharmony_ci	return 0;
115262306a36Sopenharmony_ci}
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cistatic int pm3fb_blank(int blank_mode, struct fb_info *info)
115562306a36Sopenharmony_ci{
115662306a36Sopenharmony_ci	struct pm3_par *par = info->par;
115762306a36Sopenharmony_ci	u32 video = par->video;
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/*
116062306a36Sopenharmony_ci	 * Oxygen VX1 - it appears that setting PM3VideoControl and
116162306a36Sopenharmony_ci	 * then PM3RD_SyncControl to the same SYNC settings undoes
116262306a36Sopenharmony_ci	 * any net change - they seem to xor together.  Only set the
116362306a36Sopenharmony_ci	 * sync options in PM3RD_SyncControl.  --rmk
116462306a36Sopenharmony_ci	 */
116562306a36Sopenharmony_ci	video &= ~(PM3VideoControl_HSYNC_MASK |
116662306a36Sopenharmony_ci		   PM3VideoControl_VSYNC_MASK);
116762306a36Sopenharmony_ci	video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
116862306a36Sopenharmony_ci		 PM3VideoControl_VSYNC_ACTIVE_HIGH;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	switch (blank_mode) {
117162306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:
117262306a36Sopenharmony_ci		video |= PM3VideoControl_ENABLE;
117362306a36Sopenharmony_ci		break;
117462306a36Sopenharmony_ci	case FB_BLANK_NORMAL:
117562306a36Sopenharmony_ci		video &= ~PM3VideoControl_ENABLE;
117662306a36Sopenharmony_ci		break;
117762306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
117862306a36Sopenharmony_ci		video &= ~(PM3VideoControl_HSYNC_MASK |
117962306a36Sopenharmony_ci			  PM3VideoControl_BLANK_ACTIVE_LOW);
118062306a36Sopenharmony_ci		break;
118162306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
118262306a36Sopenharmony_ci		video &= ~(PM3VideoControl_VSYNC_MASK |
118362306a36Sopenharmony_ci			  PM3VideoControl_BLANK_ACTIVE_LOW);
118462306a36Sopenharmony_ci		break;
118562306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:
118662306a36Sopenharmony_ci		video &= ~(PM3VideoControl_HSYNC_MASK |
118762306a36Sopenharmony_ci			  PM3VideoControl_VSYNC_MASK |
118862306a36Sopenharmony_ci			  PM3VideoControl_BLANK_ACTIVE_LOW);
118962306a36Sopenharmony_ci		break;
119062306a36Sopenharmony_ci	default:
119162306a36Sopenharmony_ci		DPRINTK("Unsupported blanking %d\n", blank_mode);
119262306a36Sopenharmony_ci		return 1;
119362306a36Sopenharmony_ci	}
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci	PM3_WAIT(par, 1);
119662306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3VideoControl, video);
119762306a36Sopenharmony_ci	return 0;
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/*
120162306a36Sopenharmony_ci	 *  Frame buffer operations
120262306a36Sopenharmony_ci	 */
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic const struct fb_ops pm3fb_ops = {
120562306a36Sopenharmony_ci	.owner		= THIS_MODULE,
120662306a36Sopenharmony_ci	.fb_check_var	= pm3fb_check_var,
120762306a36Sopenharmony_ci	.fb_set_par	= pm3fb_set_par,
120862306a36Sopenharmony_ci	.fb_setcolreg	= pm3fb_setcolreg,
120962306a36Sopenharmony_ci	.fb_pan_display	= pm3fb_pan_display,
121062306a36Sopenharmony_ci	.fb_fillrect	= pm3fb_fillrect,
121162306a36Sopenharmony_ci	.fb_copyarea	= pm3fb_copyarea,
121262306a36Sopenharmony_ci	.fb_imageblit	= pm3fb_imageblit,
121362306a36Sopenharmony_ci	.fb_blank	= pm3fb_blank,
121462306a36Sopenharmony_ci	.fb_sync	= pm3fb_sync,
121562306a36Sopenharmony_ci	.fb_cursor	= pm3fb_cursor,
121662306a36Sopenharmony_ci};
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	/*
122162306a36Sopenharmony_ci	 *  Initialization
122262306a36Sopenharmony_ci	 */
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci/* mmio register are already mapped when this function is called */
122562306a36Sopenharmony_ci/* the pm3fb_fix.smem_start is also set */
122662306a36Sopenharmony_cistatic unsigned long pm3fb_size_memory(struct pm3_par *par)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	unsigned long	memsize = 0;
122962306a36Sopenharmony_ci	unsigned long	tempBypass, i, temp1, temp2;
123062306a36Sopenharmony_ci	unsigned char	__iomem *screen_mem;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	pm3fb_fix.smem_len = 64 * 1024l * 1024; /* request full aperture size */
123362306a36Sopenharmony_ci	/* Linear frame buffer - request region and map it. */
123462306a36Sopenharmony_ci	if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
123562306a36Sopenharmony_ci				 "pm3fb smem")) {
123662306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
123762306a36Sopenharmony_ci		return 0;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci	screen_mem =
124062306a36Sopenharmony_ci		ioremap(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
124162306a36Sopenharmony_ci	if (!screen_mem) {
124262306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
124362306a36Sopenharmony_ci		release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
124462306a36Sopenharmony_ci		return 0;
124562306a36Sopenharmony_ci	}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	/* TODO: card-specific stuff, *before* accessing *any* FB memory */
124862306a36Sopenharmony_ci	/* For Appian Jeronimo 2000 board second head */
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	PM3_WAIT(par, 1);
125562306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* pm3 split up memory, replicates, and do a lot of
125862306a36Sopenharmony_ci	 * nasty stuff IMHO ;-)
125962306a36Sopenharmony_ci	 */
126062306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
126162306a36Sopenharmony_ci		fb_writel(i * 0x00345678,
126262306a36Sopenharmony_ci			  (screen_mem + (i * 1048576)));
126362306a36Sopenharmony_ci		mb();
126462306a36Sopenharmony_ci		temp1 = fb_readl((screen_mem + (i * 1048576)));
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci		/* Let's check for wrapover, write will fail at 16MB boundary */
126762306a36Sopenharmony_ci		if (temp1 == (i * 0x00345678))
126862306a36Sopenharmony_ci			memsize = i;
126962306a36Sopenharmony_ci		else
127062306a36Sopenharmony_ci			break;
127162306a36Sopenharmony_ci	}
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	DPRINTK("First detect pass already got %ld MB\n", memsize + 1);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (memsize + 1 == i) {
127662306a36Sopenharmony_ci		for (i = 0; i < 32; i++) {
127762306a36Sopenharmony_ci			/* Clear first 32MB ; 0 is 0, no need to byteswap */
127862306a36Sopenharmony_ci			writel(0x0000000, (screen_mem + (i * 1048576)));
127962306a36Sopenharmony_ci		}
128062306a36Sopenharmony_ci		wmb();
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci		for (i = 32; i < 64; i++) {
128362306a36Sopenharmony_ci			fb_writel(i * 0x00345678,
128462306a36Sopenharmony_ci				  (screen_mem + (i * 1048576)));
128562306a36Sopenharmony_ci			mb();
128662306a36Sopenharmony_ci			temp1 =
128762306a36Sopenharmony_ci			    fb_readl((screen_mem + (i * 1048576)));
128862306a36Sopenharmony_ci			temp2 =
128962306a36Sopenharmony_ci			    fb_readl((screen_mem + ((i - 32) * 1048576)));
129062306a36Sopenharmony_ci			/* different value, different RAM... */
129162306a36Sopenharmony_ci			if ((temp1 == (i * 0x00345678)) && (temp2 == 0))
129262306a36Sopenharmony_ci				memsize = i;
129362306a36Sopenharmony_ci			else
129462306a36Sopenharmony_ci				break;
129562306a36Sopenharmony_ci		}
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci	DPRINTK("Second detect pass got %ld MB\n", memsize + 1);
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	PM3_WAIT(par, 1);
130062306a36Sopenharmony_ci	PM3_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	iounmap(screen_mem);
130362306a36Sopenharmony_ci	release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
130462306a36Sopenharmony_ci	memsize = 1048576 * (memsize + 1);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	DPRINTK("Returning 0x%08lx bytes\n", memsize);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return memsize;
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct fb_info *info;
131462306a36Sopenharmony_ci	struct pm3_par *par;
131562306a36Sopenharmony_ci	struct device *device = &dev->dev; /* for pci drivers */
131662306a36Sopenharmony_ci	int err;
131762306a36Sopenharmony_ci	int retval = -ENXIO;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	err = aperture_remove_conflicting_pci_devices(dev, "pm3fb");
132062306a36Sopenharmony_ci	if (err)
132162306a36Sopenharmony_ci		return err;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	err = pci_enable_device(dev);
132462306a36Sopenharmony_ci	if (err) {
132562306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err);
132662306a36Sopenharmony_ci		return err;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci	/*
132962306a36Sopenharmony_ci	 * Dynamically allocate info and par
133062306a36Sopenharmony_ci	 */
133162306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct pm3_par), device);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (!info)
133462306a36Sopenharmony_ci		return -ENOMEM;
133562306a36Sopenharmony_ci	par = info->par;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	/*
133862306a36Sopenharmony_ci	 * Here we set the screen_base to the virtual memory address
133962306a36Sopenharmony_ci	 * for the framebuffer.
134062306a36Sopenharmony_ci	 */
134162306a36Sopenharmony_ci	pm3fb_fix.mmio_start = pci_resource_start(dev, 0);
134262306a36Sopenharmony_ci	pm3fb_fix.mmio_len = PM3_REGS_SIZE;
134362306a36Sopenharmony_ci#if defined(__BIG_ENDIAN)
134462306a36Sopenharmony_ci	pm3fb_fix.mmio_start += PM3_REGS_SIZE;
134562306a36Sopenharmony_ci	DPRINTK("Adjusting register base for big-endian.\n");
134662306a36Sopenharmony_ci#endif
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/* Registers - request region and map it. */
134962306a36Sopenharmony_ci	if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len,
135062306a36Sopenharmony_ci				 "pm3fb regbase")) {
135162306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n");
135262306a36Sopenharmony_ci		goto err_exit_neither;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci	par->v_regs =
135562306a36Sopenharmony_ci		ioremap(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
135662306a36Sopenharmony_ci	if (!par->v_regs) {
135762306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n",
135862306a36Sopenharmony_ci			pm3fb_fix.id);
135962306a36Sopenharmony_ci		release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
136062306a36Sopenharmony_ci		goto err_exit_neither;
136162306a36Sopenharmony_ci	}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	/* Linear frame buffer - request region and map it. */
136462306a36Sopenharmony_ci	pm3fb_fix.smem_start = pci_resource_start(dev, 1);
136562306a36Sopenharmony_ci	pm3fb_fix.smem_len = pm3fb_size_memory(par);
136662306a36Sopenharmony_ci	if (!pm3fb_fix.smem_len) {
136762306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't find memory on board.\n");
136862306a36Sopenharmony_ci		goto err_exit_mmio;
136962306a36Sopenharmony_ci	}
137062306a36Sopenharmony_ci	if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
137162306a36Sopenharmony_ci				 "pm3fb smem")) {
137262306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
137362306a36Sopenharmony_ci		goto err_exit_mmio;
137462306a36Sopenharmony_ci	}
137562306a36Sopenharmony_ci	info->screen_base = ioremap_wc(pm3fb_fix.smem_start,
137662306a36Sopenharmony_ci				       pm3fb_fix.smem_len);
137762306a36Sopenharmony_ci	if (!info->screen_base) {
137862306a36Sopenharmony_ci		printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
137962306a36Sopenharmony_ci		release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
138062306a36Sopenharmony_ci		goto err_exit_mmio;
138162306a36Sopenharmony_ci	}
138262306a36Sopenharmony_ci	info->screen_size = pm3fb_fix.smem_len;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (!nomtrr)
138562306a36Sopenharmony_ci		par->wc_cookie = arch_phys_wc_add(pm3fb_fix.smem_start,
138662306a36Sopenharmony_ci						  pm3fb_fix.smem_len);
138762306a36Sopenharmony_ci	info->fbops = &pm3fb_ops;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	par->video = PM3_READ_REG(par, PM3VideoControl);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	info->fix = pm3fb_fix;
139262306a36Sopenharmony_ci	info->pseudo_palette = par->palette;
139362306a36Sopenharmony_ci	info->flags = FBINFO_HWACCEL_XPAN |
139462306a36Sopenharmony_ci			FBINFO_HWACCEL_YPAN |
139562306a36Sopenharmony_ci			FBINFO_HWACCEL_COPYAREA |
139662306a36Sopenharmony_ci			FBINFO_HWACCEL_IMAGEBLIT |
139762306a36Sopenharmony_ci			FBINFO_HWACCEL_FILLRECT;
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci	if (noaccel) {
140062306a36Sopenharmony_ci		printk(KERN_DEBUG "disabling acceleration\n");
140162306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_DISABLED;
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	info->pixmap.addr = kmalloc(PM3_PIXMAP_SIZE, GFP_KERNEL);
140462306a36Sopenharmony_ci	if (!info->pixmap.addr) {
140562306a36Sopenharmony_ci		retval = -ENOMEM;
140662306a36Sopenharmony_ci		goto err_exit_pixmap;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci	info->pixmap.size = PM3_PIXMAP_SIZE;
140962306a36Sopenharmony_ci	info->pixmap.buf_align = 4;
141062306a36Sopenharmony_ci	info->pixmap.scan_align = 4;
141162306a36Sopenharmony_ci	info->pixmap.access_align = 32;
141262306a36Sopenharmony_ci	info->pixmap.flags = FB_PIXMAP_SYSTEM;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	/*
141562306a36Sopenharmony_ci	 * This should give a reasonable default video mode. The following is
141662306a36Sopenharmony_ci	 * done when we can set a video mode.
141762306a36Sopenharmony_ci	 */
141862306a36Sopenharmony_ci	if (!mode_option)
141962306a36Sopenharmony_ci		mode_option = "640x480@60";
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci	retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	if (!retval || retval == 4) {
142462306a36Sopenharmony_ci		retval = -EINVAL;
142562306a36Sopenharmony_ci		goto err_exit_both;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
142962306a36Sopenharmony_ci		retval = -ENOMEM;
143062306a36Sopenharmony_ci		goto err_exit_both;
143162306a36Sopenharmony_ci	}
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/*
143462306a36Sopenharmony_ci	 * For drivers that can...
143562306a36Sopenharmony_ci	 */
143662306a36Sopenharmony_ci	pm3fb_check_var(&info->var, info);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	if (register_framebuffer(info) < 0) {
143962306a36Sopenharmony_ci		retval = -EINVAL;
144062306a36Sopenharmony_ci		goto err_exit_all;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci	fb_info(info, "%s frame buffer device\n", info->fix.id);
144362306a36Sopenharmony_ci	pci_set_drvdata(dev, info);
144462306a36Sopenharmony_ci	return 0;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci err_exit_all:
144762306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
144862306a36Sopenharmony_ci err_exit_both:
144962306a36Sopenharmony_ci	kfree(info->pixmap.addr);
145062306a36Sopenharmony_ci err_exit_pixmap:
145162306a36Sopenharmony_ci	iounmap(info->screen_base);
145262306a36Sopenharmony_ci	release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
145362306a36Sopenharmony_ci err_exit_mmio:
145462306a36Sopenharmony_ci	iounmap(par->v_regs);
145562306a36Sopenharmony_ci	release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
145662306a36Sopenharmony_ci err_exit_neither:
145762306a36Sopenharmony_ci	framebuffer_release(info);
145862306a36Sopenharmony_ci	return retval;
145962306a36Sopenharmony_ci}
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	/*
146262306a36Sopenharmony_ci	 *  Cleanup
146362306a36Sopenharmony_ci	 */
146462306a36Sopenharmony_cistatic void pm3fb_remove(struct pci_dev *dev)
146562306a36Sopenharmony_ci{
146662306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(dev);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (info) {
146962306a36Sopenharmony_ci		struct fb_fix_screeninfo *fix = &info->fix;
147062306a36Sopenharmony_ci		struct pm3_par *par = info->par;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci		unregister_framebuffer(info);
147362306a36Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci		arch_phys_wc_del(par->wc_cookie);
147662306a36Sopenharmony_ci		iounmap(info->screen_base);
147762306a36Sopenharmony_ci		release_mem_region(fix->smem_start, fix->smem_len);
147862306a36Sopenharmony_ci		iounmap(par->v_regs);
147962306a36Sopenharmony_ci		release_mem_region(fix->mmio_start, fix->mmio_len);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci		kfree(info->pixmap.addr);
148262306a36Sopenharmony_ci		framebuffer_release(info);
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_cistatic const struct pci_device_id pm3fb_id_table[] = {
148762306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3DLABS, 0x0a,
148862306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
148962306a36Sopenharmony_ci	{ 0, }
149062306a36Sopenharmony_ci};
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci/* For PCI drivers */
149362306a36Sopenharmony_cistatic struct pci_driver pm3fb_driver = {
149462306a36Sopenharmony_ci	.name =		"pm3fb",
149562306a36Sopenharmony_ci	.id_table =	pm3fb_id_table,
149662306a36Sopenharmony_ci	.probe =	pm3fb_probe,
149762306a36Sopenharmony_ci	.remove =	pm3fb_remove,
149862306a36Sopenharmony_ci};
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pm3fb_id_table);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci#ifndef MODULE
150362306a36Sopenharmony_ci	/*
150462306a36Sopenharmony_ci	 *  Setup
150562306a36Sopenharmony_ci	 */
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci/*
150862306a36Sopenharmony_ci * Only necessary if your driver takes special options,
150962306a36Sopenharmony_ci * otherwise we fall back on the generic fb_setup().
151062306a36Sopenharmony_ci */
151162306a36Sopenharmony_cistatic int __init pm3fb_setup(char *options)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	char *this_opt;
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	/* Parse user specified options (`video=pm3fb:') */
151662306a36Sopenharmony_ci	if (!options || !*options)
151762306a36Sopenharmony_ci		return 0;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
152062306a36Sopenharmony_ci		if (!*this_opt)
152162306a36Sopenharmony_ci			continue;
152262306a36Sopenharmony_ci		else if (!strncmp(this_opt, "noaccel", 7))
152362306a36Sopenharmony_ci			noaccel = 1;
152462306a36Sopenharmony_ci		else if (!strncmp(this_opt, "hwcursor=", 9))
152562306a36Sopenharmony_ci			hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
152662306a36Sopenharmony_ci		else if (!strncmp(this_opt, "nomtrr", 6))
152762306a36Sopenharmony_ci			nomtrr = 1;
152862306a36Sopenharmony_ci		else
152962306a36Sopenharmony_ci			mode_option = this_opt;
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci	return 0;
153262306a36Sopenharmony_ci}
153362306a36Sopenharmony_ci#endif /* MODULE */
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_cistatic int __init pm3fb_init(void)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci	/*
153862306a36Sopenharmony_ci	 *  For kernel boot options (in 'video=pm3fb:<options>' format)
153962306a36Sopenharmony_ci	 */
154062306a36Sopenharmony_ci#ifndef MODULE
154162306a36Sopenharmony_ci	char *option = NULL;
154262306a36Sopenharmony_ci#endif
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	if (fb_modesetting_disabled("pm3fb"))
154562306a36Sopenharmony_ci		return -ENODEV;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci#ifndef MODULE
154862306a36Sopenharmony_ci	if (fb_get_options("pm3fb", &option))
154962306a36Sopenharmony_ci		return -ENODEV;
155062306a36Sopenharmony_ci	pm3fb_setup(option);
155162306a36Sopenharmony_ci#endif
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	return pci_register_driver(&pm3fb_driver);
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci#ifdef MODULE
155762306a36Sopenharmony_cistatic void __exit pm3fb_exit(void)
155862306a36Sopenharmony_ci{
155962306a36Sopenharmony_ci	pci_unregister_driver(&pm3fb_driver);
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_cimodule_exit(pm3fb_exit);
156362306a36Sopenharmony_ci#endif
156462306a36Sopenharmony_cimodule_init(pm3fb_init);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_cimodule_param(mode_option, charp, 0);
156762306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
156862306a36Sopenharmony_cimodule_param(noaccel, bool, 0);
156962306a36Sopenharmony_ciMODULE_PARM_DESC(noaccel, "Disable acceleration");
157062306a36Sopenharmony_cimodule_param(hwcursor, int, 0644);
157162306a36Sopenharmony_ciMODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
157262306a36Sopenharmony_ci			"(1=enable, 0=disable, default=1)");
157362306a36Sopenharmony_cimodule_param(nomtrr, bool, 0);
157462306a36Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ciMODULE_DESCRIPTION("Permedia3 framebuffer device driver");
157762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1578