pm3fb.c 42.5 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 48
#define DPRINTK(a, b...)	\
	printk(KERN_DEBUG "pm3fb: %s: " a, __FUNCTION__ , ## 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
static char *mode_option __devinitdata;
K
Krzysztof Helt 已提交
60
static int noaccel __devinitdata;
61 62 63

/* mtrr option */
#ifdef CONFIG_MTRR
K
Krzysztof Helt 已提交
64
static int nomtrr __devinitdata;
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 87 88 89 90 91 92 93
/*
 * 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.
 */
static struct fb_fix_screeninfo pm3fb_fix __devinitdata = {
	.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
	}
K
Krzysztof Helt 已提交
542
	if (image->depth != 1)
543
		return cfb_imageblit(info, image);
K
Krzysztof Helt 已提交
544

545 546 547 548 549 550 551 552 553
	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 已提交
554
	PM3_WAIT(par, 7);
555 556 557 558 559 560

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

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


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

		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 已提交
607 608
/* end of acceleration functions */

K
Krzysztof Helt 已提交
609 610 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
/*
 *	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 已提交
720
/* write the mode to registers */
721
static void pm3fb_write_mode(struct fb_info *info)
L
Linus Torvalds 已提交
722
{
723
	struct pm3_par *par = info->par;
K
Krzysztof Helt 已提交
724 725
	char tempsync = 0x00;
	char tempmisc = 0x00;
726 727 728 729 730 731 732 733 734 735
	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 已提交
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
	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 已提交
762
	case 8:
K
Krzysztof Helt 已提交
763
		PM3_WRITE_REG(par, PM3ByAperture1Mode,
L
Linus Torvalds 已提交
764
				   PM3ByApertureMode_PIXELSIZE_8BIT);
K
Krzysztof Helt 已提交
765
		PM3_WRITE_REG(par, PM3ByAperture2Mode,
L
Linus Torvalds 已提交
766 767 768 769 770
				   PM3ByApertureMode_PIXELSIZE_8BIT);
		break;

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

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

	default:
K
Krzysztof Helt 已提交
802
		DPRINTK("Unsupported depth %d\n", bpp);
L
Linus Torvalds 已提交
803 804 805 806 807 808 809 810 811 812
		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
	 */
	{
813
		unsigned int video = par->video;
L
Linus Torvalds 已提交
814 815 816 817 818

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

K
Krzysztof Helt 已提交
827
	wmb();
L
Linus Torvalds 已提交
828
	{
829 830 831 832 833 834 835 836 837 838 839 840 841
		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 已提交
842 843
	}
	/*
844
	   PM3_WRITE_DAC_REG(par, PM3RD_IndexControl, 0x00);
L
Linus Torvalds 已提交
845 846
	 */
	/*
847
	   PM3_SLOW_WRITE_REG(par, PM3RD_IndexControl, 0x00);
L
Linus Torvalds 已提交
848
	 */
849
	if ((par->video & PM3VideoControl_HSYNC_MASK) ==
L
Linus Torvalds 已提交
850 851
	    PM3VideoControl_HSYNC_ACTIVE_HIGH)
		tempsync |= PM3RD_SyncControl_HSYNC_ACTIVE_HIGH;
852
	if ((par->video & PM3VideoControl_VSYNC_MASK) ==
L
Linus Torvalds 已提交
853 854 855
	    PM3VideoControl_VSYNC_ACTIVE_HIGH)
		tempsync |= PM3RD_SyncControl_VSYNC_ACTIVE_HIGH;

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

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

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

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

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

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

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

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

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

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

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

999 1000 1001 1002 1003 1004 1005
	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 已提交
1006 1007
		DPRINTK("pixclock too high (%ldKHz)\n",
			PICOS2KHZ(var->pixclock));
1008
		return -EINVAL;
L
Linus Torvalds 已提交
1009 1010
	}

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

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

1018 1019 1020 1021
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 已提交
1022
	const unsigned bpp = info->var.bits_per_pixel;
L
Linus Torvalds 已提交
1023

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

1028 1029 1030 1031
	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 已提交
1032

1033 1034 1035 1036
	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 已提交
1037

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

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

K
Krzysztof Helt 已提交
1046
	switch (bpp) {
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
	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 已提交
1059 1060
	}

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

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

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

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

	/* grayscale works only partially under directcolor */
K
Krzysztof Helt 已提交
1083 1084
	/* grayscale = 0.30*R + 0.59*G + 0.11*B */
	if (info->var.grayscale)
1085 1086 1087 1088 1089 1090 1091
	   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 已提交
1092 1093
	 *					(X << green.offset) |
	 *					(X << blue.offset)
1094 1095 1096 1097 1098
	 *   RAMDAC[X] is programmed to (red, green, blue)
	 *   color depth = SUM(var->{color}.length)
	 *
	 * Pseudocolor:
	 *	var->{color}.offset is 0
K
Krzysztof Helt 已提交
1099 1100
	 *	var->{color}.length contains width of DAC or the number
	 *			of unique colors available (color depth)
1101 1102 1103 1104
	 *	pseudo_palette is not used
	 *	RAMDAC[X] is programmed to (red, green, blue)
	 *	color depth = var->{color}.length
	 */
L
Linus Torvalds 已提交
1105

1106 1107 1108 1109
	/*
	 * This is the point where the color is converted to something that
	 * is acceptable by the hardware.
	 */
K
Krzysztof Helt 已提交
1110
#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133
	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 已提交
1134
			((u32 *)(info->pseudo_palette))[regno] = v;
1135 1136 1137
			break;
		}
		return 0;
K
Krzysztof Helt 已提交
1138
	} else if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR)
1139
		pm3fb_set_color(par, regno, red, green, blue);
L
Linus Torvalds 已提交
1140

1141
	return 0;
L
Linus Torvalds 已提交
1142 1143
}

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

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

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

1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
	/*
	 * 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 已提交
1173

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

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

1204 1205 1206
	/*
	 *  Frame buffer operations
	 */
L
Linus Torvalds 已提交
1207

1208 1209 1210 1211 1212 1213
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 已提交
1214
	.fb_fillrect	= pm3fb_fillrect,
1215 1216
	.fb_copyarea	= pm3fb_copyarea,
	.fb_imageblit	= pm3fb_imageblit,
1217
	.fb_blank	= pm3fb_blank,
K
Krzysztof Helt 已提交
1218
	.fb_sync	= pm3fb_sync,
K
Krzysztof Helt 已提交
1219
	.fb_cursor	= pm3fb_cursor,
1220
};
L
Linus Torvalds 已提交
1221

1222
/* ------------------------------------------------------------------------- */
L
Linus Torvalds 已提交
1223

1224 1225 1226
	/*
	 *  Initialization
	 */
L
Linus Torvalds 已提交
1227

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

K
Krzysztof Helt 已提交
1236
	pm3fb_fix.smem_len = 64 * 1024l * 1024; /* request full aperture size */
1237 1238 1239 1240 1241
	/* 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 已提交
1242
	}
1243 1244 1245 1246 1247 1248
	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 已提交
1249 1250
	}

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

1254
	tempBypass = PM3_READ_REG(par, PM3MemBypassWriteMask);
L
Linus Torvalds 已提交
1255

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

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

K
Krzysztof Helt 已提交
1261 1262 1263
	/* pm3 split up memory, replicates, and do a lot of
	 * nasty stuff IMHO ;-)
	 */
1264 1265 1266 1267 1268
	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 已提交
1269

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

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

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

1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
		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 已提交
1299 1300
		}
	}
1301
	DPRINTK("Second detect pass got %ld MB\n", memsize + 1);
L
Linus Torvalds 已提交
1302

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

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

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

1312
	return memsize;
L
Linus Torvalds 已提交
1313 1314
}

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

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

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

1338 1339 1340 1341 1342 1343
	/*
	 * 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 已提交
1344 1345 1346 1347
#if defined(__BIG_ENDIAN)
	pm3fb_fix.mmio_start += PM3_REGS_SIZE;
	DPRINTK("Adjusting register base for big-endian.\n");
#endif
1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366

	/* 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 已提交
1367
	if (!pm3fb_fix.smem_len) {
1368 1369
		printk(KERN_WARNING "pm3fb: Can't find memory on board.\n");
		goto err_exit_mmio;
L
Linus Torvalds 已提交
1370
	}
1371 1372 1373 1374
	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 已提交
1375
	}
1376 1377 1378 1379 1380 1381
	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 已提交
1382
	}
1383
	info->screen_size = pm3fb_fix.smem_len;
L
Linus Torvalds 已提交
1384

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

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

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

1404
	if (noaccel) {
K
Krzysztof Helt 已提交
1405 1406
		printk(KERN_DEBUG "disabling acceleration\n");
		info->flags |= FBINFO_HWACCEL_DISABLED;
1407
	}
K
Krzysztof Helt 已提交
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
	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;

1419 1420 1421 1422 1423 1424
	/*
	 * 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 已提交
1425

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

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

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

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

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

1452 1453 1454
 err_exit_all:
	fb_dealloc_cmap(&info->cmap);
 err_exit_both:
K
Krzysztof Helt 已提交
1455 1456
	kfree(info->pixmap.addr);
 err_exit_pixmap:
1457 1458 1459 1460 1461 1462 1463 1464
	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 已提交
1465 1466
}

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

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

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

1481 1482 1483 1484 1485
#ifdef CONFIG_MTRR
	if (par->mtrr_handle >= 0)
		mtrr_del(par->mtrr_handle, info->fix.smem_start,
			 info->fix.smem_len);
#endif /* CONFIG_MTRR */
1486 1487 1488 1489
		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 已提交
1490

1491
		pci_set_drvdata(dev, NULL);
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 1508 1509
/* For PCI drivers */
static struct pci_driver pm3fb_driver = {
	.name =		"pm3fb",
	.id_table =	pm3fb_id_table,
	.probe =	pm3fb_probe,
	.remove =	__devexit_p(pm3fb_remove),
};
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 1526 1527 1528 1529 1530 1531 1532
#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;

	/* Parse user speficied options (`video=pm3fb:') */
	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");