pm3fb.c 42.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  linux/drivers/video/pm3fb.c -- 3DLabs Permedia3 frame buffer device
3 4 5 6 7 8
 *
 *  Copyright (C) 2001 Romain Dolbeau <romain@dolbeau.org>.
 *
 *  Ported to 2.6 kernel on 1 May 2007 by Krzysztof Helt <krzysztof.h1@wp.pl>
 *	based on pm2fb.c
 *
L
Linus Torvalds 已提交
9
 *  Based on code written by:
10 11 12
 *	   Sven Luther, <luther@dpt-info.u-strasbg.fr>
 *	   Alan Hourihane, <alanh@fairlite.demon.co.uk>
 *	   Russell King, <rmk@arm.linux.org.uk>
L
Linus Torvalds 已提交
13 14 15
 *  Based on linux/drivers/video/skeletonfb.c:
 *	Copyright (C) 1997 Geert Uytterhoeven
 *  Based on linux/driver/video/pm2fb.c:
16 17
 *	Copyright (C) 1998-1999 Ilario Nardinocchi (nardinoc@CS.UniBO.IT)
 *	Copyright (C) 1999 Jakub Jelinek (jakub@redhat.com)
L
Linus Torvalds 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
 *
 *  This file is subject to the terms and conditions of the GNU General Public
 *  License. See the file COPYING in the main directory of this archive for
 *  more details.
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
35 36 37
#ifdef CONFIG_MTRR
#include <asm/mtrr.h>
#endif
L
Linus Torvalds 已提交
38

39
#include <video/pm3fb.h>
L
Linus Torvalds 已提交
40

41 42
#if !defined(CONFIG_PCI)
#error "Only generic PCI cards supported."
L
Linus Torvalds 已提交
43 44
#endif

45 46
#undef PM3FB_MASTER_DEBUG
#ifdef PM3FB_MASTER_DEBUG
K
Krzysztof Helt 已提交
47
#define DPRINTK(a, b...)	\
48
	printk(KERN_DEBUG "pm3fb: %s: " a, __func__ , ## b)
49
#else
K
Krzysztof Helt 已提交
50
#define DPRINTK(a, b...)
L
Linus Torvalds 已提交
51 52
#endif

K
Krzysztof Helt 已提交
53 54
#define PM3_PIXMAP_SIZE	(2048 * 4)

55 56 57
/*
 * Driver data
 */
K
Krzysztof Helt 已提交
58
static int hwcursor = 1;
59 60
static char *mode_option;
static bool noaccel;
61 62 63

/* mtrr option */
#ifdef CONFIG_MTRR
64
static bool nomtrr;
65
#endif
L
Linus Torvalds 已提交
66

67 68 69 70 71 72 73 74 75 76
/*
 * This structure defines the hardware state of the graphics card. Normally
 * you place this in a header file in linux/include/video. This file usually
 * also includes register information. That allows other driver subsystems
 * and userland applications the ability to use the same header file to
 * avoid duplicate work and easy porting of software.
 */
struct pm3_par {
	unsigned char	__iomem *v_regs;/* virtual address of p_regs */
	u32		video;		/* video flags before blanking */
K
Krzysztof Helt 已提交
77
	u32		base;		/* screen base in 128 bits unit */
K
Krzysztof Helt 已提交
78
	u32		palette[16];
79
	int		mtrr_handle;
L
Linus Torvalds 已提交
80 81
};

82 83 84 85 86
/*
 * Here we define the default structs fb_fix_screeninfo and fb_var_screeninfo
 * if we don't use modedb. If we do use modedb see pm3fb_init how to use it
 * to get a fb_var_screeninfo. Otherwise define a default var as well.
 */
87
static struct fb_fix_screeninfo pm3fb_fix = {
88 89 90 91 92 93
	.id =		"Permedia3",
	.type =		FB_TYPE_PACKED_PIXELS,
	.visual =	FB_VISUAL_PSEUDOCOLOR,
	.xpanstep =	1,
	.ypanstep =	1,
	.ywrapstep =	0,
K
Krzysztof Helt 已提交
94
	.accel =	FB_ACCEL_3DLABS_PERMEDIA3,
L
Linus Torvalds 已提交
95 96
};

97 98 99
/*
 * Utility functions
 */
L
Linus Torvalds 已提交
100

101 102 103 104
static inline u32 PM3_READ_REG(struct pm3_par *par, s32 off)
{
	return fb_readl(par->v_regs + off);
}
L
Linus Torvalds 已提交
105

106 107 108 109
static inline void PM3_WRITE_REG(struct pm3_par *par, s32 off, u32 v)
{
	fb_writel(v, par->v_regs + off);
}
L
Linus Torvalds 已提交
110

111 112
static inline void PM3_WAIT(struct pm3_par *par, u32 n)
{
113 114
	while (PM3_READ_REG(par, PM3InFIFOSpace) < n)
		cpu_relax();
L
Linus Torvalds 已提交
115 116
}

117
static inline void PM3_WRITE_DAC_REG(struct pm3_par *par, unsigned r, u8 v)
L
Linus Torvalds 已提交
118
{
K
Krzysztof Helt 已提交
119 120 121
	PM3_WAIT(par, 3);
	PM3_WRITE_REG(par, PM3RD_IndexHigh, (r >> 8) & 0xff);
	PM3_WRITE_REG(par, PM3RD_IndexLow, r & 0xff);
122 123
	wmb();
	PM3_WRITE_REG(par, PM3RD_IndexedData, v);
K
Krzysztof Helt 已提交
124
	wmb();
L
Linus Torvalds 已提交
125 126
}

127 128
static inline void pm3fb_set_color(struct pm3_par *par, unsigned char regno,
			unsigned char r, unsigned char g, unsigned char b)
L
Linus Torvalds 已提交
129
{
K
Krzysztof Helt 已提交
130 131 132 133 134 135 136 137 138
	PM3_WAIT(par, 4);
	PM3_WRITE_REG(par, PM3RD_PaletteWriteAddress, regno);
	wmb();
	PM3_WRITE_REG(par, PM3RD_PaletteData, r);
	wmb();
	PM3_WRITE_REG(par, PM3RD_PaletteData, g);
	wmb();
	PM3_WRITE_REG(par, PM3RD_PaletteData, b);
	wmb();
139 140 141 142 143 144 145
}

static void pm3fb_clear_colormap(struct pm3_par *par,
			unsigned char r, unsigned char g, unsigned char b)
{
	int i;

K
Krzysztof Helt 已提交
146
	for (i = 0; i < 256 ; i++)
147 148
		pm3fb_set_color(par, i, r, g, b);

L
Linus Torvalds 已提交
149 150
}

K
Krzysztof Helt 已提交
151
/* Calculating various clock parameters */
152 153 154 155
static void pm3fb_calculate_clock(unsigned long reqclock,
				unsigned char *prescale,
				unsigned char *feedback,
				unsigned char *postscale)
L
Linus Torvalds 已提交
156 157 158 159
{
	int f, pre, post;
	unsigned long freq;
	long freqerr = 1000;
160
	long currerr;
L
Linus Torvalds 已提交
161 162 163 164

	for (f = 1; f < 256; f++) {
		for (pre = 1; pre < 256; pre++) {
			for (post = 0; post < 5; post++) {
165 166 167 168 169 170
				freq = ((2*PM3_REF_CLOCK * f) >> post) / pre;
				currerr = (reqclock > freq)
					? reqclock - freq
					: freq - reqclock;
				if (currerr < freqerr) {
					freqerr = currerr;
L
Linus Torvalds 已提交
171 172 173 174 175 176 177 178 179
					*feedback = f;
					*prescale = pre;
					*postscale = post;
				}
			}
		}
	}
}

K
Krzysztof Helt 已提交
180
static inline int pm3fb_depth(const struct fb_var_screeninfo *var)
L
Linus Torvalds 已提交
181
{
K
Krzysztof Helt 已提交
182
	if (var->bits_per_pixel == 16)
K
Krzysztof Helt 已提交
183 184 185 186 187 188 189 190 191
		return var->red.length + var->green.length
			+ var->blue.length;

	return var->bits_per_pixel;
}

static inline int pm3fb_shift_bpp(unsigned bpp, int v)
{
	switch (bpp) {
L
Linus Torvalds 已提交
192 193 194 195 196 197 198
	case 8:
		return (v >> 4);
	case 16:
		return (v >> 3);
	case 32:
		return (v >> 2);
	}
K
Krzysztof Helt 已提交
199
	DPRINTK("Unsupported depth %u\n", bpp);
200
	return 0;
L
Linus Torvalds 已提交
201 202
}

K
Krzysztof Helt 已提交
203 204 205 206 207 208 209 210 211 212
/* acceleration */
static int pm3fb_sync(struct fb_info *info)
{
	struct pm3_par *par = info->par;

	PM3_WAIT(par, 2);
	PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
	PM3_WRITE_REG(par, PM3Sync, 0);
	mb();
	do {
213 214
		while ((PM3_READ_REG(par, PM3OutFIFOWords)) == 0)
			cpu_relax();
K
Krzysztof Helt 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
	} while ((PM3_READ_REG(par, PM3OutputFifo)) != PM3Sync_Tag);

	return 0;
}

static void pm3fb_init_engine(struct fb_info *info)
{
	struct pm3_par *par = info->par;
	const u32 width = (info->var.xres_virtual + 7) & ~7;

	PM3_WAIT(par, 50);
	PM3_WRITE_REG(par, PM3FilterMode, PM3FilterModeSync);
	PM3_WRITE_REG(par, PM3StatisticMode, 0x0);
	PM3_WRITE_REG(par, PM3DeltaMode, 0x0);
	PM3_WRITE_REG(par, PM3RasterizerMode, 0x0);
	PM3_WRITE_REG(par, PM3ScissorMode, 0x0);
	PM3_WRITE_REG(par, PM3LineStippleMode, 0x0);
	PM3_WRITE_REG(par, PM3AreaStippleMode, 0x0);
	PM3_WRITE_REG(par, PM3GIDMode, 0x0);
	PM3_WRITE_REG(par, PM3DepthMode, 0x0);
	PM3_WRITE_REG(par, PM3StencilMode, 0x0);
	PM3_WRITE_REG(par, PM3StencilData, 0x0);
	PM3_WRITE_REG(par, PM3ColorDDAMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureCoordMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureIndexMode0, 0x0);
	PM3_WRITE_REG(par, PM3TextureIndexMode1, 0x0);
	PM3_WRITE_REG(par, PM3TextureReadMode, 0x0);
	PM3_WRITE_REG(par, PM3LUTMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureFilterMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureCompositeMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureApplicationMode, 0x0);
	PM3_WRITE_REG(par, PM3TextureCompositeColorMode1, 0x0);
	PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode1, 0x0);
	PM3_WRITE_REG(par, PM3TextureCompositeColorMode0, 0x0);
	PM3_WRITE_REG(par, PM3TextureCompositeAlphaMode0, 0x0);
	PM3_WRITE_REG(par, PM3FogMode, 0x0);
	PM3_WRITE_REG(par, PM3ChromaTestMode, 0x0);
	PM3_WRITE_REG(par, PM3AlphaTestMode, 0x0);
	PM3_WRITE_REG(par, PM3AntialiasMode, 0x0);
	PM3_WRITE_REG(par, PM3YUVMode, 0x0);
	PM3_WRITE_REG(par, PM3AlphaBlendColorMode, 0x0);
	PM3_WRITE_REG(par, PM3AlphaBlendAlphaMode, 0x0);
	PM3_WRITE_REG(par, PM3DitherMode, 0x0);
	PM3_WRITE_REG(par, PM3LogicalOpMode, 0x0);
	PM3_WRITE_REG(par, PM3RouterMode, 0x0);
	PM3_WRITE_REG(par, PM3Window, 0x0);

	PM3_WRITE_REG(par, PM3Config2D, 0x0);

	PM3_WRITE_REG(par, PM3SpanColorMask, 0xffffffff);

	PM3_WRITE_REG(par, PM3XBias, 0x0);
	PM3_WRITE_REG(par, PM3YBias, 0x0);
	PM3_WRITE_REG(par, PM3DeltaControl, 0x0);

	PM3_WRITE_REG(par, PM3BitMaskPattern, 0xffffffff);

	PM3_WRITE_REG(par, PM3FBDestReadEnables,
			   PM3FBDestReadEnables_E(0xff) |
			   PM3FBDestReadEnables_R(0xff) |
			   PM3FBDestReadEnables_ReferenceAlpha(0xff));
	PM3_WRITE_REG(par, PM3FBDestReadBufferAddr0, 0x0);
	PM3_WRITE_REG(par, PM3FBDestReadBufferOffset0, 0x0);
	PM3_WRITE_REG(par, PM3FBDestReadBufferWidth0,
			   PM3FBDestReadBufferWidth_Width(width));

	PM3_WRITE_REG(par, PM3FBDestReadMode,
			   PM3FBDestReadMode_ReadEnable |
			   PM3FBDestReadMode_Enable0);
	PM3_WRITE_REG(par, PM3FBSourceReadBufferAddr, 0x0);
	PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset, 0x0);
	PM3_WRITE_REG(par, PM3FBSourceReadBufferWidth,
			   PM3FBSourceReadBufferWidth_Width(width));
	PM3_WRITE_REG(par, PM3FBSourceReadMode,
			   PM3FBSourceReadMode_Blocking |
			   PM3FBSourceReadMode_ReadEnable);

	PM3_WAIT(par, 2);
	{
294 295
		/* invert bits in bitmask */
		unsigned long rm = 1 | (3 << 7);
K
Krzysztof Helt 已提交
296 297 298 299
		switch (info->var.bits_per_pixel) {
		case 8:
			PM3_WRITE_REG(par, PM3PixelSize,
					   PM3PixelSize_GLOBAL_8BIT);
300 301 302
#ifdef __BIG_ENDIAN
			rm |= 3 << 15;
#endif
K
Krzysztof Helt 已提交
303 304 305 306
			break;
		case 16:
			PM3_WRITE_REG(par, PM3PixelSize,
					   PM3PixelSize_GLOBAL_16BIT);
307 308 309
#ifdef __BIG_ENDIAN
			rm |= 2 << 15;
#endif
K
Krzysztof Helt 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
			break;
		case 32:
			PM3_WRITE_REG(par, PM3PixelSize,
					   PM3PixelSize_GLOBAL_32BIT);
			break;
		default:
			DPRINTK(1, "Unsupported depth %d\n",
				info->var.bits_per_pixel);
			break;
		}
		PM3_WRITE_REG(par, PM3RasterizerMode, rm);
	}

	PM3_WAIT(par, 20);
	PM3_WRITE_REG(par, PM3FBSoftwareWriteMask, 0xffffffff);
	PM3_WRITE_REG(par, PM3FBHardwareWriteMask, 0xffffffff);
	PM3_WRITE_REG(par, PM3FBWriteMode,
			   PM3FBWriteMode_WriteEnable |
			   PM3FBWriteMode_OpaqueSpan |
			   PM3FBWriteMode_Enable0);
	PM3_WRITE_REG(par, PM3FBWriteBufferAddr0, 0x0);
	PM3_WRITE_REG(par, PM3FBWriteBufferOffset0, 0x0);
	PM3_WRITE_REG(par, PM3FBWriteBufferWidth0,
			   PM3FBWriteBufferWidth_Width(width));

	PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 0x0);
	{
		/* size in lines of FB */
		unsigned long sofb = info->screen_size /
			info->fix.line_length;
		if (sofb > 4095)
			PM3_WRITE_REG(par, PM3SizeOfFramebuffer, 4095);
		else
			PM3_WRITE_REG(par, PM3SizeOfFramebuffer, sofb);

		switch (info->var.bits_per_pixel) {
		case 8:
			PM3_WRITE_REG(par, PM3DitherMode,
					   (1 << 10) | (2 << 3));
			break;
		case 16:
			PM3_WRITE_REG(par, PM3DitherMode,
					   (1 << 10) | (1 << 3));
			break;
		case 32:
			PM3_WRITE_REG(par, PM3DitherMode,
					   (1 << 10) | (0 << 3));
			break;
		default:
			DPRINTK(1, "Unsupported depth %d\n",
				info->current_par->depth);
			break;
		}
	}

	PM3_WRITE_REG(par, PM3dXDom, 0x0);
	PM3_WRITE_REG(par, PM3dXSub, 0x0);
K
Krzysztof Helt 已提交
367
	PM3_WRITE_REG(par, PM3dY, 1 << 16);
K
Krzysztof Helt 已提交
368 369 370 371 372 373 374 375 376 377 378 379 380 381
	PM3_WRITE_REG(par, PM3StartXDom, 0x0);
	PM3_WRITE_REG(par, PM3StartXSub, 0x0);
	PM3_WRITE_REG(par, PM3StartY, 0x0);
	PM3_WRITE_REG(par, PM3Count, 0x0);

/* Disable LocalBuffer. better safe than sorry */
	PM3_WRITE_REG(par, PM3LBDestReadMode, 0x0);
	PM3_WRITE_REG(par, PM3LBDestReadEnables, 0x0);
	PM3_WRITE_REG(par, PM3LBSourceReadMode, 0x0);
	PM3_WRITE_REG(par, PM3LBWriteMode, 0x0);

	pm3fb_sync(info);
}

K
Krzysztof Helt 已提交
382
static void pm3fb_fillrect(struct fb_info *info,
K
Krzysztof Helt 已提交
383 384 385 386 387
				const struct fb_fillrect *region)
{
	struct pm3_par *par = info->par;
	struct fb_fillrect modded;
	int vxres, vyres;
K
Krzysztof Helt 已提交
388
	int rop;
K
Krzysztof Helt 已提交
389
	u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
K
Krzysztof Helt 已提交
390
		((u32 *)info->pseudo_palette)[region->color] : region->color;
K
Krzysztof Helt 已提交
391 392 393

	if (info->state != FBINFO_STATE_RUNNING)
		return;
K
Krzysztof Helt 已提交
394
	if (info->flags & FBINFO_HWACCEL_DISABLED) {
K
Krzysztof Helt 已提交
395 396 397
		cfb_fillrect(info, region);
		return;
	}
K
Krzysztof Helt 已提交
398 399 400 401 402
	if (region->rop == ROP_COPY )
		rop = PM3Config2D_ForegroundROP(0x3); /* GXcopy */
	else
		rop = PM3Config2D_ForegroundROP(0x6) | /* GXxor */
			PM3Config2D_FBDestReadEnable;
K
Krzysztof Helt 已提交
403 404 405 406 407 408

	vxres = info->var.xres_virtual;
	vyres = info->var.yres_virtual;

	memcpy(&modded, region, sizeof(struct fb_fillrect));

K
Krzysztof Helt 已提交
409 410
	if (!modded.width || !modded.height ||
	    modded.dx >= vxres || modded.dy >= vyres)
K
Krzysztof Helt 已提交
411 412
		return;

K
Krzysztof Helt 已提交
413
	if (modded.dx + modded.width  > vxres)
K
Krzysztof Helt 已提交
414
		modded.width  = vxres - modded.dx;
K
Krzysztof Helt 已提交
415
	if (modded.dy + modded.height > vyres)
K
Krzysztof Helt 已提交
416 417
		modded.height = vyres - modded.dy;

K
Krzysztof Helt 已提交
418
	if (info->var.bits_per_pixel == 8)
K
Krzysztof Helt 已提交
419
		color |= color << 8;
K
Krzysztof Helt 已提交
420
	if (info->var.bits_per_pixel <= 16)
K
Krzysztof Helt 已提交
421 422 423
		color |= color << 16;

	PM3_WAIT(par, 4);
424
	/* ROP Ox3 is GXcopy */
K
Krzysztof Helt 已提交
425
	PM3_WRITE_REG(par, PM3Config2D,
426 427
			PM3Config2D_UseConstantSource |
			PM3Config2D_ForegroundROPEnable |
K
Krzysztof Helt 已提交
428
			rop |
429
			PM3Config2D_FBWriteEnable);
K
Krzysztof Helt 已提交
430 431 432 433

	PM3_WRITE_REG(par, PM3ForegroundColor, color);

	PM3_WRITE_REG(par, PM3RectanglePosition,
K
Krzysztof Helt 已提交
434 435
			PM3RectanglePosition_XOffset(modded.dx) |
			PM3RectanglePosition_YOffset(modded.dy));
K
Krzysztof Helt 已提交
436 437 438 439 440 441

	PM3_WRITE_REG(par, PM3Render2D,
		      PM3Render2D_XPositive |
		      PM3Render2D_YPositive |
		      PM3Render2D_Operation_Normal |
		      PM3Render2D_SpanOperation |
K
Krzysztof Helt 已提交
442 443
		      PM3Render2D_Width(modded.width) |
		      PM3Render2D_Height(modded.height));
K
Krzysztof Helt 已提交
444
}
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465

static void pm3fb_copyarea(struct fb_info *info,
				const struct fb_copyarea *area)
{
	struct pm3_par *par = info->par;
	struct fb_copyarea modded;
	u32 vxres, vyres;
	int x_align, o_x, o_y;

	if (info->state != FBINFO_STATE_RUNNING)
		return;
	if (info->flags & FBINFO_HWACCEL_DISABLED) {
		cfb_copyarea(info, area);
		return;
	}

	memcpy(&modded, area, sizeof(struct fb_copyarea));

	vxres = info->var.xres_virtual;
	vyres = info->var.yres_virtual;

K
Krzysztof Helt 已提交
466 467 468
	if (!modded.width || !modded.height ||
	    modded.sx >= vxres || modded.sy >= vyres ||
	    modded.dx >= vxres || modded.dy >= vyres)
469 470
		return;

K
Krzysztof Helt 已提交
471
	if (modded.sx + modded.width > vxres)
472
		modded.width = vxres - modded.sx;
K
Krzysztof Helt 已提交
473
	if (modded.dx + modded.width > vxres)
474
		modded.width = vxres - modded.dx;
K
Krzysztof Helt 已提交
475
	if (modded.sy + modded.height > vyres)
476
		modded.height = vyres - modded.sy;
K
Krzysztof Helt 已提交
477
	if (modded.dy + modded.height > vyres)
478 479 480 481 482 483 484 485 486 487 488 489 490
		modded.height = vyres - modded.dy;

	o_x = modded.sx - modded.dx;	/*(sx > dx ) ? (sx - dx) : (dx - sx); */
	o_y = modded.sy - modded.dy;	/*(sy > dy ) ? (sy - dy) : (dy - sy); */

	x_align = (modded.sx & 0x1f);

	PM3_WAIT(par, 6);

	PM3_WRITE_REG(par, PM3Config2D,
			PM3Config2D_UserScissorEnable |
			PM3Config2D_ForegroundROPEnable |
			PM3Config2D_Blocking |
K
Krzysztof Helt 已提交
491
			PM3Config2D_ForegroundROP(0x3) | /* Ox3 is GXcopy */
492 493 494 495 496 497 498 499 500 501 502 503 504
			PM3Config2D_FBWriteEnable);

	PM3_WRITE_REG(par, PM3ScissorMinXY,
			((modded.dy & 0x0fff) << 16) | (modded.dx & 0x0fff));
	PM3_WRITE_REG(par, PM3ScissorMaxXY,
			(((modded.dy + modded.height) & 0x0fff) << 16) |
			((modded.dx + modded.width) & 0x0fff));

	PM3_WRITE_REG(par, PM3FBSourceReadBufferOffset,
			PM3FBSourceReadBufferOffset_XOffset(o_x) |
			PM3FBSourceReadBufferOffset_YOffset(o_y));

	PM3_WRITE_REG(par, PM3RectanglePosition,
K
Krzysztof Helt 已提交
505 506
			PM3RectanglePosition_XOffset(modded.dx - x_align) |
			PM3RectanglePosition_YOffset(modded.dy));
507 508 509 510 511 512 513

	PM3_WRITE_REG(par, PM3Render2D,
			((modded.sx > modded.dx) ? PM3Render2D_XPositive : 0) |
			((modded.sy > modded.dy) ? PM3Render2D_YPositive : 0) |
			PM3Render2D_Operation_Normal |
			PM3Render2D_SpanOperation |
			PM3Render2D_FBSourceReadEnable |
K
Krzysztof Helt 已提交
514 515
			PM3Render2D_Width(modded.width + x_align) |
			PM3Render2D_Height(modded.height));
516 517 518 519 520 521 522
}

static void pm3fb_imageblit(struct fb_info *info, const struct fb_image *image)
{
	struct pm3_par *par = info->par;
	u32 height = image->height;
	u32 fgx, bgx;
K
Krzysztof Helt 已提交
523
	const u32 *src = (const u32 *)image->data;
524

K
Krzysztof Helt 已提交
525 526 527 528 529 530
	if (info->state != FBINFO_STATE_RUNNING)
		return;
	if (info->flags & FBINFO_HWACCEL_DISABLED) {
		cfb_imageblit(info, image);
		return;
	}
531
	switch (info->fix.visual) {
K
Krzysztof Helt 已提交
532 533 534 535 536 537 538 539 540
	case FB_VISUAL_PSEUDOCOLOR:
		fgx = image->fg_color;
		bgx = image->bg_color;
		break;
	case FB_VISUAL_TRUECOLOR:
	default:
		fgx = par->palette[image->fg_color];
		bgx = par->palette[image->bg_color];
		break;
541
	}
H
Hannes Eder 已提交
542 543 544 545
	if (image->depth != 1) {
		cfb_imageblit(info, image);
		return;
	}
K
Krzysztof Helt 已提交
546

547 548 549 550 551 552 553 554 555
	if (info->var.bits_per_pixel == 8) {
		fgx |= fgx << 8;
		bgx |= bgx << 8;
	}
	if (info->var.bits_per_pixel <= 16) {
		fgx |= fgx << 16;
		bgx |= bgx << 16;
	}

K
Krzysztof Helt 已提交
556
	PM3_WAIT(par, 7);
557 558 559 560 561 562

	PM3_WRITE_REG(par, PM3ForegroundColor, fgx);
	PM3_WRITE_REG(par, PM3BackgroundColor, bgx);

	/* ROP Ox3 is GXcopy */
	PM3_WRITE_REG(par, PM3Config2D,
K
Krzysztof Helt 已提交
563
			PM3Config2D_UserScissorEnable |
564 565
			PM3Config2D_UseConstantSource |
			PM3Config2D_ForegroundROPEnable |
K
Krzysztof Helt 已提交
566
			PM3Config2D_ForegroundROP(0x3) |
567 568
			PM3Config2D_OpaqueSpan |
			PM3Config2D_FBWriteEnable);
K
Krzysztof Helt 已提交
569 570 571 572 573
	PM3_WRITE_REG(par, PM3ScissorMinXY,
			((image->dy & 0x0fff) << 16) | (image->dx & 0x0fff));
	PM3_WRITE_REG(par, PM3ScissorMaxXY,
			(((image->dy + image->height) & 0x0fff) << 16) |
			((image->dx + image->width) & 0x0fff));
574
	PM3_WRITE_REG(par, PM3RectanglePosition,
K
Krzysztof Helt 已提交
575 576
			PM3RectanglePosition_XOffset(image->dx) |
			PM3RectanglePosition_YOffset(image->dy));
577 578 579 580 581
	PM3_WRITE_REG(par, PM3Render2D,
			PM3Render2D_XPositive |
			PM3Render2D_YPositive |
			PM3Render2D_Operation_SyncOnBitMask |
			PM3Render2D_SpanOperation |
K
Krzysztof Helt 已提交
582 583
			PM3Render2D_Width(image->width) |
			PM3Render2D_Height(image->height));
584 585 586


	while (height--) {
K
Krzysztof Helt 已提交
587 588
		int width = ((image->width + 7) >> 3)
				+ info->pixmap.scan_align - 1;
K
Krzysztof Helt 已提交
589
		width >>= 2;
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

		while (width >= PM3_FIFO_SIZE) {
			int i = PM3_FIFO_SIZE - 1;

			PM3_WAIT(par, PM3_FIFO_SIZE);
			while (i--) {
				PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
				src++;
			}
			width -= PM3_FIFO_SIZE - 1;
		}

		PM3_WAIT(par, width + 1);
		while (width--) {
			PM3_WRITE_REG(par, PM3BitMaskPattern, *src);
			src++;
		}
	}
}
K
Krzysztof Helt 已提交
609 610
/* end of acceleration functions */

K
Krzysztof Helt 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
/*
 *	Hardware Cursor support.
 */
static const u8 cursor_bits_lookup[16] = {
	0x00, 0x40, 0x10, 0x50, 0x04, 0x44, 0x14, 0x54,
	0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
};

static int pm3fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
	struct pm3_par *par = info->par;
	u8 mode;

	if (!hwcursor)
		return -EINVAL;	/* just to force soft_cursor() call */

	/* Too large of a cursor or wrong bpp :-( */
	if (cursor->image.width > 64 ||
	    cursor->image.height > 64 ||
	    cursor->image.depth > 1)
		return -EINVAL;

	mode = PM3RD_CursorMode_TYPE_X;
	if (cursor->enable)
		 mode |= PM3RD_CursorMode_CURSOR_ENABLE;

	PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, mode);

	/*
	 * If the cursor is not be changed this means either we want the
	 * current cursor state (if enable is set) or we want to query what
	 * we can do with the cursor (if enable is not set)
	 */
	if (!cursor->set)
		return 0;

	if (cursor->set & FB_CUR_SETPOS) {
		int x = cursor->image.dx - info->var.xoffset;
		int y = cursor->image.dy - info->var.yoffset;

		PM3_WRITE_DAC_REG(par, PM3RD_CursorXLow, x & 0xff);
		PM3_WRITE_DAC_REG(par, PM3RD_CursorXHigh, (x >> 8) & 0xf);
		PM3_WRITE_DAC_REG(par, PM3RD_CursorYLow, y & 0xff);
		PM3_WRITE_DAC_REG(par, PM3RD_CursorYHigh, (y >> 8) & 0xf);
	}

	if (cursor->set & FB_CUR_SETHOT) {
		PM3_WRITE_DAC_REG(par, PM3RD_CursorHotSpotX,
				  cursor->hot.x & 0x3f);
		PM3_WRITE_DAC_REG(par, PM3RD_CursorHotSpotY,
				  cursor->hot.y & 0x3f);
	}

	if (cursor->set & FB_CUR_SETCMAP) {
		u32 fg_idx = cursor->image.fg_color;
		u32 bg_idx = cursor->image.bg_color;
		struct fb_cmap cmap = info->cmap;

		/* the X11 driver says one should use these color registers */
		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(39),
				  cmap.red[fg_idx] >> 8 );
		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(40),
				  cmap.green[fg_idx] >> 8 );
		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(41),
				  cmap.blue[fg_idx] >> 8 );

		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(42),
				  cmap.red[bg_idx] >> 8 );
		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(43),
				  cmap.green[bg_idx] >> 8 );
		PM3_WRITE_DAC_REG(par, PM3RD_CursorPalette(44),
				  cmap.blue[bg_idx] >> 8 );
	}

	if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETIMAGE)) {
		u8 *bitmap = (u8 *)cursor->image.data;
		u8 *mask = (u8 *)cursor->mask;
		int i;
		int pos = PM3RD_CursorPattern(0);

		for (i = 0; i < cursor->image.height; i++) {
			int j = (cursor->image.width + 7) >> 3;
			int k = 8 - j;

			for (; j > 0; j--) {
				u8 data = *bitmap ^ *mask;

				if (cursor->rop == ROP_COPY)
					data = *mask & *bitmap;
				/* Upper 4 bits of bitmap data */
				PM3_WRITE_DAC_REG(par, pos++,
					cursor_bits_lookup[data >> 4] |
					(cursor_bits_lookup[*mask >> 4] << 1));
				/* Lower 4 bits of bitmap */
				PM3_WRITE_DAC_REG(par, pos++,
					cursor_bits_lookup[data & 0xf] |
					(cursor_bits_lookup[*mask & 0xf] << 1));
				bitmap++;
				mask++;
			}
			for (; k > 0; k--) {
				PM3_WRITE_DAC_REG(par, pos++, 0);
				PM3_WRITE_DAC_REG(par, pos++, 0);
			}
		}
		while (pos < PM3RD_CursorPattern(1024))
			PM3_WRITE_DAC_REG(par, pos++, 0);
	}
	return 0;
}

L
Linus Torvalds 已提交
722
/* write the mode to registers */
723
static void pm3fb_write_mode(struct fb_info *info)
L
Linus Torvalds 已提交
724
{
725
	struct pm3_par *par = info->par;
K
Krzysztof Helt 已提交
726 727
	char tempsync = 0x00;
	char tempmisc = 0x00;
728 729 730 731 732 733 734 735 736 737
	const u32 hsstart = info->var.right_margin;
	const u32 hsend = hsstart + info->var.hsync_len;
	const u32 hbend = hsend + info->var.left_margin;
	const u32 xres = (info->var.xres + 31) & ~31;
	const u32 htotal = xres + hbend;
	const u32 vsstart = info->var.lower_margin;
	const u32 vsend = vsstart + info->var.vsync_len;
	const u32 vbend = vsend + info->var.upper_margin;
	const u32 vtotal = info->var.yres + vbend;
	const u32 width = (info->var.xres_virtual + 7) & ~7;
K
Krzysztof Helt 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
	const unsigned bpp = info->var.bits_per_pixel;

	PM3_WAIT(par, 20);
	PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xffffffff);
	PM3_WRITE_REG(par, PM3Aperture0, 0x00000000);
	PM3_WRITE_REG(par, PM3Aperture1, 0x00000000);
	PM3_WRITE_REG(par, PM3FIFODis, 0x00000007);

	PM3_WRITE_REG(par, PM3HTotal,
			   pm3fb_shift_bpp(bpp, htotal - 1));
	PM3_WRITE_REG(par, PM3HsEnd,
			   pm3fb_shift_bpp(bpp, hsend));
	PM3_WRITE_REG(par, PM3HsStart,
			   pm3fb_shift_bpp(bpp, hsstart));
	PM3_WRITE_REG(par, PM3HbEnd,
			   pm3fb_shift_bpp(bpp, hbend));
	PM3_WRITE_REG(par, PM3HgEnd,
			   pm3fb_shift_bpp(bpp, hbend));
	PM3_WRITE_REG(par, PM3ScreenStride,
			   pm3fb_shift_bpp(bpp, width));
	PM3_WRITE_REG(par, PM3VTotal, vtotal - 1);
	PM3_WRITE_REG(par, PM3VsEnd, vsend - 1);
	PM3_WRITE_REG(par, PM3VsStart, vsstart - 1);
	PM3_WRITE_REG(par, PM3VbEnd, vbend);

	switch (bpp) {
L
Linus Torvalds 已提交
764
	case 8:
K
Krzysztof Helt 已提交
765
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
766
				   PM3ByApertureMode_PIXELSIZE_8BIT);
K
Krzysztof Helt 已提交
767
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
768 769 770 771 772
				   PM3ByApertureMode_PIXELSIZE_8BIT);
		break;

	case 16:
#ifndef __BIG_ENDIAN
K
Krzysztof Helt 已提交
773
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
774
				   PM3ByApertureMode_PIXELSIZE_16BIT);
K
Krzysztof Helt 已提交
775
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
776 777
				   PM3ByApertureMode_PIXELSIZE_16BIT);
#else
K
Krzysztof Helt 已提交
778
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
779 780
				   PM3ByApertureMode_PIXELSIZE_16BIT |
				   PM3ByApertureMode_BYTESWAP_BADC);
K
Krzysztof Helt 已提交
781
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
782 783 784 785 786 787 788
				   PM3ByApertureMode_PIXELSIZE_16BIT |
				   PM3ByApertureMode_BYTESWAP_BADC);
#endif /* ! __BIG_ENDIAN */
		break;

	case 32:
#ifndef __BIG_ENDIAN
K
Krzysztof Helt 已提交
789
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
790
				   PM3ByApertureMode_PIXELSIZE_32BIT);
K
Krzysztof Helt 已提交
791
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
792 793
				   PM3ByApertureMode_PIXELSIZE_32BIT);
#else
K
Krzysztof Helt 已提交
794
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
795 796
				   PM3ByApertureMode_PIXELSIZE_32BIT |
				   PM3ByApertureMode_BYTESWAP_DCBA);
K
Krzysztof Helt 已提交
797
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
798 799 800 801 802 803
				   PM3ByApertureMode_PIXELSIZE_32BIT |
				   PM3ByApertureMode_BYTESWAP_DCBA);
#endif /* ! __BIG_ENDIAN */
		break;

	default:
K
Krzysztof Helt 已提交
804
		DPRINTK("Unsupported depth %d\n", bpp);
L
Linus Torvalds 已提交
805 806 807 808 809 810 811 812 813 814
		break;
	}

	/*
	 * Oxygen VX1 - it appears that setting PM3VideoControl and
	 * then PM3RD_SyncControl to the same SYNC settings undoes
	 * any net change - they seem to xor together.  Only set the
	 * sync options in PM3RD_SyncControl.  --rmk
	 */
	{
815
		unsigned int video = par->video;
L
Linus Torvalds 已提交
816 817 818 819 820

		video &= ~(PM3VideoControl_HSYNC_MASK |
			   PM3VideoControl_VSYNC_MASK);
		video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
			 PM3VideoControl_VSYNC_ACTIVE_HIGH;
K
Krzysztof Helt 已提交
821
		PM3_WRITE_REG(par, PM3VideoControl, video);
L
Linus Torvalds 已提交
822
	}
K
Krzysztof Helt 已提交
823
	PM3_WRITE_REG(par, PM3VClkCtl,
824
			   (PM3_READ_REG(par, PM3VClkCtl) & 0xFFFFFFFC));
K
Krzysztof Helt 已提交
825 826
	PM3_WRITE_REG(par, PM3ScreenBase, par->base);
	PM3_WRITE_REG(par, PM3ChipConfig,
827
			   (PM3_READ_REG(par, PM3ChipConfig) & 0xFFFFFFFD));
L
Linus Torvalds 已提交
828

K
Krzysztof Helt 已提交
829
	wmb();
L
Linus Torvalds 已提交
830
	{
831 832 833 834 835 836 837 838 839 840 841 842 843
		unsigned char uninitialized_var(m);	/* ClkPreScale */
		unsigned char uninitialized_var(n);	/* ClkFeedBackScale */
		unsigned char uninitialized_var(p);	/* ClkPostScale */
		unsigned long pixclock = PICOS2KHZ(info->var.pixclock);

		(void)pm3fb_calculate_clock(pixclock, &m, &n, &p);

		DPRINTK("Pixclock: %ld, Pre: %d, Feedback: %d, Post: %d\n",
			pixclock, (int) m, (int) n, (int) p);

		PM3_WRITE_DAC_REG(par, PM3RD_DClk0PreScale, m);
		PM3_WRITE_DAC_REG(par, PM3RD_DClk0FeedbackScale, n);
		PM3_WRITE_DAC_REG(par, PM3RD_DClk0PostScale, p);
L
Linus Torvalds 已提交
844 845
	}
	/*
846
	   PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00);
L
Linus Torvalds 已提交
847 848
	 */
	/*
849
	   PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00);
L
Linus Torvalds 已提交
850
	 */
851
	if ((par->video & PM3VideoControl_HSYNC_MASK) ==
L
Linus Torvalds 已提交
852 853
	    PM3VideoControl_HSYNC_ACTIVE_HIGH)
		tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
854
	if ((par->video & PM3VideoControl_VSYNC_MASK) ==
L
Linus Torvalds 已提交
855 856 857
	    PM3VideoControl_VSYNC_ACTIVE_HIGH)
		tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;

858 859 860 861 862
	PM3_WRITE_DAC_REG(par, PM3RD_SyncControl, tempsync);
	DPRINTK("PM3RD_SyncControl: %d\n", tempsync);

	PM3_WRITE_DAC_REG(par, PM3RD_DACControl, 0x00);

K
Krzysztof Helt 已提交
863
	switch (pm3fb_depth(&info->var)) {
L
Linus Torvalds 已提交
864
	case 8:
865
		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
L
Linus Torvalds 已提交
866
				  PM3RD_PixelSize_8_BIT_PIXELS);
867
		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
L
Linus Torvalds 已提交
868 869 870 871 872
				  PM3RD_ColorFormat_CI8_COLOR |
				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
		tempmisc |= PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
		break;
	case 12:
873
		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
L
Linus Torvalds 已提交
874
				  PM3RD_PixelSize_16_BIT_PIXELS);
875
		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
L
Linus Torvalds 已提交
876 877 878 879 880
				  PM3RD_ColorFormat_4444_COLOR |
				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
881
		break;
L
Linus Torvalds 已提交
882
	case 15:
883
		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
L
Linus Torvalds 已提交
884
				  PM3RD_PixelSize_16_BIT_PIXELS);
885
		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
L
Linus Torvalds 已提交
886 887 888 889 890
				  PM3RD_ColorFormat_5551_FRONT_COLOR |
				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
891
		break;
L
Linus Torvalds 已提交
892
	case 16:
893
		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
L
Linus Torvalds 已提交
894
				  PM3RD_PixelSize_16_BIT_PIXELS);
895
		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
L
Linus Torvalds 已提交
896 897 898 899 900 901 902
				  PM3RD_ColorFormat_565_FRONT_COLOR |
				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW |
				  PM3RD_ColorFormat_LINEAR_COLOR_EXT_ENABLE);
		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
		break;
	case 32:
903
		PM3_WRITE_DAC_REG(par, PM3RD_PixelSize,
L
Linus Torvalds 已提交
904
				  PM3RD_PixelSize_32_BIT_PIXELS);
905
		PM3_WRITE_DAC_REG(par, PM3RD_ColorFormat,
L
Linus Torvalds 已提交
906 907 908 909 910 911
				  PM3RD_ColorFormat_8888_COLOR |
				  PM3RD_ColorFormat_COLOR_ORDER_BLUE_LOW);
		tempmisc |= PM3RD_MiscControl_DIRECTCOLOR_ENABLE |
			PM3RD_MiscControl_HIGHCOLOR_RES_ENABLE;
		break;
	}
912
	PM3_WRITE_DAC_REG(par, PM3RD_MiscControl, tempmisc);
L
Linus Torvalds 已提交
913 914
}

915 916 917 918 919 920
/*
 * hardware independent functions
 */
static int pm3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
	u32 lpitch;
K
Krzysztof Helt 已提交
921 922
	unsigned bpp = var->red.length + var->green.length
			+ var->blue.length + var->transp.length;
L
Linus Torvalds 已提交
923

K
Krzysztof Helt 已提交
924
	if (bpp != var->bits_per_pixel) {
K
Krzysztof Helt 已提交
925 926
		/* set predefined mode for bits_per_pixel settings */

K
Krzysztof Helt 已提交
927
		switch (var->bits_per_pixel) {
K
Krzysztof Helt 已提交
928
		case 8:
K
Krzysztof Helt 已提交
929 930 931 932 933 934
			var->red.length = 8;
			var->green.length = 8;
			var->blue.length = 8;
			var->red.offset = 0;
			var->green.offset = 0;
			var->blue.offset = 0;
K
Krzysztof Helt 已提交
935 936 937 938
			var->transp.offset = 0;
			var->transp.length = 0;
			break;
		case 16:
K
Krzysztof Helt 已提交
939 940
			var->red.length = 5;
			var->blue.length = 5;
K
Krzysztof Helt 已提交
941 942 943 944
			var->green.length = 6;
			var->transp.length = 0;
			break;
		case 32:
K
Krzysztof Helt 已提交
945 946 947
			var->red.length = 8;
			var->green.length = 8;
			var->blue.length = 8;
K
Krzysztof Helt 已提交
948 949 950
			var->transp.length = 8;
			break;
		default:
K
Krzysztof Helt 已提交
951 952
			DPRINTK("depth not supported: %u\n",
				var->bits_per_pixel);
K
Krzysztof Helt 已提交
953 954 955 956
			return -EINVAL;
		}
	}
	/* it is assumed BGRA order */
K
Krzysztof Helt 已提交
957
	if (var->bits_per_pixel > 8 ) {
K
Krzysztof Helt 已提交
958 959 960 961
		var->blue.offset = 0;
		var->green.offset = var->blue.length;
		var->red.offset = var->green.offset + var->green.length;
		var->transp.offset = var->red.offset + var->red.length;
L
Linus Torvalds 已提交
962
	}
K
Krzysztof Helt 已提交
963 964
	var->height = -1;
	var->width = -1;
L
Linus Torvalds 已提交
965

966
	if (var->xres != var->xres_virtual) {
K
Krzysztof Helt 已提交
967 968
		DPRINTK("virtual x resolution != "
			"physical x resolution not supported\n");
969 970
		return -EINVAL;
	}
L
Linus Torvalds 已提交
971

972
	if (var->yres > var->yres_virtual) {
K
Krzysztof Helt 已提交
973 974
		DPRINTK("virtual y resolution < "
			"physical y resolution not possible\n");
975
		return -EINVAL;
L
Linus Torvalds 已提交
976 977
	}

978 979 980
	if (var->xoffset) {
		DPRINTK("xoffset not supported\n");
		return -EINVAL;
L
Linus Torvalds 已提交
981 982
	}

983 984 985
	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) {
		DPRINTK("interlace not supported\n");
		return -EINVAL;
L
Linus Torvalds 已提交
986 987
	}

988
	var->xres = (var->xres + 31) & ~31; /* could sometimes be 8 */
K
Krzysztof Helt 已提交
989
	lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
L
Linus Torvalds 已提交
990

991 992 993 994
	if (var->xres < 200 || var->xres > 2048) {
		DPRINTK("width not supported: %u\n", var->xres);
		return -EINVAL;
	}
L
Linus Torvalds 已提交
995

996 997 998 999
	if (var->yres < 200 || var->yres > 4095) {
		DPRINTK("height not supported: %u\n", var->yres);
		return -EINVAL;
	}
L
Linus Torvalds 已提交
1000

1001 1002 1003 1004 1005 1006 1007
	if (lpitch * var->yres_virtual > info->fix.smem_len) {
		DPRINTK("no memory for screen (%ux%ux%u)\n",
			var->xres, var->yres_virtual, var->bits_per_pixel);
		return -EINVAL;
	}

	if (PICOS2KHZ(var->pixclock) > PM3_MAX_PIXCLOCK) {
K
Krzysztof Helt 已提交
1008 1009
		DPRINTK("pixclock too high (%ldKHz)\n",
			PICOS2KHZ(var->pixclock));
1010
		return -EINVAL;
L
Linus Torvalds 已提交
1011 1012
	}

1013
	var->accel_flags = 0;	/* Can't mmap if this is on */
L
Linus Torvalds 已提交
1014

1015 1016 1017 1018
	DPRINTK("Checking graphics mode at %dx%d depth %d\n",
		var->xres, var->yres, var->bits_per_pixel);
	return 0;
}
L
Linus Torvalds 已提交
1019

1020 1021 1022 1023
static int pm3fb_set_par(struct fb_info *info)
{
	struct pm3_par *par = info->par;
	const u32 xres = (info->var.xres + 31) & ~31;
K
Krzysztof Helt 已提交
1024
	const unsigned bpp = info->var.bits_per_pixel;
L
Linus Torvalds 已提交
1025

K
Krzysztof Helt 已提交
1026
	par->base = pm3fb_shift_bpp(bpp, (info->var.yoffset * xres)
1027 1028
					+ info->var.xoffset);
	par->video = 0;
L
Linus Torvalds 已提交
1029

1030 1031 1032 1033
	if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
		par->video |= PM3VideoControl_HSYNC_ACTIVE_HIGH;
	else
		par->video |= PM3VideoControl_HSYNC_ACTIVE_LOW;
L
Linus Torvalds 已提交
1034

1035 1036 1037 1038
	if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
		par->video |= PM3VideoControl_VSYNC_ACTIVE_HIGH;
	else
		par->video |= PM3VideoControl_VSYNC_ACTIVE_LOW;
L
Linus Torvalds 已提交
1039

1040 1041
	if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
		par->video |= PM3VideoControl_LINE_DOUBLE_ON;
L
Linus Torvalds 已提交
1042

1043
	if ((info->var.activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW)
1044
		par->video |= PM3VideoControl_ENABLE;
K
Krzysztof Helt 已提交
1045
	else
1046
		DPRINTK("PM3Video disabled\n");
K
Krzysztof Helt 已提交
1047

K
Krzysztof Helt 已提交
1048
	switch (bpp) {
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
	case 8:
		par->video |= PM3VideoControl_PIXELSIZE_8BIT;
		break;
	case 16:
		par->video |= PM3VideoControl_PIXELSIZE_16BIT;
		break;
	case 32:
		par->video |= PM3VideoControl_PIXELSIZE_32BIT;
		break;
	default:
		DPRINTK("Unsupported depth\n");
		break;
L
Linus Torvalds 已提交
1061 1062
	}

1063
	info->fix.visual =
K
Krzysztof Helt 已提交
1064
		(bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
K
Krzysztof Helt 已提交
1065
	info->fix.line_length = ((info->var.xres_virtual + 7)  >> 3) * bpp;
L
Linus Torvalds 已提交
1066

1067 1068
/*	pm3fb_clear_memory(info, 0);*/
	pm3fb_clear_colormap(par, 0, 0, 0);
K
Krzysztof Helt 已提交
1069
	PM3_WRITE_DAC_REG(par, PM3RD_CursorMode, 0);
K
Krzysztof Helt 已提交
1070
	pm3fb_init_engine(info);
1071 1072
	pm3fb_write_mode(info);
	return 0;
L
Linus Torvalds 已提交
1073 1074
}

1075 1076 1077
static int pm3fb_setcolreg(unsigned regno, unsigned red, unsigned green,
			   unsigned blue, unsigned transp,
			   struct fb_info *info)
L
Linus Torvalds 已提交
1078
{
1079 1080 1081 1082 1083 1084
	struct pm3_par *par = info->par;

	if (regno >= 256)  /* no. of hw registers */
	   return -EINVAL;

	/* grayscale works only partially under directcolor */
K
Krzysztof Helt 已提交
1085 1086
	/* grayscale = 0.30*R + 0.59*G + 0.11*B */
	if (info->var.grayscale)
1087 1088 1089 1090 1091 1092 1093
	   red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;

	/* Directcolor:
	 *   var->{color}.offset contains start of bitfield
	 *   var->{color}.length contains length of bitfield
	 *   {hardwarespecific} contains width of DAC
	 *   pseudo_palette[X] is programmed to (X << red.offset) |
K
Krzysztof Helt 已提交
1094 1095
	 *					(X << green.offset) |
	 *					(X << blue.offset)
1096 1097 1098 1099 1100
	 *   RAMDAC[X] is programmed to (red, green, blue)
	 *   color depth = SUM(var->{color}.length)
	 *
	 * Pseudocolor:
	 *	var->{color}.offset is 0
K
Krzysztof Helt 已提交
1101 1102
	 *	var->{color}.length contains width of DAC or the number
	 *			of unique colors available (color depth)
1103 1104 1105 1106
	 *	pseudo_palette is not used
	 *	RAMDAC[X] is programmed to (red, green, blue)
	 *	color depth = var->{color}.length
	 */
L
Linus Torvalds 已提交
1107

1108 1109 1110 1111
	/*
	 * This is the point where the color is converted to something that
	 * is acceptable by the hardware.
	 */
K
Krzysztof Helt 已提交
1112
#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
	red = CNVT_TOHW(red, info->var.red.length);
	green = CNVT_TOHW(green, info->var.green.length);
	blue = CNVT_TOHW(blue, info->var.blue.length);
	transp = CNVT_TOHW(transp, info->var.transp.length);
#undef CNVT_TOHW

	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
	info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
		u32 v;

		if (regno >= 16)
			return -EINVAL;

		v = (red << info->var.red.offset) |
			(green << info->var.green.offset) |
			(blue << info->var.blue.offset) |
			(transp << info->var.transp.offset);

		switch (info->var.bits_per_pixel) {
		case 8:
			break;
		case 16:
		case 32:
K
Krzysztof Helt 已提交
1136
			((u32 *)(info->pseudo_palette))[regno] = v;
1137 1138 1139
			break;
		}
		return 0;
K
Krzysztof Helt 已提交
1140
	} else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
1141
		pm3fb_set_color(par, regno, red, green, blue);
L
Linus Torvalds 已提交
1142

1143
	return 0;
L
Linus Torvalds 已提交
1144 1145
}

1146 1147
static int pm3fb_pan_display(struct fb_var_screeninfo *var,
				 struct fb_info *info)
L
Linus Torvalds 已提交
1148
{
1149
	struct pm3_par *par = info->par;
1150
	const u32 xres = (info->var.xres + 31) & ~31;
L
Linus Torvalds 已提交
1151

1152
	par->base = pm3fb_shift_bpp(info->var.bits_per_pixel,
1153 1154
					(var->yoffset * xres)
					+ var->xoffset);
K
Krzysztof Helt 已提交
1155 1156
	PM3_WAIT(par, 1);
	PM3_WRITE_REG(par, PM3ScreenBase, par->base);
1157 1158
	return 0;
}
L
Linus Torvalds 已提交
1159

1160 1161 1162 1163
static int pm3fb_blank(int blank_mode, struct fb_info *info)
{
	struct pm3_par *par = info->par;
	u32 video = par->video;
L
Linus Torvalds 已提交
1164

1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
	/*
	 * Oxygen VX1 - it appears that setting PM3VideoControl and
	 * then PM3RD_SyncControl to the same SYNC settings undoes
	 * any net change - they seem to xor together.  Only set the
	 * sync options in PM3RD_SyncControl.  --rmk
	 */
	video &= ~(PM3VideoControl_HSYNC_MASK |
		   PM3VideoControl_VSYNC_MASK);
	video |= PM3VideoControl_HSYNC_ACTIVE_HIGH |
		 PM3VideoControl_VSYNC_ACTIVE_HIGH;
L
Linus Torvalds 已提交
1175

1176 1177
	switch (blank_mode) {
	case FB_BLANK_UNBLANK:
K
Krzysztof Helt 已提交
1178
		video |= PM3VideoControl_ENABLE;
1179
		break;
K
Krzysztof Helt 已提交
1180
	case FB_BLANK_NORMAL:
K
Krzysztof Helt 已提交
1181
		video &= ~PM3VideoControl_ENABLE;
1182 1183
		break;
	case FB_BLANK_HSYNC_SUSPEND:
K
Krzysztof Helt 已提交
1184 1185
		video &= ~(PM3VideoControl_HSYNC_MASK |
			  PM3VideoControl_BLANK_ACTIVE_LOW);
1186 1187
		break;
	case FB_BLANK_VSYNC_SUSPEND:
K
Krzysztof Helt 已提交
1188 1189
		video &= ~(PM3VideoControl_VSYNC_MASK |
			  PM3VideoControl_BLANK_ACTIVE_LOW);
1190 1191
		break;
	case FB_BLANK_POWERDOWN:
K
Krzysztof Helt 已提交
1192 1193 1194
		video &= ~(PM3VideoControl_HSYNC_MASK |
			  PM3VideoControl_VSYNC_MASK |
			  PM3VideoControl_BLANK_ACTIVE_LOW);
1195 1196 1197 1198
		break;
	default:
		DPRINTK("Unsupported blanking %d\n", blank_mode);
		return 1;
L
Linus Torvalds 已提交
1199 1200
	}

K
Krzysztof Helt 已提交
1201
	PM3_WAIT(par, 1);
K
Krzysztof Helt 已提交
1202
	PM3_WRITE_REG(par, PM3VideoControl, video);
1203
	return 0;
L
Linus Torvalds 已提交
1204 1205
}

1206 1207 1208
	/*
	 *  Frame buffer operations
	 */
L
Linus Torvalds 已提交
1209

1210 1211 1212 1213 1214 1215
static struct fb_ops pm3fb_ops = {
	.owner		= THIS_MODULE,
	.fb_check_var	= pm3fb_check_var,
	.fb_set_par	= pm3fb_set_par,
	.fb_setcolreg	= pm3fb_setcolreg,
	.fb_pan_display	= pm3fb_pan_display,
K
Krzysztof Helt 已提交
1216
	.fb_fillrect	= pm3fb_fillrect,
1217 1218
	.fb_copyarea	= pm3fb_copyarea,
	.fb_imageblit	= pm3fb_imageblit,
1219
	.fb_blank	= pm3fb_blank,
K
Krzysztof Helt 已提交
1220
	.fb_sync	= pm3fb_sync,
K
Krzysztof Helt 已提交
1221
	.fb_cursor	= pm3fb_cursor,
1222
};
L
Linus Torvalds 已提交
1223

1224
/* ------------------------------------------------------------------------- */
L
Linus Torvalds 已提交
1225

1226 1227 1228
	/*
	 *  Initialization
	 */
L
Linus Torvalds 已提交
1229

1230 1231
/* mmio register are already mapped when this function is called */
/* the pm3fb_fix.smem_start is also set */
1232
static unsigned long pm3fb_size_memory(struct pm3_par *par)
L
Linus Torvalds 已提交
1233
{
K
Krzysztof Helt 已提交
1234 1235
	unsigned long	memsize = 0;
	unsigned long	tempBypass, i, temp1, temp2;
1236
	unsigned char	__iomem *screen_mem;
L
Linus Torvalds 已提交
1237

K
Krzysztof Helt 已提交
1238
	pm3fb_fix.smem_len = 64 * 1024l * 1024; /* request full aperture size */
1239 1240 1241 1242 1243
	/* Linear frame buffer - request region and map it. */
	if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
				 "pm3fb smem")) {
		printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
		return 0;
L
Linus Torvalds 已提交
1244
	}
1245 1246 1247 1248 1249 1250
	screen_mem =
		ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
	if (!screen_mem) {
		printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
		release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
		return 0;
L
Linus Torvalds 已提交
1251 1252
	}

1253 1254
	/* TODO: card-specific stuff, *before* accessing *any* FB memory */
	/* For Appian Jeronimo 2000 board second head */
L
Linus Torvalds 已提交
1255

1256
	tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask);
L
Linus Torvalds 已提交
1257

1258
	DPRINTK("PM3MemBypassWriteMask was: 0x%08lx\n", tempBypass);
L
Linus Torvalds 已提交
1259

K
Krzysztof Helt 已提交
1260 1261
	PM3_WAIT(par, 1);
	PM3_WRITE_REG(par, PM3MemBypassWriteMask, 0xFFFFFFFF);
L
Linus Torvalds 已提交
1262

K
Krzysztof Helt 已提交
1263 1264 1265
	/* pm3 split up memory, replicates, and do a lot of
	 * nasty stuff IMHO ;-)
	 */
1266 1267 1268 1269 1270
	for (i = 0; i < 32; i++) {
		fb_writel(i * 0x00345678,
			  (screen_mem + (i * 1048576)));
		mb();
		temp1 = fb_readl((screen_mem + (i * 1048576)));
L
Linus Torvalds 已提交
1271

1272 1273 1274
		/* Let's check for wrapover, write will fail at 16MB boundary */
		if (temp1 == (i * 0x00345678))
			memsize = i;
L
Linus Torvalds 已提交
1275
		else
1276
			break;
L
Linus Torvalds 已提交
1277 1278
	}

1279
	DPRINTK("First detect pass already got %ld MB\n", memsize + 1);
L
Linus Torvalds 已提交
1280

1281 1282 1283
	if (memsize + 1 == i) {
		for (i = 0; i < 32; i++) {
			/* Clear first 32MB ; 0 is 0, no need to byteswap */
K
Krzysztof Helt 已提交
1284
			writel(0x0000000, (screen_mem + (i * 1048576)));
L
Linus Torvalds 已提交
1285
		}
K
Krzysztof Helt 已提交
1286
		wmb();
L
Linus Torvalds 已提交
1287

1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
		for (i = 32; i < 64; i++) {
			fb_writel(i * 0x00345678,
				  (screen_mem + (i * 1048576)));
			mb();
			temp1 =
			    fb_readl((screen_mem + (i * 1048576)));
			temp2 =
			    fb_readl((screen_mem + ((i - 32) * 1048576)));
			/* different value, different RAM... */
			if ((temp1 == (i * 0x00345678)) && (temp2 == 0))
				memsize = i;
			else
				break;
L
Linus Torvalds 已提交
1301 1302
		}
	}
1303
	DPRINTK("Second detect pass got %ld MB\n", memsize + 1);
L
Linus Torvalds 已提交
1304

K
Krzysztof Helt 已提交
1305 1306
	PM3_WAIT(par, 1);
	PM3_WRITE_REG(par, PM3MemBypassWriteMask, tempBypass);
L
Linus Torvalds 已提交
1307

1308 1309 1310
	iounmap(screen_mem);
	release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
	memsize = 1048576 * (memsize + 1);
L
Linus Torvalds 已提交
1311

1312
	DPRINTK("Returning 0x%08lx bytes\n", memsize);
L
Linus Torvalds 已提交
1313

1314
	return memsize;
L
Linus Torvalds 已提交
1315 1316
}

1317
static int pm3fb_probe(struct pci_dev *dev, const struct pci_device_id *ent)
L
Linus Torvalds 已提交
1318
{
1319 1320
	struct fb_info *info;
	struct pm3_par *par;
K
Krzysztof Helt 已提交
1321 1322 1323
	struct device *device = &dev->dev; /* for pci drivers */
	int err;
	int retval = -ENXIO;
L
Linus Torvalds 已提交
1324

1325 1326 1327 1328
	err = pci_enable_device(dev);
	if (err) {
		printk(KERN_WARNING "pm3fb: Can't enable PCI dev: %d\n", err);
		return err;
L
Linus Torvalds 已提交
1329
	}
1330 1331 1332 1333
	/*
	 * Dynamically allocate info and par
	 */
	info = framebuffer_alloc(sizeof(struct pm3_par), device);
L
Linus Torvalds 已提交
1334

1335 1336 1337
	if (!info)
		return -ENOMEM;
	par = info->par;
L
Linus Torvalds 已提交
1338

1339 1340 1341 1342 1343 1344
	/*
	 * Here we set the screen_base to the virtual memory address
	 * for the framebuffer.
	 */
	pm3fb_fix.mmio_start = pci_resource_start(dev, 0);
	pm3fb_fix.mmio_len = PM3_REGS_SIZE;
K
Krzysztof Helt 已提交
1345 1346 1347 1348
#if defined(__BIG_ENDIAN)
	pm3fb_fix.mmio_start += PM3_REGS_SIZE;
	DPRINTK("Adjusting register base for big-endian.\n");
#endif
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367

	/* Registers - request region and map it. */
	if (!request_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len,
				 "pm3fb regbase")) {
		printk(KERN_WARNING "pm3fb: Can't reserve regbase.\n");
		goto err_exit_neither;
	}
	par->v_regs =
		ioremap_nocache(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
	if (!par->v_regs) {
		printk(KERN_WARNING "pm3fb: Can't remap %s register area.\n",
			pm3fb_fix.id);
		release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
		goto err_exit_neither;
	}

	/* Linear frame buffer - request region and map it. */
	pm3fb_fix.smem_start = pci_resource_start(dev, 1);
	pm3fb_fix.smem_len = pm3fb_size_memory(par);
K
Krzysztof Helt 已提交
1368
	if (!pm3fb_fix.smem_len) {
1369 1370
		printk(KERN_WARNING "pm3fb: Can't find memory on board.\n");
		goto err_exit_mmio;
L
Linus Torvalds 已提交
1371
	}
1372 1373 1374 1375
	if (!request_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len,
				 "pm3fb smem")) {
		printk(KERN_WARNING "pm3fb: Can't reserve smem.\n");
		goto err_exit_mmio;
L
Linus Torvalds 已提交
1376
	}
1377 1378 1379 1380 1381 1382
	info->screen_base =
		ioremap_nocache(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
	if (!info->screen_base) {
		printk(KERN_WARNING "pm3fb: Can't ioremap smem area.\n");
		release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
		goto err_exit_mmio;
L
Linus Torvalds 已提交
1383
	}
1384
	info->screen_size = pm3fb_fix.smem_len;
L
Linus Torvalds 已提交
1385

1386
#ifdef CONFIG_MTRR
K
Krzysztof Helt 已提交
1387
	if (!nomtrr)
1388 1389 1390 1391
		par->mtrr_handle = mtrr_add(pm3fb_fix.smem_start,
						pm3fb_fix.smem_len,
						MTRR_TYPE_WRCOMB, 1);
#endif
1392
	info->fbops = &pm3fb_ops;
L
Linus Torvalds 已提交
1393

1394
	par->video = PM3_READ_REG(par, PM3VideoControl);
L
Linus Torvalds 已提交
1395

1396 1397
	info->fix = pm3fb_fix;
	info->pseudo_palette = par->palette;
K
Krzysztof Helt 已提交
1398
	info->flags = FBINFO_DEFAULT |
K
Krzysztof Helt 已提交
1399 1400
			FBINFO_HWACCEL_XPAN |
			FBINFO_HWACCEL_YPAN |
1401 1402 1403
			FBINFO_HWACCEL_COPYAREA |
			FBINFO_HWACCEL_IMAGEBLIT |
			FBINFO_HWACCEL_FILLRECT;
L
Linus Torvalds 已提交
1404

1405
	if (noaccel) {
K
Krzysztof Helt 已提交
1406 1407
		printk(KERN_DEBUG "disabling acceleration\n");
		info->flags |= FBINFO_HWACCEL_DISABLED;
1408
	}
K
Krzysztof Helt 已提交
1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419
	info->pixmap.addr = kmalloc(PM3_PIXMAP_SIZE, GFP_KERNEL);
	if (!info->pixmap.addr) {
		retval = -ENOMEM;
		goto err_exit_pixmap;
	}
	info->pixmap.size = PM3_PIXMAP_SIZE;
	info->pixmap.buf_align = 4;
	info->pixmap.scan_align = 4;
	info->pixmap.access_align = 32;
	info->pixmap.flags = FB_PIXMAP_SYSTEM;

1420 1421 1422 1423 1424 1425
	/*
	 * This should give a reasonable default video mode. The following is
	 * done when we can set a video mode.
	 */
	if (!mode_option)
		mode_option = "640x480@60";
L
Linus Torvalds 已提交
1426

1427
	retval = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
L
Linus Torvalds 已提交
1428

1429 1430 1431
	if (!retval || retval == 4) {
		retval = -EINVAL;
		goto err_exit_both;
L
Linus Torvalds 已提交
1432 1433
	}

1434 1435 1436
	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
		retval = -ENOMEM;
		goto err_exit_both;
L
Linus Torvalds 已提交
1437 1438
	}

1439 1440 1441 1442
	/*
	 * For drivers that can...
	 */
	pm3fb_check_var(&info->var, info);
L
Linus Torvalds 已提交
1443

1444 1445 1446
	if (register_framebuffer(info) < 0) {
		retval = -EINVAL;
		goto err_exit_all;
L
Linus Torvalds 已提交
1447
	}
1448 1449
	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
	   info->fix.id);
K
Krzysztof Helt 已提交
1450
	pci_set_drvdata(dev, info);
1451
	return 0;
L
Linus Torvalds 已提交
1452

1453 1454 1455
 err_exit_all:
	fb_dealloc_cmap(&info->cmap);
 err_exit_both:
K
Krzysztof Helt 已提交
1456 1457
	kfree(info->pixmap.addr);
 err_exit_pixmap:
1458 1459 1460 1461 1462 1463 1464 1465
	iounmap(info->screen_base);
	release_mem_region(pm3fb_fix.smem_start, pm3fb_fix.smem_len);
 err_exit_mmio:
	iounmap(par->v_regs);
	release_mem_region(pm3fb_fix.mmio_start, pm3fb_fix.mmio_len);
 err_exit_neither:
	framebuffer_release(info);
	return retval;
L
Linus Torvalds 已提交
1466 1467
}

1468 1469 1470
	/*
	 *  Cleanup
	 */
1471
static void pm3fb_remove(struct pci_dev *dev)
L
Linus Torvalds 已提交
1472
{
1473
	struct fb_info *info = pci_get_drvdata(dev);
L
Linus Torvalds 已提交
1474

1475 1476 1477
	if (info) {
		struct fb_fix_screeninfo *fix = &info->fix;
		struct pm3_par *par = info->par;
L
Linus Torvalds 已提交
1478

1479 1480
		unregister_framebuffer(info);
		fb_dealloc_cmap(&info->cmap);
L
Linus Torvalds 已提交
1481

1482 1483 1484 1485 1486
#ifdef CONFIG_MTRR
	if (par->mtrr_handle >= 0)
		mtrr_del(par->mtrr_handle, info->fix.smem_start,
			 info->fix.smem_len);
#endif /* CONFIG_MTRR */
1487 1488 1489 1490
		iounmap(info->screen_base);
		release_mem_region(fix->smem_start, fix->smem_len);
		iounmap(par->v_regs);
		release_mem_region(fix->mmio_start, fix->mmio_len);
L
Linus Torvalds 已提交
1491

K
Krzysztof Helt 已提交
1492
		kfree(info->pixmap.addr);
1493
		framebuffer_release(info);
L
Linus Torvalds 已提交
1494 1495 1496
	}
}

1497 1498
static struct pci_device_id pm3fb_id_table[] = {
	{ PCI_VENDOR_ID_3DLABS, 0x0a,
K
Krzysztof Helt 已提交
1499
	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
1500 1501
	{ 0, }
};
L
Linus Torvalds 已提交
1502

1503 1504 1505 1506 1507
/* For PCI drivers */
static struct pci_driver pm3fb_driver = {
	.name =		"pm3fb",
	.id_table =	pm3fb_id_table,
	.probe =	pm3fb_probe,
1508
	.remove =	pm3fb_remove,
1509
};
L
Linus Torvalds 已提交
1510

1511
MODULE_DEVICE_TABLE(pci, pm3fb_id_table);
L
Linus Torvalds 已提交
1512

1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525
#ifndef MODULE
	/*
	 *  Setup
	 */

/*
 * Only necessary if your driver takes special options,
 * otherwise we fall back on the generic fb_setup().
 */
static int __init pm3fb_setup(char *options)
{
	char *this_opt;

J
Joe Perches 已提交
1526
	/* Parse user specified options (`video=pm3fb:') */
1527 1528 1529 1530 1531 1532
	if (!options || !*options)
		return 0;

	while ((this_opt = strsep(&options, ",")) != NULL) {
		if (!*this_opt)
			continue;
K
Krzysztof Helt 已提交
1533
		else if (!strncmp(this_opt, "noaccel", 7))
1534
			noaccel = 1;
K
Krzysztof Helt 已提交
1535 1536
		else if (!strncmp(this_opt, "hwcursor=", 9))
			hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
1537
#ifdef CONFIG_MTRR
K
Krzysztof Helt 已提交
1538
		else if (!strncmp(this_opt, "nomtrr", 6))
1539 1540
			nomtrr = 1;
#endif
K
Krzysztof Helt 已提交
1541
		else
1542 1543 1544 1545 1546 1547
			mode_option = this_opt;
	}
	return 0;
}
#endif /* MODULE */

A
Adrian Bunk 已提交
1548
static int __init pm3fb_init(void)
K
Krzysztof Helt 已提交
1549
{
1550 1551 1552
	/*
	 *  For kernel boot options (in 'video=pm3fb:<options>' format)
	 */
1553
#ifndef MODULE
1554 1555 1556
	char *option = NULL;

	if (fb_get_options("pm3fb", &option))
1557
		return -ENODEV;
1558
	pm3fb_setup(option);
L
Linus Torvalds 已提交
1559
#endif
1560

1561
	return pci_register_driver(&pm3fb_driver);
L
Linus Torvalds 已提交
1562 1563
}

1564
#ifdef MODULE
1565
static void __exit pm3fb_exit(void)
L
Linus Torvalds 已提交
1566
{
1567
	pci_unregister_driver(&pm3fb_driver);
L
Linus Torvalds 已提交
1568 1569
}

1570
module_exit(pm3fb_exit);
1571 1572 1573
#endif
module_init(pm3fb_init);

1574 1575
module_param(mode_option, charp, 0);
MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
1576 1577
module_param(noaccel, bool, 0);
MODULE_PARM_DESC(noaccel, "Disable acceleration");
K
Krzysztof Helt 已提交
1578 1579 1580
module_param(hwcursor, int, 0644);
MODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
			"(1=enable, 0=disable, default=1)");
1581 1582 1583 1584
#ifdef CONFIG_MTRR
module_param(nomtrr, bool, 0);
MODULE_PARM_DESC(nomtrr, "Disable MTRR support (0 or 1=disabled) (default=0)");
#endif
1585

K
Krzysztof Helt 已提交
1586
MODULE_DESCRIPTION("Permedia3 framebuffer device driver");
1587
MODULE_LICENSE("GPL");