162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * linux/drivers/video/neofb.c -- NeoMagic Framebuffer Driver
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2001-2002  Denis Oliver Kropp <dok@directfb.org>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Card specific code is based on XFree86's neomagic driver.
862306a36Sopenharmony_ci * Framebuffer framework code is based on code of cyber2000fb.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General
1162306a36Sopenharmony_ci * Public License.  See the file COPYING in the main directory of this
1262306a36Sopenharmony_ci * archive for more details.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * 0.4.1
1662306a36Sopenharmony_ci *  - Cosmetic changes (dok)
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * 0.4
1962306a36Sopenharmony_ci *  - Toshiba Libretto support, allow modes larger than LCD size if
2062306a36Sopenharmony_ci *    LCD is disabled, keep BIOS settings if internal/external display
2162306a36Sopenharmony_ci *    haven't been enabled explicitly
2262306a36Sopenharmony_ci *                          (Thomas J. Moore <dark@mama.indstate.edu>)
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * 0.3.3
2562306a36Sopenharmony_ci *  - Porting over to new fbdev api. (jsimmons)
2662306a36Sopenharmony_ci *
2762306a36Sopenharmony_ci * 0.3.2
2862306a36Sopenharmony_ci *  - got rid of all floating point (dok)
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * 0.3.1
3162306a36Sopenharmony_ci *  - added module license (dok)
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * 0.3
3462306a36Sopenharmony_ci *  - hardware accelerated clear and move for 2200 and above (dok)
3562306a36Sopenharmony_ci *  - maximum allowed dotclock is handled now (dok)
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * 0.2.1
3862306a36Sopenharmony_ci *  - correct panning after X usage (dok)
3962306a36Sopenharmony_ci *  - added module and kernel parameters (dok)
4062306a36Sopenharmony_ci *  - no stretching if external display is enabled (dok)
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * 0.2
4362306a36Sopenharmony_ci *  - initial version (dok)
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * TODO
4762306a36Sopenharmony_ci * - ioctl for internal/external switching
4862306a36Sopenharmony_ci * - blanking
4962306a36Sopenharmony_ci * - 32bit depth support, maybe impossible
5062306a36Sopenharmony_ci * - disable pan-on-sync, need specs
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * BUGS
5362306a36Sopenharmony_ci * - white margin on bootup like with tdfxfb (colormap problem?)
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#include <linux/aperture.h>
5862306a36Sopenharmony_ci#include <linux/module.h>
5962306a36Sopenharmony_ci#include <linux/kernel.h>
6062306a36Sopenharmony_ci#include <linux/errno.h>
6162306a36Sopenharmony_ci#include <linux/string.h>
6262306a36Sopenharmony_ci#include <linux/mm.h>
6362306a36Sopenharmony_ci#include <linux/slab.h>
6462306a36Sopenharmony_ci#include <linux/delay.h>
6562306a36Sopenharmony_ci#include <linux/fb.h>
6662306a36Sopenharmony_ci#include <linux/pci.h>
6762306a36Sopenharmony_ci#include <linux/init.h>
6862306a36Sopenharmony_ci#ifdef CONFIG_TOSHIBA
6962306a36Sopenharmony_ci#include <linux/toshiba.h>
7062306a36Sopenharmony_ci#endif
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#include <asm/io.h>
7362306a36Sopenharmony_ci#include <asm/irq.h>
7462306a36Sopenharmony_ci#include <video/vga.h>
7562306a36Sopenharmony_ci#include <video/neomagic.h>
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci#define NEOFB_VERSION "0.4.2"
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic bool internal;
8262306a36Sopenharmony_cistatic bool external;
8362306a36Sopenharmony_cistatic bool libretto;
8462306a36Sopenharmony_cistatic bool nostretch;
8562306a36Sopenharmony_cistatic bool nopciburst;
8662306a36Sopenharmony_cistatic char *mode_option = NULL;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#ifdef MODULE
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciMODULE_AUTHOR("(c) 2001-2002  Denis Oliver Kropp <dok@convergence.de>");
9162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9262306a36Sopenharmony_ciMODULE_DESCRIPTION("FBDev driver for NeoMagic PCI Chips");
9362306a36Sopenharmony_cimodule_param(internal, bool, 0);
9462306a36Sopenharmony_ciMODULE_PARM_DESC(internal, "Enable output on internal LCD Display.");
9562306a36Sopenharmony_cimodule_param(external, bool, 0);
9662306a36Sopenharmony_ciMODULE_PARM_DESC(external, "Enable output on external CRT.");
9762306a36Sopenharmony_cimodule_param(libretto, bool, 0);
9862306a36Sopenharmony_ciMODULE_PARM_DESC(libretto, "Force Libretto 100/110 800x480 LCD.");
9962306a36Sopenharmony_cimodule_param(nostretch, bool, 0);
10062306a36Sopenharmony_ciMODULE_PARM_DESC(nostretch,
10162306a36Sopenharmony_ci		 "Disable stretching of modes smaller than LCD.");
10262306a36Sopenharmony_cimodule_param(nopciburst, bool, 0);
10362306a36Sopenharmony_ciMODULE_PARM_DESC(nopciburst, "Disable PCI burst mode.");
10462306a36Sopenharmony_cimodule_param(mode_option, charp, 0);
10562306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Preferred video mode ('640x480-8@60', etc)");
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci#endif
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic biosMode bios8[] = {
11362306a36Sopenharmony_ci	{320, 240, 0x40},
11462306a36Sopenharmony_ci	{300, 400, 0x42},
11562306a36Sopenharmony_ci	{640, 400, 0x20},
11662306a36Sopenharmony_ci	{640, 480, 0x21},
11762306a36Sopenharmony_ci	{800, 600, 0x23},
11862306a36Sopenharmony_ci	{1024, 768, 0x25},
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic biosMode bios16[] = {
12262306a36Sopenharmony_ci	{320, 200, 0x2e},
12362306a36Sopenharmony_ci	{320, 240, 0x41},
12462306a36Sopenharmony_ci	{300, 400, 0x43},
12562306a36Sopenharmony_ci	{640, 480, 0x31},
12662306a36Sopenharmony_ci	{800, 600, 0x34},
12762306a36Sopenharmony_ci	{1024, 768, 0x37},
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic biosMode bios24[] = {
13162306a36Sopenharmony_ci	{640, 480, 0x32},
13262306a36Sopenharmony_ci	{800, 600, 0x35},
13362306a36Sopenharmony_ci	{1024, 768, 0x38}
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
13762306a36Sopenharmony_ci/* FIXME: guessed values, wrong */
13862306a36Sopenharmony_cistatic biosMode bios32[] = {
13962306a36Sopenharmony_ci	{640, 480, 0x33},
14062306a36Sopenharmony_ci	{800, 600, 0x36},
14162306a36Sopenharmony_ci	{1024, 768, 0x39}
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci#endif
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic inline void write_le32(int regindex, u32 val, const struct neofb_par *par)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	writel(val, par->neo2200 + par->cursorOff + regindex);
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int neoFindMode(int xres, int yres, int depth)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int xres_s;
15362306a36Sopenharmony_ci	int i, size;
15462306a36Sopenharmony_ci	biosMode *mode;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	switch (depth) {
15762306a36Sopenharmony_ci	case 8:
15862306a36Sopenharmony_ci		size = ARRAY_SIZE(bios8);
15962306a36Sopenharmony_ci		mode = bios8;
16062306a36Sopenharmony_ci		break;
16162306a36Sopenharmony_ci	case 16:
16262306a36Sopenharmony_ci		size = ARRAY_SIZE(bios16);
16362306a36Sopenharmony_ci		mode = bios16;
16462306a36Sopenharmony_ci		break;
16562306a36Sopenharmony_ci	case 24:
16662306a36Sopenharmony_ci		size = ARRAY_SIZE(bios24);
16762306a36Sopenharmony_ci		mode = bios24;
16862306a36Sopenharmony_ci		break;
16962306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
17062306a36Sopenharmony_ci	case 32:
17162306a36Sopenharmony_ci		size = ARRAY_SIZE(bios32);
17262306a36Sopenharmony_ci		mode = bios32;
17362306a36Sopenharmony_ci		break;
17462306a36Sopenharmony_ci#endif
17562306a36Sopenharmony_ci	default:
17662306a36Sopenharmony_ci		return 0;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	for (i = 0; i < size; i++) {
18062306a36Sopenharmony_ci		if (xres <= mode[i].x_res) {
18162306a36Sopenharmony_ci			xres_s = mode[i].x_res;
18262306a36Sopenharmony_ci			for (; i < size; i++) {
18362306a36Sopenharmony_ci				if (mode[i].x_res != xres_s)
18462306a36Sopenharmony_ci					return mode[i - 1].mode;
18562306a36Sopenharmony_ci				if (yres <= mode[i].y_res)
18662306a36Sopenharmony_ci					return mode[i].mode;
18762306a36Sopenharmony_ci			}
18862306a36Sopenharmony_ci		}
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci	return mode[size - 1].mode;
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci/*
19462306a36Sopenharmony_ci * neoCalcVCLK --
19562306a36Sopenharmony_ci *
19662306a36Sopenharmony_ci * Determine the closest clock frequency to the one requested.
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_ci#define MAX_N 127
19962306a36Sopenharmony_ci#define MAX_D 31
20062306a36Sopenharmony_ci#define MAX_F 1
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void neoCalcVCLK(const struct fb_info *info,
20362306a36Sopenharmony_ci			struct neofb_par *par, long freq)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int n, d, f;
20662306a36Sopenharmony_ci	int n_best = 0, d_best = 0, f_best = 0;
20762306a36Sopenharmony_ci	long f_best_diff = 0x7ffff;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	for (f = 0; f <= MAX_F; f++)
21062306a36Sopenharmony_ci		for (d = 0; d <= MAX_D; d++)
21162306a36Sopenharmony_ci			for (n = 0; n <= MAX_N; n++) {
21262306a36Sopenharmony_ci				long f_out;
21362306a36Sopenharmony_ci				long f_diff;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci				f_out = ((14318 * (n + 1)) / (d + 1)) >> f;
21662306a36Sopenharmony_ci				f_diff = abs(f_out - freq);
21762306a36Sopenharmony_ci				if (f_diff <= f_best_diff) {
21862306a36Sopenharmony_ci					f_best_diff = f_diff;
21962306a36Sopenharmony_ci					n_best = n;
22062306a36Sopenharmony_ci					d_best = d;
22162306a36Sopenharmony_ci					f_best = f;
22262306a36Sopenharmony_ci				}
22362306a36Sopenharmony_ci				if (f_out > freq)
22462306a36Sopenharmony_ci					break;
22562306a36Sopenharmony_ci			}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 ||
22862306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2230 ||
22962306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2360 ||
23062306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2380) {
23162306a36Sopenharmony_ci		/* NOT_DONE:  We are trying the full range of the 2200 clock.
23262306a36Sopenharmony_ci		   We should be able to try n up to 2047 */
23362306a36Sopenharmony_ci		par->VCLK3NumeratorLow = n_best;
23462306a36Sopenharmony_ci		par->VCLK3NumeratorHigh = (f_best << 7);
23562306a36Sopenharmony_ci	} else
23662306a36Sopenharmony_ci		par->VCLK3NumeratorLow = n_best | (f_best << 7);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	par->VCLK3Denominator = d_best;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci#ifdef NEOFB_DEBUG
24162306a36Sopenharmony_ci	printk(KERN_DEBUG "neoVCLK: f:%ld NumLow=%d NumHi=%d Den=%d Df=%ld\n",
24262306a36Sopenharmony_ci	       freq,
24362306a36Sopenharmony_ci	       par->VCLK3NumeratorLow,
24462306a36Sopenharmony_ci	       par->VCLK3NumeratorHigh,
24562306a36Sopenharmony_ci	       par->VCLK3Denominator, f_best_diff);
24662306a36Sopenharmony_ci#endif
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci/*
25062306a36Sopenharmony_ci * vgaHWInit --
25162306a36Sopenharmony_ci *      Handle the initialization, etc. of a screen.
25262306a36Sopenharmony_ci *      Return FALSE on failure.
25362306a36Sopenharmony_ci */
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int vgaHWInit(const struct fb_var_screeninfo *var,
25662306a36Sopenharmony_ci		     struct neofb_par *par)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	int hsync_end = var->xres + var->right_margin + var->hsync_len;
25962306a36Sopenharmony_ci	int htotal = (hsync_end + var->left_margin) >> 3;
26062306a36Sopenharmony_ci	int vsync_start = var->yres + var->lower_margin;
26162306a36Sopenharmony_ci	int vsync_end = vsync_start + var->vsync_len;
26262306a36Sopenharmony_ci	int vtotal = vsync_end + var->upper_margin;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	par->MiscOutReg = 0x23;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
26762306a36Sopenharmony_ci		par->MiscOutReg |= 0x40;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
27062306a36Sopenharmony_ci		par->MiscOutReg |= 0x80;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/*
27362306a36Sopenharmony_ci	 * Time Sequencer
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	par->Sequencer[0] = 0x00;
27662306a36Sopenharmony_ci	par->Sequencer[1] = 0x01;
27762306a36Sopenharmony_ci	par->Sequencer[2] = 0x0F;
27862306a36Sopenharmony_ci	par->Sequencer[3] = 0x00;	/* Font select */
27962306a36Sopenharmony_ci	par->Sequencer[4] = 0x0E;	/* Misc */
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/*
28262306a36Sopenharmony_ci	 * CRTC Controller
28362306a36Sopenharmony_ci	 */
28462306a36Sopenharmony_ci	par->CRTC[0] = htotal - 5;
28562306a36Sopenharmony_ci	par->CRTC[1] = (var->xres >> 3) - 1;
28662306a36Sopenharmony_ci	par->CRTC[2] = (var->xres >> 3) - 1;
28762306a36Sopenharmony_ci	par->CRTC[3] = ((htotal - 1) & 0x1F) | 0x80;
28862306a36Sopenharmony_ci	par->CRTC[4] = ((var->xres + var->right_margin) >> 3);
28962306a36Sopenharmony_ci	par->CRTC[5] = (((htotal - 1) & 0x20) << 2)
29062306a36Sopenharmony_ci	    | (((hsync_end >> 3)) & 0x1F);
29162306a36Sopenharmony_ci	par->CRTC[6] = (vtotal - 2) & 0xFF;
29262306a36Sopenharmony_ci	par->CRTC[7] = (((vtotal - 2) & 0x100) >> 8)
29362306a36Sopenharmony_ci	    | (((var->yres - 1) & 0x100) >> 7)
29462306a36Sopenharmony_ci	    | ((vsync_start & 0x100) >> 6)
29562306a36Sopenharmony_ci	    | (((var->yres - 1) & 0x100) >> 5)
29662306a36Sopenharmony_ci	    | 0x10 | (((vtotal - 2) & 0x200) >> 4)
29762306a36Sopenharmony_ci	    | (((var->yres - 1) & 0x200) >> 3)
29862306a36Sopenharmony_ci	    | ((vsync_start & 0x200) >> 2);
29962306a36Sopenharmony_ci	par->CRTC[8] = 0x00;
30062306a36Sopenharmony_ci	par->CRTC[9] = (((var->yres - 1) & 0x200) >> 4) | 0x40;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (var->vmode & FB_VMODE_DOUBLE)
30362306a36Sopenharmony_ci		par->CRTC[9] |= 0x80;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	par->CRTC[10] = 0x00;
30662306a36Sopenharmony_ci	par->CRTC[11] = 0x00;
30762306a36Sopenharmony_ci	par->CRTC[12] = 0x00;
30862306a36Sopenharmony_ci	par->CRTC[13] = 0x00;
30962306a36Sopenharmony_ci	par->CRTC[14] = 0x00;
31062306a36Sopenharmony_ci	par->CRTC[15] = 0x00;
31162306a36Sopenharmony_ci	par->CRTC[16] = vsync_start & 0xFF;
31262306a36Sopenharmony_ci	par->CRTC[17] = (vsync_end & 0x0F) | 0x20;
31362306a36Sopenharmony_ci	par->CRTC[18] = (var->yres - 1) & 0xFF;
31462306a36Sopenharmony_ci	par->CRTC[19] = var->xres_virtual >> 4;
31562306a36Sopenharmony_ci	par->CRTC[20] = 0x00;
31662306a36Sopenharmony_ci	par->CRTC[21] = (var->yres - 1) & 0xFF;
31762306a36Sopenharmony_ci	par->CRTC[22] = (vtotal - 1) & 0xFF;
31862306a36Sopenharmony_ci	par->CRTC[23] = 0xC3;
31962306a36Sopenharmony_ci	par->CRTC[24] = 0xFF;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/*
32262306a36Sopenharmony_ci	 * are these unnecessary?
32362306a36Sopenharmony_ci	 * vgaHWHBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN | KGA_ENABLE_ON_ZERO);
32462306a36Sopenharmony_ci	 * vgaHWVBlankKGA(mode, regp, 0, KGA_FIX_OVERSCAN | KGA_ENABLE_ON_ZERO);
32562306a36Sopenharmony_ci	 */
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/*
32862306a36Sopenharmony_ci	 * Graphics Display Controller
32962306a36Sopenharmony_ci	 */
33062306a36Sopenharmony_ci	par->Graphics[0] = 0x00;
33162306a36Sopenharmony_ci	par->Graphics[1] = 0x00;
33262306a36Sopenharmony_ci	par->Graphics[2] = 0x00;
33362306a36Sopenharmony_ci	par->Graphics[3] = 0x00;
33462306a36Sopenharmony_ci	par->Graphics[4] = 0x00;
33562306a36Sopenharmony_ci	par->Graphics[5] = 0x40;
33662306a36Sopenharmony_ci	par->Graphics[6] = 0x05;	/* only map 64k VGA memory !!!! */
33762306a36Sopenharmony_ci	par->Graphics[7] = 0x0F;
33862306a36Sopenharmony_ci	par->Graphics[8] = 0xFF;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	par->Attribute[0] = 0x00;	/* standard colormap translation */
34262306a36Sopenharmony_ci	par->Attribute[1] = 0x01;
34362306a36Sopenharmony_ci	par->Attribute[2] = 0x02;
34462306a36Sopenharmony_ci	par->Attribute[3] = 0x03;
34562306a36Sopenharmony_ci	par->Attribute[4] = 0x04;
34662306a36Sopenharmony_ci	par->Attribute[5] = 0x05;
34762306a36Sopenharmony_ci	par->Attribute[6] = 0x06;
34862306a36Sopenharmony_ci	par->Attribute[7] = 0x07;
34962306a36Sopenharmony_ci	par->Attribute[8] = 0x08;
35062306a36Sopenharmony_ci	par->Attribute[9] = 0x09;
35162306a36Sopenharmony_ci	par->Attribute[10] = 0x0A;
35262306a36Sopenharmony_ci	par->Attribute[11] = 0x0B;
35362306a36Sopenharmony_ci	par->Attribute[12] = 0x0C;
35462306a36Sopenharmony_ci	par->Attribute[13] = 0x0D;
35562306a36Sopenharmony_ci	par->Attribute[14] = 0x0E;
35662306a36Sopenharmony_ci	par->Attribute[15] = 0x0F;
35762306a36Sopenharmony_ci	par->Attribute[16] = 0x41;
35862306a36Sopenharmony_ci	par->Attribute[17] = 0xFF;
35962306a36Sopenharmony_ci	par->Attribute[18] = 0x0F;
36062306a36Sopenharmony_ci	par->Attribute[19] = 0x00;
36162306a36Sopenharmony_ci	par->Attribute[20] = 0x00;
36262306a36Sopenharmony_ci	return 0;
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void vgaHWLock(struct vgastate *state)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	/* Protect CRTC[0-7] */
36862306a36Sopenharmony_ci	vga_wcrt(state->vgabase, 0x11, vga_rcrt(state->vgabase, 0x11) | 0x80);
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void vgaHWUnlock(void)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	/* Unprotect CRTC[0-7] */
37462306a36Sopenharmony_ci	vga_wcrt(NULL, 0x11, vga_rcrt(NULL, 0x11) & ~0x80);
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void neoLock(struct vgastate *state)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	vga_wgfx(state->vgabase, 0x09, 0x00);
38062306a36Sopenharmony_ci	vgaHWLock(state);
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic void neoUnlock(void)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	vgaHWUnlock();
38662306a36Sopenharmony_ci	vga_wgfx(NULL, 0x09, 0x26);
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci/*
39062306a36Sopenharmony_ci * VGA Palette management
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_cistatic int paletteEnabled = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cistatic inline void VGAenablePalette(void)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	vga_r(NULL, VGA_IS1_RC);
39762306a36Sopenharmony_ci	vga_w(NULL, VGA_ATT_W, 0x00);
39862306a36Sopenharmony_ci	paletteEnabled = 1;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic inline void VGAdisablePalette(void)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	vga_r(NULL, VGA_IS1_RC);
40462306a36Sopenharmony_ci	vga_w(NULL, VGA_ATT_W, 0x20);
40562306a36Sopenharmony_ci	paletteEnabled = 0;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic inline void VGAwATTR(u8 index, u8 value)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	if (paletteEnabled)
41162306a36Sopenharmony_ci		index &= ~0x20;
41262306a36Sopenharmony_ci	else
41362306a36Sopenharmony_ci		index |= 0x20;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	vga_r(NULL, VGA_IS1_RC);
41662306a36Sopenharmony_ci	vga_wattr(NULL, index, value);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_cistatic void vgaHWProtect(int on)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	unsigned char tmp;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	tmp = vga_rseq(NULL, 0x01);
42462306a36Sopenharmony_ci	if (on) {
42562306a36Sopenharmony_ci		/*
42662306a36Sopenharmony_ci		 * Turn off screen and disable sequencer.
42762306a36Sopenharmony_ci		 */
42862306a36Sopenharmony_ci		vga_wseq(NULL, 0x00, 0x01);		/* Synchronous Reset */
42962306a36Sopenharmony_ci		vga_wseq(NULL, 0x01, tmp | 0x20);	/* disable the display */
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci		VGAenablePalette();
43262306a36Sopenharmony_ci	} else {
43362306a36Sopenharmony_ci		/*
43462306a36Sopenharmony_ci		 * Reenable sequencer, then turn on screen.
43562306a36Sopenharmony_ci		 */
43662306a36Sopenharmony_ci		vga_wseq(NULL, 0x01, tmp & ~0x20);	/* reenable display */
43762306a36Sopenharmony_ci		vga_wseq(NULL, 0x00, 0x03);		/* clear synchronousreset */
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		VGAdisablePalette();
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic void vgaHWRestore(const struct fb_info *info,
44462306a36Sopenharmony_ci			 const struct neofb_par *par)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int i;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	vga_w(NULL, VGA_MIS_W, par->MiscOutReg);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (i = 1; i < 5; i++)
45162306a36Sopenharmony_ci		vga_wseq(NULL, i, par->Sequencer[i]);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or CRTC[17] */
45462306a36Sopenharmony_ci	vga_wcrt(NULL, 17, par->CRTC[17] & ~0x80);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	for (i = 0; i < 25; i++)
45762306a36Sopenharmony_ci		vga_wcrt(NULL, i, par->CRTC[i]);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	for (i = 0; i < 9; i++)
46062306a36Sopenharmony_ci		vga_wgfx(NULL, i, par->Graphics[i]);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	VGAenablePalette();
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	for (i = 0; i < 21; i++)
46562306a36Sopenharmony_ci		VGAwATTR(i, par->Attribute[i]);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	VGAdisablePalette();
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci/* -------------------- Hardware specific routines ------------------------- */
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/*
47462306a36Sopenharmony_ci * Hardware Acceleration for Neo2200+
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_cistatic inline int neo2200_sync(struct fb_info *info)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	struct neofb_par *par = info->par;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	while (readl(&par->neo2200->bltStat) & 1)
48162306a36Sopenharmony_ci		cpu_relax();
48262306a36Sopenharmony_ci	return 0;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic inline void neo2200_wait_fifo(struct fb_info *info,
48662306a36Sopenharmony_ci				     int requested_fifo_space)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	//  ndev->neo.waitfifo_calls++;
48962306a36Sopenharmony_ci	//  ndev->neo.waitfifo_sum += requested_fifo_space;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	/* FIXME: does not work
49262306a36Sopenharmony_ci	   if (neo_fifo_space < requested_fifo_space)
49362306a36Sopenharmony_ci	   {
49462306a36Sopenharmony_ci	   neo_fifo_waitcycles++;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	   while (1)
49762306a36Sopenharmony_ci	   {
49862306a36Sopenharmony_ci	   neo_fifo_space = (neo2200->bltStat >> 8);
49962306a36Sopenharmony_ci	   if (neo_fifo_space >= requested_fifo_space)
50062306a36Sopenharmony_ci	   break;
50162306a36Sopenharmony_ci	   }
50262306a36Sopenharmony_ci	   }
50362306a36Sopenharmony_ci	   else
50462306a36Sopenharmony_ci	   {
50562306a36Sopenharmony_ci	   neo_fifo_cache_hits++;
50662306a36Sopenharmony_ci	   }
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	   neo_fifo_space -= requested_fifo_space;
50962306a36Sopenharmony_ci	 */
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	neo2200_sync(info);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic inline void neo2200_accel_init(struct fb_info *info,
51562306a36Sopenharmony_ci				      struct fb_var_screeninfo *var)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct neofb_par *par = info->par;
51862306a36Sopenharmony_ci	Neo2200 __iomem *neo2200 = par->neo2200;
51962306a36Sopenharmony_ci	u32 bltMod, pitch;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	neo2200_sync(info);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
52462306a36Sopenharmony_ci	case 8:
52562306a36Sopenharmony_ci		bltMod = NEO_MODE1_DEPTH8;
52662306a36Sopenharmony_ci		pitch = var->xres_virtual;
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci	case 15:
52962306a36Sopenharmony_ci	case 16:
53062306a36Sopenharmony_ci		bltMod = NEO_MODE1_DEPTH16;
53162306a36Sopenharmony_ci		pitch = var->xres_virtual * 2;
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case 24:
53462306a36Sopenharmony_ci		bltMod = NEO_MODE1_DEPTH24;
53562306a36Sopenharmony_ci		pitch = var->xres_virtual * 3;
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci	default:
53862306a36Sopenharmony_ci		printk(KERN_ERR
53962306a36Sopenharmony_ci		       "neofb: neo2200_accel_init: unexpected bits per pixel!\n");
54062306a36Sopenharmony_ci		return;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	writel(bltMod << 16, &neo2200->bltStat);
54462306a36Sopenharmony_ci	writel((pitch << 16) | pitch, &neo2200->pitch);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic int
55062306a36Sopenharmony_cineofb_open(struct fb_info *info, int user)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct neofb_par *par = info->par;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (!par->ref_count) {
55562306a36Sopenharmony_ci		memset(&par->state, 0, sizeof(struct vgastate));
55662306a36Sopenharmony_ci		par->state.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS;
55762306a36Sopenharmony_ci		save_vga(&par->state);
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci	par->ref_count++;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	return 0;
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int
56562306a36Sopenharmony_cineofb_release(struct fb_info *info, int user)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct neofb_par *par = info->par;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!par->ref_count)
57062306a36Sopenharmony_ci		return -EINVAL;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	if (par->ref_count == 1) {
57362306a36Sopenharmony_ci		restore_vga(&par->state);
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	par->ref_count--;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	return 0;
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic int
58162306a36Sopenharmony_cineofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct neofb_par *par = info->par;
58462306a36Sopenharmony_ci	int memlen, vramlen;
58562306a36Sopenharmony_ci	int mode_ok = 0;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	DBG("neofb_check_var");
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	if (!var->pixclock || PICOS2KHZ(var->pixclock) > par->maxClock)
59062306a36Sopenharmony_ci		return -EINVAL;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* Is the mode larger than the LCD panel? */
59362306a36Sopenharmony_ci	if (par->internal_display &&
59462306a36Sopenharmony_ci            ((var->xres > par->NeoPanelWidth) ||
59562306a36Sopenharmony_ci	     (var->yres > par->NeoPanelHeight))) {
59662306a36Sopenharmony_ci		printk(KERN_INFO
59762306a36Sopenharmony_ci		       "Mode (%dx%d) larger than the LCD panel (%dx%d)\n",
59862306a36Sopenharmony_ci		       var->xres, var->yres, par->NeoPanelWidth,
59962306a36Sopenharmony_ci		       par->NeoPanelHeight);
60062306a36Sopenharmony_ci		return -EINVAL;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Is the mode one of the acceptable sizes? */
60462306a36Sopenharmony_ci	if (!par->internal_display)
60562306a36Sopenharmony_ci		mode_ok = 1;
60662306a36Sopenharmony_ci	else {
60762306a36Sopenharmony_ci		switch (var->xres) {
60862306a36Sopenharmony_ci		case 1280:
60962306a36Sopenharmony_ci			if (var->yres == 1024)
61062306a36Sopenharmony_ci				mode_ok = 1;
61162306a36Sopenharmony_ci			break;
61262306a36Sopenharmony_ci		case 1024:
61362306a36Sopenharmony_ci			if (var->yres == 768)
61462306a36Sopenharmony_ci				mode_ok = 1;
61562306a36Sopenharmony_ci			break;
61662306a36Sopenharmony_ci		case 800:
61762306a36Sopenharmony_ci			if (var->yres == (par->libretto ? 480 : 600))
61862306a36Sopenharmony_ci				mode_ok = 1;
61962306a36Sopenharmony_ci			break;
62062306a36Sopenharmony_ci		case 640:
62162306a36Sopenharmony_ci			if (var->yres == 480)
62262306a36Sopenharmony_ci				mode_ok = 1;
62362306a36Sopenharmony_ci			break;
62462306a36Sopenharmony_ci		}
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (!mode_ok) {
62862306a36Sopenharmony_ci		printk(KERN_INFO
62962306a36Sopenharmony_ci		       "Mode (%dx%d) won't display properly on LCD\n",
63062306a36Sopenharmony_ci		       var->xres, var->yres);
63162306a36Sopenharmony_ci		return -EINVAL;
63262306a36Sopenharmony_ci	}
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	var->red.msb_right = 0;
63562306a36Sopenharmony_ci	var->green.msb_right = 0;
63662306a36Sopenharmony_ci	var->blue.msb_right = 0;
63762306a36Sopenharmony_ci	var->transp.msb_right = 0;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	var->transp.offset = 0;
64062306a36Sopenharmony_ci	var->transp.length = 0;
64162306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
64262306a36Sopenharmony_ci	case 8:		/* PSEUDOCOLOUR, 256 */
64362306a36Sopenharmony_ci		var->red.offset = 0;
64462306a36Sopenharmony_ci		var->red.length = 8;
64562306a36Sopenharmony_ci		var->green.offset = 0;
64662306a36Sopenharmony_ci		var->green.length = 8;
64762306a36Sopenharmony_ci		var->blue.offset = 0;
64862306a36Sopenharmony_ci		var->blue.length = 8;
64962306a36Sopenharmony_ci		break;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	case 16:		/* DIRECTCOLOUR, 64k */
65262306a36Sopenharmony_ci		var->red.offset = 11;
65362306a36Sopenharmony_ci		var->red.length = 5;
65462306a36Sopenharmony_ci		var->green.offset = 5;
65562306a36Sopenharmony_ci		var->green.length = 6;
65662306a36Sopenharmony_ci		var->blue.offset = 0;
65762306a36Sopenharmony_ci		var->blue.length = 5;
65862306a36Sopenharmony_ci		break;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	case 24:		/* TRUECOLOUR, 16m */
66162306a36Sopenharmony_ci		var->red.offset = 16;
66262306a36Sopenharmony_ci		var->red.length = 8;
66362306a36Sopenharmony_ci		var->green.offset = 8;
66462306a36Sopenharmony_ci		var->green.length = 8;
66562306a36Sopenharmony_ci		var->blue.offset = 0;
66662306a36Sopenharmony_ci		var->blue.length = 8;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
67062306a36Sopenharmony_ci	case 32:		/* TRUECOLOUR, 16m */
67162306a36Sopenharmony_ci		var->transp.offset = 24;
67262306a36Sopenharmony_ci		var->transp.length = 8;
67362306a36Sopenharmony_ci		var->red.offset = 16;
67462306a36Sopenharmony_ci		var->red.length = 8;
67562306a36Sopenharmony_ci		var->green.offset = 8;
67662306a36Sopenharmony_ci		var->green.length = 8;
67762306a36Sopenharmony_ci		var->blue.offset = 0;
67862306a36Sopenharmony_ci		var->blue.length = 8;
67962306a36Sopenharmony_ci		break;
68062306a36Sopenharmony_ci#endif
68162306a36Sopenharmony_ci	default:
68262306a36Sopenharmony_ci		printk(KERN_WARNING "neofb: no support for %dbpp\n",
68362306a36Sopenharmony_ci		       var->bits_per_pixel);
68462306a36Sopenharmony_ci		return -EINVAL;
68562306a36Sopenharmony_ci	}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	vramlen = info->fix.smem_len;
68862306a36Sopenharmony_ci	if (vramlen > 4 * 1024 * 1024)
68962306a36Sopenharmony_ci		vramlen = 4 * 1024 * 1024;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (var->xres_virtual < var->xres)
69262306a36Sopenharmony_ci		var->xres_virtual = var->xres;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	memlen = var->xres_virtual * var->bits_per_pixel * var->yres_virtual >> 3;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (memlen > vramlen) {
69762306a36Sopenharmony_ci		var->yres_virtual =  vramlen * 8 / (var->xres_virtual *
69862306a36Sopenharmony_ci				   	var->bits_per_pixel);
69962306a36Sopenharmony_ci		memlen = var->xres_virtual * var->bits_per_pixel *
70062306a36Sopenharmony_ci				var->yres_virtual / 8;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* we must round yres/xres down, we already rounded y/xres_virtual up
70462306a36Sopenharmony_ci	   if it was possible. We should return -EINVAL, but I disagree */
70562306a36Sopenharmony_ci	if (var->yres_virtual < var->yres)
70662306a36Sopenharmony_ci		var->yres = var->yres_virtual;
70762306a36Sopenharmony_ci	if (var->xoffset + var->xres > var->xres_virtual)
70862306a36Sopenharmony_ci		var->xoffset = var->xres_virtual - var->xres;
70962306a36Sopenharmony_ci	if (var->yoffset + var->yres > var->yres_virtual)
71062306a36Sopenharmony_ci		var->yoffset = var->yres_virtual - var->yres;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	var->nonstd = 0;
71362306a36Sopenharmony_ci	var->height = -1;
71462306a36Sopenharmony_ci	var->width = -1;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (var->bits_per_pixel >= 24 || !par->neo2200)
71762306a36Sopenharmony_ci		var->accel_flags &= ~FB_ACCELF_TEXT;
71862306a36Sopenharmony_ci	return 0;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int neofb_set_par(struct fb_info *info)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct neofb_par *par = info->par;
72462306a36Sopenharmony_ci	unsigned char temp;
72562306a36Sopenharmony_ci	int i, clock_hi = 0;
72662306a36Sopenharmony_ci	int lcd_stretch;
72762306a36Sopenharmony_ci	int hoffset, voffset;
72862306a36Sopenharmony_ci	int vsync_start, vtotal;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	DBG("neofb_set_par");
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	neoUnlock();
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	vgaHWProtect(1);	/* Blank the screen */
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	vsync_start = info->var.yres + info->var.lower_margin;
73762306a36Sopenharmony_ci	vtotal = vsync_start + info->var.vsync_len + info->var.upper_margin;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	/*
74062306a36Sopenharmony_ci	 * This will allocate the datastructure and initialize all of the
74162306a36Sopenharmony_ci	 * generic VGA registers.
74262306a36Sopenharmony_ci	 */
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (vgaHWInit(&info->var, par))
74562306a36Sopenharmony_ci		return -EINVAL;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	/*
74862306a36Sopenharmony_ci	 * The default value assigned by vgaHW.c is 0x41, but this does
74962306a36Sopenharmony_ci	 * not work for NeoMagic.
75062306a36Sopenharmony_ci	 */
75162306a36Sopenharmony_ci	par->Attribute[16] = 0x01;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
75462306a36Sopenharmony_ci	case 8:
75562306a36Sopenharmony_ci		par->CRTC[0x13] = info->var.xres_virtual >> 3;
75662306a36Sopenharmony_ci		par->ExtCRTOffset = info->var.xres_virtual >> 11;
75762306a36Sopenharmony_ci		par->ExtColorModeSelect = 0x11;
75862306a36Sopenharmony_ci		break;
75962306a36Sopenharmony_ci	case 16:
76062306a36Sopenharmony_ci		par->CRTC[0x13] = info->var.xres_virtual >> 2;
76162306a36Sopenharmony_ci		par->ExtCRTOffset = info->var.xres_virtual >> 10;
76262306a36Sopenharmony_ci		par->ExtColorModeSelect = 0x13;
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci	case 24:
76562306a36Sopenharmony_ci		par->CRTC[0x13] = (info->var.xres_virtual * 3) >> 3;
76662306a36Sopenharmony_ci		par->ExtCRTOffset = (info->var.xres_virtual * 3) >> 11;
76762306a36Sopenharmony_ci		par->ExtColorModeSelect = 0x14;
76862306a36Sopenharmony_ci		break;
76962306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
77062306a36Sopenharmony_ci	case 32:		/* FIXME: guessed values */
77162306a36Sopenharmony_ci		par->CRTC[0x13] = info->var.xres_virtual >> 1;
77262306a36Sopenharmony_ci		par->ExtCRTOffset = info->var.xres_virtual >> 9;
77362306a36Sopenharmony_ci		par->ExtColorModeSelect = 0x15;
77462306a36Sopenharmony_ci		break;
77562306a36Sopenharmony_ci#endif
77662306a36Sopenharmony_ci	default:
77762306a36Sopenharmony_ci		break;
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	par->ExtCRTDispAddr = 0x10;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* Vertical Extension */
78362306a36Sopenharmony_ci	par->VerticalExt = (((vtotal - 2) & 0x400) >> 10)
78462306a36Sopenharmony_ci	    | (((info->var.yres - 1) & 0x400) >> 9)
78562306a36Sopenharmony_ci	    | (((vsync_start) & 0x400) >> 8)
78662306a36Sopenharmony_ci	    | (((vsync_start) & 0x400) >> 7);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	/* Fast write bursts on unless disabled. */
78962306a36Sopenharmony_ci	if (par->pci_burst)
79062306a36Sopenharmony_ci		par->SysIfaceCntl1 = 0x30;
79162306a36Sopenharmony_ci	else
79262306a36Sopenharmony_ci		par->SysIfaceCntl1 = 0x00;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	par->SysIfaceCntl2 = 0xc0;	/* VESA Bios sets this to 0x80! */
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	/* Initialize: by default, we want display config register to be read */
79762306a36Sopenharmony_ci	par->PanelDispCntlRegRead = 1;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Enable any user specified display devices. */
80062306a36Sopenharmony_ci	par->PanelDispCntlReg1 = 0x00;
80162306a36Sopenharmony_ci	if (par->internal_display)
80262306a36Sopenharmony_ci		par->PanelDispCntlReg1 |= 0x02;
80362306a36Sopenharmony_ci	if (par->external_display)
80462306a36Sopenharmony_ci		par->PanelDispCntlReg1 |= 0x01;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* If the user did not specify any display devices, then... */
80762306a36Sopenharmony_ci	if (par->PanelDispCntlReg1 == 0x00) {
80862306a36Sopenharmony_ci		/* Default to internal (i.e., LCD) only. */
80962306a36Sopenharmony_ci		par->PanelDispCntlReg1 = vga_rgfx(NULL, 0x20) & 0x03;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* If we are using a fixed mode, then tell the chip we are. */
81362306a36Sopenharmony_ci	switch (info->var.xres) {
81462306a36Sopenharmony_ci	case 1280:
81562306a36Sopenharmony_ci		par->PanelDispCntlReg1 |= 0x60;
81662306a36Sopenharmony_ci		break;
81762306a36Sopenharmony_ci	case 1024:
81862306a36Sopenharmony_ci		par->PanelDispCntlReg1 |= 0x40;
81962306a36Sopenharmony_ci		break;
82062306a36Sopenharmony_ci	case 800:
82162306a36Sopenharmony_ci		par->PanelDispCntlReg1 |= 0x20;
82262306a36Sopenharmony_ci		break;
82362306a36Sopenharmony_ci	case 640:
82462306a36Sopenharmony_ci	default:
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* Setup shadow register locking. */
82962306a36Sopenharmony_ci	switch (par->PanelDispCntlReg1 & 0x03) {
83062306a36Sopenharmony_ci	case 0x01:		/* External CRT only mode: */
83162306a36Sopenharmony_ci		par->GeneralLockReg = 0x00;
83262306a36Sopenharmony_ci		/* We need to program the VCLK for external display only mode. */
83362306a36Sopenharmony_ci		par->ProgramVCLK = 1;
83462306a36Sopenharmony_ci		break;
83562306a36Sopenharmony_ci	case 0x02:		/* Internal LCD only mode: */
83662306a36Sopenharmony_ci	case 0x03:		/* Simultaneous internal/external (LCD/CRT) mode: */
83762306a36Sopenharmony_ci		par->GeneralLockReg = 0x01;
83862306a36Sopenharmony_ci		/* Don't program the VCLK when using the LCD. */
83962306a36Sopenharmony_ci		par->ProgramVCLK = 0;
84062306a36Sopenharmony_ci		break;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * If the screen is to be stretched, turn on stretching for the
84562306a36Sopenharmony_ci	 * various modes.
84662306a36Sopenharmony_ci	 *
84762306a36Sopenharmony_ci	 * OPTION_LCD_STRETCH means stretching should be turned off!
84862306a36Sopenharmony_ci	 */
84962306a36Sopenharmony_ci	par->PanelDispCntlReg2 = 0x00;
85062306a36Sopenharmony_ci	par->PanelDispCntlReg3 = 0x00;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	if (par->lcd_stretch && (par->PanelDispCntlReg1 == 0x02) &&	/* LCD only */
85362306a36Sopenharmony_ci	    (info->var.xres != par->NeoPanelWidth)) {
85462306a36Sopenharmony_ci		switch (info->var.xres) {
85562306a36Sopenharmony_ci		case 320:	/* Needs testing.  KEM -- 24 May 98 */
85662306a36Sopenharmony_ci		case 400:	/* Needs testing.  KEM -- 24 May 98 */
85762306a36Sopenharmony_ci		case 640:
85862306a36Sopenharmony_ci		case 800:
85962306a36Sopenharmony_ci		case 1024:
86062306a36Sopenharmony_ci			lcd_stretch = 1;
86162306a36Sopenharmony_ci			par->PanelDispCntlReg2 |= 0xC6;
86262306a36Sopenharmony_ci			break;
86362306a36Sopenharmony_ci		default:
86462306a36Sopenharmony_ci			lcd_stretch = 0;
86562306a36Sopenharmony_ci			/* No stretching in these modes. */
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci	} else
86862306a36Sopenharmony_ci		lcd_stretch = 0;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/*
87162306a36Sopenharmony_ci	 * If the screen is to be centerd, turn on the centering for the
87262306a36Sopenharmony_ci	 * various modes.
87362306a36Sopenharmony_ci	 */
87462306a36Sopenharmony_ci	par->PanelVertCenterReg1 = 0x00;
87562306a36Sopenharmony_ci	par->PanelVertCenterReg2 = 0x00;
87662306a36Sopenharmony_ci	par->PanelVertCenterReg3 = 0x00;
87762306a36Sopenharmony_ci	par->PanelVertCenterReg4 = 0x00;
87862306a36Sopenharmony_ci	par->PanelVertCenterReg5 = 0x00;
87962306a36Sopenharmony_ci	par->PanelHorizCenterReg1 = 0x00;
88062306a36Sopenharmony_ci	par->PanelHorizCenterReg2 = 0x00;
88162306a36Sopenharmony_ci	par->PanelHorizCenterReg3 = 0x00;
88262306a36Sopenharmony_ci	par->PanelHorizCenterReg4 = 0x00;
88362306a36Sopenharmony_ci	par->PanelHorizCenterReg5 = 0x00;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (par->PanelDispCntlReg1 & 0x02) {
88762306a36Sopenharmony_ci		if (info->var.xres == par->NeoPanelWidth) {
88862306a36Sopenharmony_ci			/*
88962306a36Sopenharmony_ci			 * No centering required when the requested display width
89062306a36Sopenharmony_ci			 * equals the panel width.
89162306a36Sopenharmony_ci			 */
89262306a36Sopenharmony_ci		} else {
89362306a36Sopenharmony_ci			par->PanelDispCntlReg2 |= 0x01;
89462306a36Sopenharmony_ci			par->PanelDispCntlReg3 |= 0x10;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci			/* Calculate the horizontal and vertical offsets. */
89762306a36Sopenharmony_ci			if (!lcd_stretch) {
89862306a36Sopenharmony_ci				hoffset =
89962306a36Sopenharmony_ci				    ((par->NeoPanelWidth -
90062306a36Sopenharmony_ci				      info->var.xres) >> 4) - 1;
90162306a36Sopenharmony_ci				voffset =
90262306a36Sopenharmony_ci				    ((par->NeoPanelHeight -
90362306a36Sopenharmony_ci				      info->var.yres) >> 1) - 2;
90462306a36Sopenharmony_ci			} else {
90562306a36Sopenharmony_ci				/* Stretched modes cannot be centered. */
90662306a36Sopenharmony_ci				hoffset = 0;
90762306a36Sopenharmony_ci				voffset = 0;
90862306a36Sopenharmony_ci			}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci			switch (info->var.xres) {
91162306a36Sopenharmony_ci			case 320:	/* Needs testing.  KEM -- 24 May 98 */
91262306a36Sopenharmony_ci				par->PanelHorizCenterReg3 = hoffset;
91362306a36Sopenharmony_ci				par->PanelVertCenterReg2 = voffset;
91462306a36Sopenharmony_ci				break;
91562306a36Sopenharmony_ci			case 400:	/* Needs testing.  KEM -- 24 May 98 */
91662306a36Sopenharmony_ci				par->PanelHorizCenterReg4 = hoffset;
91762306a36Sopenharmony_ci				par->PanelVertCenterReg1 = voffset;
91862306a36Sopenharmony_ci				break;
91962306a36Sopenharmony_ci			case 640:
92062306a36Sopenharmony_ci				par->PanelHorizCenterReg1 = hoffset;
92162306a36Sopenharmony_ci				par->PanelVertCenterReg3 = voffset;
92262306a36Sopenharmony_ci				break;
92362306a36Sopenharmony_ci			case 800:
92462306a36Sopenharmony_ci				par->PanelHorizCenterReg2 = hoffset;
92562306a36Sopenharmony_ci				par->PanelVertCenterReg4 = voffset;
92662306a36Sopenharmony_ci				break;
92762306a36Sopenharmony_ci			case 1024:
92862306a36Sopenharmony_ci				par->PanelHorizCenterReg5 = hoffset;
92962306a36Sopenharmony_ci				par->PanelVertCenterReg5 = voffset;
93062306a36Sopenharmony_ci				break;
93162306a36Sopenharmony_ci			case 1280:
93262306a36Sopenharmony_ci			default:
93362306a36Sopenharmony_ci				/* No centering in these modes. */
93462306a36Sopenharmony_ci				break;
93562306a36Sopenharmony_ci			}
93662306a36Sopenharmony_ci		}
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	par->biosMode =
94062306a36Sopenharmony_ci	    neoFindMode(info->var.xres, info->var.yres,
94162306a36Sopenharmony_ci			info->var.bits_per_pixel);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	/*
94462306a36Sopenharmony_ci	 * Calculate the VCLK that most closely matches the requested dot
94562306a36Sopenharmony_ci	 * clock.
94662306a36Sopenharmony_ci	 */
94762306a36Sopenharmony_ci	neoCalcVCLK(info, par, PICOS2KHZ(info->var.pixclock));
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	/* Since we program the clocks ourselves, always use VCLK3. */
95062306a36Sopenharmony_ci	par->MiscOutReg |= 0x0C;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	/* alread unlocked above */
95362306a36Sopenharmony_ci	/* BOGUS  vga_wgfx(NULL, 0x09, 0x26); */
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	/* don't know what this is, but it's 0 from bootup anyway */
95662306a36Sopenharmony_ci	vga_wgfx(NULL, 0x15, 0x00);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* was set to 0x01 by my bios in text and vesa modes */
95962306a36Sopenharmony_ci	vga_wgfx(NULL, 0x0A, par->GeneralLockReg);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	/*
96262306a36Sopenharmony_ci	 * The color mode needs to be set before calling vgaHWRestore
96362306a36Sopenharmony_ci	 * to ensure the DAC is initialized properly.
96462306a36Sopenharmony_ci	 *
96562306a36Sopenharmony_ci	 * NOTE: Make sure we don't change bits make sure we don't change
96662306a36Sopenharmony_ci	 * any reserved bits.
96762306a36Sopenharmony_ci	 */
96862306a36Sopenharmony_ci	temp = vga_rgfx(NULL, 0x90);
96962306a36Sopenharmony_ci	switch (info->fix.accel) {
97062306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2070:
97162306a36Sopenharmony_ci		temp &= 0xF0;	/* Save bits 7:4 */
97262306a36Sopenharmony_ci		temp |= (par->ExtColorModeSelect & ~0xF0);
97362306a36Sopenharmony_ci		break;
97462306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2090:
97562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2093:
97662306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2097:
97762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2160:
97862306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2200:
97962306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2230:
98062306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2360:
98162306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2380:
98262306a36Sopenharmony_ci		temp &= 0x70;	/* Save bits 6:4 */
98362306a36Sopenharmony_ci		temp |= (par->ExtColorModeSelect & ~0x70);
98462306a36Sopenharmony_ci		break;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	vga_wgfx(NULL, 0x90, temp);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	/*
99062306a36Sopenharmony_ci	 * In some rare cases a lockup might occur if we don't delay
99162306a36Sopenharmony_ci	 * here. (Reported by Miles Lane)
99262306a36Sopenharmony_ci	 */
99362306a36Sopenharmony_ci	//mdelay(200);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/*
99662306a36Sopenharmony_ci	 * Disable horizontal and vertical graphics and text expansions so
99762306a36Sopenharmony_ci	 * that vgaHWRestore works properly.
99862306a36Sopenharmony_ci	 */
99962306a36Sopenharmony_ci	temp = vga_rgfx(NULL, 0x25);
100062306a36Sopenharmony_ci	temp &= 0x39;
100162306a36Sopenharmony_ci	vga_wgfx(NULL, 0x25, temp);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	/*
100462306a36Sopenharmony_ci	 * Sleep for 200ms to make sure that the two operations above have
100562306a36Sopenharmony_ci	 * had time to take effect.
100662306a36Sopenharmony_ci	 */
100762306a36Sopenharmony_ci	mdelay(200);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/*
101062306a36Sopenharmony_ci	 * This function handles restoring the generic VGA registers.  */
101162306a36Sopenharmony_ci	vgaHWRestore(info, par);
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	/* linear colormap for non palettized modes */
101462306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
101562306a36Sopenharmony_ci	case 8:
101662306a36Sopenharmony_ci		/* PseudoColor, 256 */
101762306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
101862306a36Sopenharmony_ci		break;
101962306a36Sopenharmony_ci	case 16:
102062306a36Sopenharmony_ci		/* TrueColor, 64k */
102162306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		for (i = 0; i < 64; i++) {
102462306a36Sopenharmony_ci			outb(i, 0x3c8);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci			outb(i << 1, 0x3c9);
102762306a36Sopenharmony_ci			outb(i, 0x3c9);
102862306a36Sopenharmony_ci			outb(i << 1, 0x3c9);
102962306a36Sopenharmony_ci		}
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	case 24:
103262306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
103362306a36Sopenharmony_ci	case 32:
103462306a36Sopenharmony_ci#endif
103562306a36Sopenharmony_ci		/* TrueColor, 16m */
103662306a36Sopenharmony_ci		info->fix.visual = FB_VISUAL_TRUECOLOR;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		for (i = 0; i < 256; i++) {
103962306a36Sopenharmony_ci			outb(i, 0x3c8);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci			outb(i, 0x3c9);
104262306a36Sopenharmony_ci			outb(i, 0x3c9);
104362306a36Sopenharmony_ci			outb(i, 0x3c9);
104462306a36Sopenharmony_ci		}
104562306a36Sopenharmony_ci		break;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	vga_wgfx(NULL, 0x0E, par->ExtCRTDispAddr);
104962306a36Sopenharmony_ci	vga_wgfx(NULL, 0x0F, par->ExtCRTOffset);
105062306a36Sopenharmony_ci	temp = vga_rgfx(NULL, 0x10);
105162306a36Sopenharmony_ci	temp &= 0x0F;		/* Save bits 3:0 */
105262306a36Sopenharmony_ci	temp |= (par->SysIfaceCntl1 & ~0x0F);	/* VESA Bios sets bit 1! */
105362306a36Sopenharmony_ci	vga_wgfx(NULL, 0x10, temp);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	vga_wgfx(NULL, 0x11, par->SysIfaceCntl2);
105662306a36Sopenharmony_ci	vga_wgfx(NULL, 0x15, 0 /*par->SingleAddrPage */ );
105762306a36Sopenharmony_ci	vga_wgfx(NULL, 0x16, 0 /*par->DualAddrPage */ );
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	temp = vga_rgfx(NULL, 0x20);
106062306a36Sopenharmony_ci	switch (info->fix.accel) {
106162306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2070:
106262306a36Sopenharmony_ci		temp &= 0xFC;	/* Save bits 7:2 */
106362306a36Sopenharmony_ci		temp |= (par->PanelDispCntlReg1 & ~0xFC);
106462306a36Sopenharmony_ci		break;
106562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2090:
106662306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2093:
106762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2097:
106862306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2160:
106962306a36Sopenharmony_ci		temp &= 0xDC;	/* Save bits 7:6,4:2 */
107062306a36Sopenharmony_ci		temp |= (par->PanelDispCntlReg1 & ~0xDC);
107162306a36Sopenharmony_ci		break;
107262306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2200:
107362306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2230:
107462306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2360:
107562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2380:
107662306a36Sopenharmony_ci		temp &= 0x98;	/* Save bits 7,4:3 */
107762306a36Sopenharmony_ci		temp |= (par->PanelDispCntlReg1 & ~0x98);
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci	vga_wgfx(NULL, 0x20, temp);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	temp = vga_rgfx(NULL, 0x25);
108362306a36Sopenharmony_ci	temp &= 0x38;		/* Save bits 5:3 */
108462306a36Sopenharmony_ci	temp |= (par->PanelDispCntlReg2 & ~0x38);
108562306a36Sopenharmony_ci	vga_wgfx(NULL, 0x25, temp);
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	if (info->fix.accel != FB_ACCEL_NEOMAGIC_NM2070) {
108862306a36Sopenharmony_ci		temp = vga_rgfx(NULL, 0x30);
108962306a36Sopenharmony_ci		temp &= 0xEF;	/* Save bits 7:5 and bits 3:0 */
109062306a36Sopenharmony_ci		temp |= (par->PanelDispCntlReg3 & ~0xEF);
109162306a36Sopenharmony_ci		vga_wgfx(NULL, 0x30, temp);
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	vga_wgfx(NULL, 0x28, par->PanelVertCenterReg1);
109562306a36Sopenharmony_ci	vga_wgfx(NULL, 0x29, par->PanelVertCenterReg2);
109662306a36Sopenharmony_ci	vga_wgfx(NULL, 0x2a, par->PanelVertCenterReg3);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (info->fix.accel != FB_ACCEL_NEOMAGIC_NM2070) {
109962306a36Sopenharmony_ci		vga_wgfx(NULL, 0x32, par->PanelVertCenterReg4);
110062306a36Sopenharmony_ci		vga_wgfx(NULL, 0x33, par->PanelHorizCenterReg1);
110162306a36Sopenharmony_ci		vga_wgfx(NULL, 0x34, par->PanelHorizCenterReg2);
110262306a36Sopenharmony_ci		vga_wgfx(NULL, 0x35, par->PanelHorizCenterReg3);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2160)
110662306a36Sopenharmony_ci		vga_wgfx(NULL, 0x36, par->PanelHorizCenterReg4);
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 ||
110962306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2230 ||
111062306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2360 ||
111162306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2380) {
111262306a36Sopenharmony_ci		vga_wgfx(NULL, 0x36, par->PanelHorizCenterReg4);
111362306a36Sopenharmony_ci		vga_wgfx(NULL, 0x37, par->PanelVertCenterReg5);
111462306a36Sopenharmony_ci		vga_wgfx(NULL, 0x38, par->PanelHorizCenterReg5);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		clock_hi = 1;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	/* Program VCLK3 if needed. */
112062306a36Sopenharmony_ci	if (par->ProgramVCLK && ((vga_rgfx(NULL, 0x9B) != par->VCLK3NumeratorLow)
112162306a36Sopenharmony_ci				 || (vga_rgfx(NULL, 0x9F) != par->VCLK3Denominator)
112262306a36Sopenharmony_ci				 || (clock_hi && ((vga_rgfx(NULL, 0x8F) & ~0x0f)
112362306a36Sopenharmony_ci						  != (par->VCLK3NumeratorHigh &
112462306a36Sopenharmony_ci						      ~0x0F))))) {
112562306a36Sopenharmony_ci		vga_wgfx(NULL, 0x9B, par->VCLK3NumeratorLow);
112662306a36Sopenharmony_ci		if (clock_hi) {
112762306a36Sopenharmony_ci			temp = vga_rgfx(NULL, 0x8F);
112862306a36Sopenharmony_ci			temp &= 0x0F;	/* Save bits 3:0 */
112962306a36Sopenharmony_ci			temp |= (par->VCLK3NumeratorHigh & ~0x0F);
113062306a36Sopenharmony_ci			vga_wgfx(NULL, 0x8F, temp);
113162306a36Sopenharmony_ci		}
113262306a36Sopenharmony_ci		vga_wgfx(NULL, 0x9F, par->VCLK3Denominator);
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (par->biosMode)
113662306a36Sopenharmony_ci		vga_wcrt(NULL, 0x23, par->biosMode);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	vga_wgfx(NULL, 0x93, 0xc0);	/* Gives 5x faster framebuffer writes !!! */
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	/* Program vertical extension register */
114162306a36Sopenharmony_ci	if (info->fix.accel == FB_ACCEL_NEOMAGIC_NM2200 ||
114262306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2230 ||
114362306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2360 ||
114462306a36Sopenharmony_ci	    info->fix.accel == FB_ACCEL_NEOMAGIC_NM2380) {
114562306a36Sopenharmony_ci		vga_wcrt(NULL, 0x70, par->VerticalExt);
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	vgaHWProtect(0);	/* Turn on screen */
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* Calling this also locks offset registers required in update_start */
115162306a36Sopenharmony_ci	neoLock(&par->state);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	info->fix.line_length =
115462306a36Sopenharmony_ci	    info->var.xres_virtual * (info->var.bits_per_pixel >> 3);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	switch (info->fix.accel) {
115762306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
115862306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
115962306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
116062306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
116162306a36Sopenharmony_ci			neo2200_accel_init(info, &info->var);
116262306a36Sopenharmony_ci			break;
116362306a36Sopenharmony_ci		default:
116462306a36Sopenharmony_ci			break;
116562306a36Sopenharmony_ci	}
116662306a36Sopenharmony_ci	return 0;
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci/*
117062306a36Sopenharmony_ci *    Pan or Wrap the Display
117162306a36Sopenharmony_ci */
117262306a36Sopenharmony_cistatic int neofb_pan_display(struct fb_var_screeninfo *var,
117362306a36Sopenharmony_ci			     struct fb_info *info)
117462306a36Sopenharmony_ci{
117562306a36Sopenharmony_ci	struct neofb_par *par = info->par;
117662306a36Sopenharmony_ci	struct vgastate *state = &par->state;
117762306a36Sopenharmony_ci	int oldExtCRTDispAddr;
117862306a36Sopenharmony_ci	int Base;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	DBG("neofb_update_start");
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	Base = (var->yoffset * info->var.xres_virtual + var->xoffset) >> 2;
118362306a36Sopenharmony_ci	Base *= (info->var.bits_per_pixel + 7) / 8;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	neoUnlock();
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/*
118862306a36Sopenharmony_ci	 * These are the generic starting address registers.
118962306a36Sopenharmony_ci	 */
119062306a36Sopenharmony_ci	vga_wcrt(state->vgabase, 0x0C, (Base & 0x00FF00) >> 8);
119162306a36Sopenharmony_ci	vga_wcrt(state->vgabase, 0x0D, (Base & 0x00FF));
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/*
119462306a36Sopenharmony_ci	 * Make sure we don't clobber some other bits that might already
119562306a36Sopenharmony_ci	 * have been set. NOTE: NM2200 has a writable bit 3, but it shouldn't
119662306a36Sopenharmony_ci	 * be needed.
119762306a36Sopenharmony_ci	 */
119862306a36Sopenharmony_ci	oldExtCRTDispAddr = vga_rgfx(NULL, 0x0E);
119962306a36Sopenharmony_ci	vga_wgfx(state->vgabase, 0x0E, (((Base >> 16) & 0x0f) | (oldExtCRTDispAddr & 0xf0)));
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	neoLock(state);
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	return 0;
120462306a36Sopenharmony_ci}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_cistatic int neofb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
120762306a36Sopenharmony_ci			   u_int transp, struct fb_info *fb)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	if (regno >= fb->cmap.len || regno > 255)
121062306a36Sopenharmony_ci		return -EINVAL;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (fb->var.bits_per_pixel <= 8) {
121362306a36Sopenharmony_ci		outb(regno, 0x3c8);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci		outb(red >> 10, 0x3c9);
121662306a36Sopenharmony_ci		outb(green >> 10, 0x3c9);
121762306a36Sopenharmony_ci		outb(blue >> 10, 0x3c9);
121862306a36Sopenharmony_ci	} else if (regno < 16) {
121962306a36Sopenharmony_ci		switch (fb->var.bits_per_pixel) {
122062306a36Sopenharmony_ci		case 16:
122162306a36Sopenharmony_ci			((u32 *) fb->pseudo_palette)[regno] =
122262306a36Sopenharmony_ci				((red & 0xf800)) | ((green & 0xfc00) >> 5) |
122362306a36Sopenharmony_ci				((blue & 0xf800) >> 11);
122462306a36Sopenharmony_ci			break;
122562306a36Sopenharmony_ci		case 24:
122662306a36Sopenharmony_ci			((u32 *) fb->pseudo_palette)[regno] =
122762306a36Sopenharmony_ci				((red & 0xff00) << 8) | ((green & 0xff00)) |
122862306a36Sopenharmony_ci				((blue & 0xff00) >> 8);
122962306a36Sopenharmony_ci			break;
123062306a36Sopenharmony_ci#ifdef NO_32BIT_SUPPORT_YET
123162306a36Sopenharmony_ci		case 32:
123262306a36Sopenharmony_ci			((u32 *) fb->pseudo_palette)[regno] =
123362306a36Sopenharmony_ci				((transp & 0xff00) << 16) | ((red & 0xff00) << 8) |
123462306a36Sopenharmony_ci				((green & 0xff00)) | ((blue & 0xff00) >> 8);
123562306a36Sopenharmony_ci			break;
123662306a36Sopenharmony_ci#endif
123762306a36Sopenharmony_ci		default:
123862306a36Sopenharmony_ci			return 1;
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci/*
124662306a36Sopenharmony_ci *    (Un)Blank the display.
124762306a36Sopenharmony_ci */
124862306a36Sopenharmony_cistatic int neofb_blank(int blank_mode, struct fb_info *info)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	/*
125162306a36Sopenharmony_ci	 *  Blank the screen if blank_mode != 0, else unblank.
125262306a36Sopenharmony_ci	 *  Return 0 if blanking succeeded, != 0 if un-/blanking failed due to
125362306a36Sopenharmony_ci	 *  e.g. a video mode which doesn't support it. Implements VESA suspend
125462306a36Sopenharmony_ci	 *  and powerdown modes for monitors, and backlight control on LCDs.
125562306a36Sopenharmony_ci	 *    blank_mode == 0: unblanked (backlight on)
125662306a36Sopenharmony_ci	 *    blank_mode == 1: blank (backlight on)
125762306a36Sopenharmony_ci	 *    blank_mode == 2: suspend vsync (backlight off)
125862306a36Sopenharmony_ci	 *    blank_mode == 3: suspend hsync (backlight off)
125962306a36Sopenharmony_ci	 *    blank_mode == 4: powerdown (backlight off)
126062306a36Sopenharmony_ci	 *
126162306a36Sopenharmony_ci	 *  wms...Enable VESA DPMS compatible powerdown mode
126262306a36Sopenharmony_ci	 *  run "setterm -powersave powerdown" to take advantage
126362306a36Sopenharmony_ci	 */
126462306a36Sopenharmony_ci	struct neofb_par *par = info->par;
126562306a36Sopenharmony_ci	int seqflags, lcdflags, dpmsflags, reg, tmpdisp;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/*
126862306a36Sopenharmony_ci	 * Read back the register bits related to display configuration. They might
126962306a36Sopenharmony_ci	 * have been changed underneath the driver via Fn key stroke.
127062306a36Sopenharmony_ci	 */
127162306a36Sopenharmony_ci	neoUnlock();
127262306a36Sopenharmony_ci	tmpdisp = vga_rgfx(NULL, 0x20) & 0x03;
127362306a36Sopenharmony_ci	neoLock(&par->state);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	/* In case we blank the screen, we want to store the possibly new
127662306a36Sopenharmony_ci	 * configuration in the driver. During un-blank, we re-apply this setting,
127762306a36Sopenharmony_ci	 * since the LCD bit will be cleared in order to switch off the backlight.
127862306a36Sopenharmony_ci	 */
127962306a36Sopenharmony_ci	if (par->PanelDispCntlRegRead) {
128062306a36Sopenharmony_ci		par->PanelDispCntlReg1 = tmpdisp;
128162306a36Sopenharmony_ci	}
128262306a36Sopenharmony_ci	par->PanelDispCntlRegRead = !blank_mode;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	switch (blank_mode) {
128562306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN:	/* powerdown - both sync lines down */
128662306a36Sopenharmony_ci		seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */
128762306a36Sopenharmony_ci		lcdflags = 0;			/* LCD off */
128862306a36Sopenharmony_ci		dpmsflags = NEO_GR01_SUPPRESS_HSYNC |
128962306a36Sopenharmony_ci			    NEO_GR01_SUPPRESS_VSYNC;
129062306a36Sopenharmony_ci#ifdef CONFIG_TOSHIBA
129162306a36Sopenharmony_ci		/* Do we still need this ? */
129262306a36Sopenharmony_ci		/* attempt to turn off backlight on toshiba; also turns off external */
129362306a36Sopenharmony_ci		{
129462306a36Sopenharmony_ci			SMMRegisters regs;
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci			regs.eax = 0xff00; /* HCI_SET */
129762306a36Sopenharmony_ci			regs.ebx = 0x0002; /* HCI_BACKLIGHT */
129862306a36Sopenharmony_ci			regs.ecx = 0x0000; /* HCI_DISABLE */
129962306a36Sopenharmony_ci			tosh_smm(&regs);
130062306a36Sopenharmony_ci		}
130162306a36Sopenharmony_ci#endif
130262306a36Sopenharmony_ci		break;
130362306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:		/* hsync off */
130462306a36Sopenharmony_ci		seqflags = VGA_SR01_SCREEN_OFF;	/* Disable sequencer */
130562306a36Sopenharmony_ci		lcdflags = 0;			/* LCD off */
130662306a36Sopenharmony_ci		dpmsflags = NEO_GR01_SUPPRESS_HSYNC;
130762306a36Sopenharmony_ci		break;
130862306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:		/* vsync off */
130962306a36Sopenharmony_ci		seqflags = VGA_SR01_SCREEN_OFF;	/* Disable sequencer */
131062306a36Sopenharmony_ci		lcdflags = 0;			/* LCD off */
131162306a36Sopenharmony_ci		dpmsflags = NEO_GR01_SUPPRESS_VSYNC;
131262306a36Sopenharmony_ci		break;
131362306a36Sopenharmony_ci	case FB_BLANK_NORMAL:		/* just blank screen (backlight stays on) */
131462306a36Sopenharmony_ci		seqflags = VGA_SR01_SCREEN_OFF;	/* Disable sequencer */
131562306a36Sopenharmony_ci		/*
131662306a36Sopenharmony_ci		 * During a blank operation with the LID shut, we might store "LCD off"
131762306a36Sopenharmony_ci		 * by mistake. Due to timing issues, the BIOS may switch the lights
131862306a36Sopenharmony_ci		 * back on, and we turn it back off once we "unblank".
131962306a36Sopenharmony_ci		 *
132062306a36Sopenharmony_ci		 * So here is an attempt to implement ">=" - if we are in the process
132162306a36Sopenharmony_ci		 * of unblanking, and the LCD bit is unset in the driver but set in the
132262306a36Sopenharmony_ci		 * register, we must keep it.
132362306a36Sopenharmony_ci		 */
132462306a36Sopenharmony_ci		lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
132562306a36Sopenharmony_ci		dpmsflags = 0x00;	/* no hsync/vsync suppression */
132662306a36Sopenharmony_ci		break;
132762306a36Sopenharmony_ci	case FB_BLANK_UNBLANK:		/* unblank */
132862306a36Sopenharmony_ci		seqflags = 0;			/* Enable sequencer */
132962306a36Sopenharmony_ci		lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
133062306a36Sopenharmony_ci		dpmsflags = 0x00;	/* no hsync/vsync suppression */
133162306a36Sopenharmony_ci#ifdef CONFIG_TOSHIBA
133262306a36Sopenharmony_ci		/* Do we still need this ? */
133362306a36Sopenharmony_ci		/* attempt to re-enable backlight/external on toshiba */
133462306a36Sopenharmony_ci		{
133562306a36Sopenharmony_ci			SMMRegisters regs;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci			regs.eax = 0xff00; /* HCI_SET */
133862306a36Sopenharmony_ci			regs.ebx = 0x0002; /* HCI_BACKLIGHT */
133962306a36Sopenharmony_ci			regs.ecx = 0x0001; /* HCI_ENABLE */
134062306a36Sopenharmony_ci			tosh_smm(&regs);
134162306a36Sopenharmony_ci		}
134262306a36Sopenharmony_ci#endif
134362306a36Sopenharmony_ci		break;
134462306a36Sopenharmony_ci	default:	/* Anything else we don't understand; return 1 to tell
134562306a36Sopenharmony_ci			 * fb_blank we didn't aactually do anything */
134662306a36Sopenharmony_ci		return 1;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	neoUnlock();
135062306a36Sopenharmony_ci	reg = (vga_rseq(NULL, 0x01) & ~0x20) | seqflags;
135162306a36Sopenharmony_ci	vga_wseq(NULL, 0x01, reg);
135262306a36Sopenharmony_ci	reg = (vga_rgfx(NULL, 0x20) & ~0x02) | lcdflags;
135362306a36Sopenharmony_ci	vga_wgfx(NULL, 0x20, reg);
135462306a36Sopenharmony_ci	reg = (vga_rgfx(NULL, 0x01) & ~0xF0) | 0x80 | dpmsflags;
135562306a36Sopenharmony_ci	vga_wgfx(NULL, 0x01, reg);
135662306a36Sopenharmony_ci	neoLock(&par->state);
135762306a36Sopenharmony_ci	return 0;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic void
136162306a36Sopenharmony_cineo2200_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct neofb_par *par = info->par;
136462306a36Sopenharmony_ci	u_long dst, rop;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	dst = rect->dx + rect->dy * info->var.xres_virtual;
136762306a36Sopenharmony_ci	rop = rect->rop ? 0x060000 : 0x0c0000;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	neo2200_wait_fifo(info, 4);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* set blt control */
137262306a36Sopenharmony_ci	writel(NEO_BC3_FIFO_EN |
137362306a36Sopenharmony_ci	       NEO_BC0_SRC_IS_FG | NEO_BC3_SKIP_MAPPING |
137462306a36Sopenharmony_ci	       //               NEO_BC3_DST_XY_ADDR  |
137562306a36Sopenharmony_ci	       //               NEO_BC3_SRC_XY_ADDR  |
137662306a36Sopenharmony_ci	       rop, &par->neo2200->bltCntl);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
137962306a36Sopenharmony_ci	case 8:
138062306a36Sopenharmony_ci		writel(rect->color, &par->neo2200->fgColor);
138162306a36Sopenharmony_ci		break;
138262306a36Sopenharmony_ci	case 16:
138362306a36Sopenharmony_ci	case 24:
138462306a36Sopenharmony_ci		writel(((u32 *) (info->pseudo_palette))[rect->color],
138562306a36Sopenharmony_ci		       &par->neo2200->fgColor);
138662306a36Sopenharmony_ci		break;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	writel(dst * ((info->var.bits_per_pixel + 7) >> 3),
139062306a36Sopenharmony_ci	       &par->neo2200->dstStart);
139162306a36Sopenharmony_ci	writel((rect->height << 16) | (rect->width & 0xffff),
139262306a36Sopenharmony_ci	       &par->neo2200->xyExt);
139362306a36Sopenharmony_ci}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cistatic void
139662306a36Sopenharmony_cineo2200_copyarea(struct fb_info *info, const struct fb_copyarea *area)
139762306a36Sopenharmony_ci{
139862306a36Sopenharmony_ci	u32 sx = area->sx, sy = area->sy, dx = area->dx, dy = area->dy;
139962306a36Sopenharmony_ci	struct neofb_par *par = info->par;
140062306a36Sopenharmony_ci	u_long src, dst, bltCntl;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	bltCntl = NEO_BC3_FIFO_EN | NEO_BC3_SKIP_MAPPING | 0x0C0000;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	if ((dy > sy) || ((dy == sy) && (dx > sx))) {
140562306a36Sopenharmony_ci		/* Start with the lower right corner */
140662306a36Sopenharmony_ci		sy += (area->height - 1);
140762306a36Sopenharmony_ci		dy += (area->height - 1);
140862306a36Sopenharmony_ci		sx += (area->width - 1);
140962306a36Sopenharmony_ci		dx += (area->width - 1);
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		bltCntl |= NEO_BC0_X_DEC | NEO_BC0_DST_Y_DEC | NEO_BC0_SRC_Y_DEC;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	src = sx * (info->var.bits_per_pixel >> 3) + sy*info->fix.line_length;
141562306a36Sopenharmony_ci	dst = dx * (info->var.bits_per_pixel >> 3) + dy*info->fix.line_length;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	neo2200_wait_fifo(info, 4);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/* set blt control */
142062306a36Sopenharmony_ci	writel(bltCntl, &par->neo2200->bltCntl);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	writel(src, &par->neo2200->srcStart);
142362306a36Sopenharmony_ci	writel(dst, &par->neo2200->dstStart);
142462306a36Sopenharmony_ci	writel((area->height << 16) | (area->width & 0xffff),
142562306a36Sopenharmony_ci	       &par->neo2200->xyExt);
142662306a36Sopenharmony_ci}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_cistatic void
142962306a36Sopenharmony_cineo2200_imageblit(struct fb_info *info, const struct fb_image *image)
143062306a36Sopenharmony_ci{
143162306a36Sopenharmony_ci	struct neofb_par *par = info->par;
143262306a36Sopenharmony_ci	int s_pitch = (image->width * image->depth + 7) >> 3;
143362306a36Sopenharmony_ci	int scan_align = info->pixmap.scan_align - 1;
143462306a36Sopenharmony_ci	int buf_align = info->pixmap.buf_align - 1;
143562306a36Sopenharmony_ci	int bltCntl_flags, d_pitch, data_len;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	// The data is padded for the hardware
143862306a36Sopenharmony_ci	d_pitch = (s_pitch + scan_align) & ~scan_align;
143962306a36Sopenharmony_ci	data_len = ((d_pitch * image->height) + buf_align) & ~buf_align;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	neo2200_sync(info);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (image->depth == 1) {
144462306a36Sopenharmony_ci		if (info->var.bits_per_pixel == 24 && image->width < 16) {
144562306a36Sopenharmony_ci			/* FIXME. There is a bug with accelerated color-expanded
144662306a36Sopenharmony_ci			 * transfers in 24 bit mode if the image being transferred
144762306a36Sopenharmony_ci			 * is less than 16 bits wide. This is due to insufficient
144862306a36Sopenharmony_ci			 * padding when writing the image. We need to adjust
144962306a36Sopenharmony_ci			 * struct fb_pixmap. Not yet done. */
145062306a36Sopenharmony_ci			cfb_imageblit(info, image);
145162306a36Sopenharmony_ci			return;
145262306a36Sopenharmony_ci		}
145362306a36Sopenharmony_ci		bltCntl_flags = NEO_BC0_SRC_MONO;
145462306a36Sopenharmony_ci	} else if (image->depth == info->var.bits_per_pixel) {
145562306a36Sopenharmony_ci		bltCntl_flags = 0;
145662306a36Sopenharmony_ci	} else {
145762306a36Sopenharmony_ci		/* We don't currently support hardware acceleration if image
145862306a36Sopenharmony_ci		 * depth is different from display */
145962306a36Sopenharmony_ci		cfb_imageblit(info, image);
146062306a36Sopenharmony_ci		return;
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
146462306a36Sopenharmony_ci	case 8:
146562306a36Sopenharmony_ci		writel(image->fg_color, &par->neo2200->fgColor);
146662306a36Sopenharmony_ci		writel(image->bg_color, &par->neo2200->bgColor);
146762306a36Sopenharmony_ci		break;
146862306a36Sopenharmony_ci	case 16:
146962306a36Sopenharmony_ci	case 24:
147062306a36Sopenharmony_ci		writel(((u32 *) (info->pseudo_palette))[image->fg_color],
147162306a36Sopenharmony_ci		       &par->neo2200->fgColor);
147262306a36Sopenharmony_ci		writel(((u32 *) (info->pseudo_palette))[image->bg_color],
147362306a36Sopenharmony_ci		       &par->neo2200->bgColor);
147462306a36Sopenharmony_ci		break;
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	writel(NEO_BC0_SYS_TO_VID |
147862306a36Sopenharmony_ci		NEO_BC3_SKIP_MAPPING | bltCntl_flags |
147962306a36Sopenharmony_ci		// NEO_BC3_DST_XY_ADDR |
148062306a36Sopenharmony_ci		0x0c0000, &par->neo2200->bltCntl);
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci	writel(0, &par->neo2200->srcStart);
148362306a36Sopenharmony_ci//      par->neo2200->dstStart = (image->dy << 16) | (image->dx & 0xffff);
148462306a36Sopenharmony_ci	writel(((image->dx & 0xffff) * (info->var.bits_per_pixel >> 3) +
148562306a36Sopenharmony_ci		image->dy * info->fix.line_length), &par->neo2200->dstStart);
148662306a36Sopenharmony_ci	writel((image->height << 16) | (image->width & 0xffff),
148762306a36Sopenharmony_ci	       &par->neo2200->xyExt);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	memcpy_toio(par->mmio_vbase + 0x100000, image->data, data_len);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic void
149362306a36Sopenharmony_cineofb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	switch (info->fix.accel) {
149662306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
149762306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
149862306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
149962306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
150062306a36Sopenharmony_ci			neo2200_fillrect(info, rect);
150162306a36Sopenharmony_ci			break;
150262306a36Sopenharmony_ci		default:
150362306a36Sopenharmony_ci			cfb_fillrect(info, rect);
150462306a36Sopenharmony_ci			break;
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_cistatic void
150962306a36Sopenharmony_cineofb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
151062306a36Sopenharmony_ci{
151162306a36Sopenharmony_ci	switch (info->fix.accel) {
151262306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
151362306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
151462306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
151562306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
151662306a36Sopenharmony_ci			neo2200_copyarea(info, area);
151762306a36Sopenharmony_ci			break;
151862306a36Sopenharmony_ci		default:
151962306a36Sopenharmony_ci			cfb_copyarea(info, area);
152062306a36Sopenharmony_ci			break;
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci}
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_cistatic void
152562306a36Sopenharmony_cineofb_imageblit(struct fb_info *info, const struct fb_image *image)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	switch (info->fix.accel) {
152862306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
152962306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
153062306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
153162306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
153262306a36Sopenharmony_ci			neo2200_imageblit(info, image);
153362306a36Sopenharmony_ci			break;
153462306a36Sopenharmony_ci		default:
153562306a36Sopenharmony_ci			cfb_imageblit(info, image);
153662306a36Sopenharmony_ci			break;
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cistatic int
154162306a36Sopenharmony_cineofb_sync(struct fb_info *info)
154262306a36Sopenharmony_ci{
154362306a36Sopenharmony_ci	switch (info->fix.accel) {
154462306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
154562306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
154662306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
154762306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
154862306a36Sopenharmony_ci			neo2200_sync(info);
154962306a36Sopenharmony_ci			break;
155062306a36Sopenharmony_ci		default:
155162306a36Sopenharmony_ci			break;
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci	return 0;
155462306a36Sopenharmony_ci}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci/*
155762306a36Sopenharmony_cistatic void
155862306a36Sopenharmony_cineofb_draw_cursor(struct fb_info *info, u8 *dst, u8 *src, unsigned int width)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	//memset_io(info->sprite.addr, 0xff, 1);
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic int
156462306a36Sopenharmony_cineofb_cursor(struct fb_info *info, struct fb_cursor *cursor)
156562306a36Sopenharmony_ci{
156662306a36Sopenharmony_ci	struct neofb_par *par = (struct neofb_par *) info->par;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	* Disable cursor *
156962306a36Sopenharmony_ci	write_le32(NEOREG_CURSCNTL, ~NEO_CURS_ENABLE, par);
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETPOS) {
157262306a36Sopenharmony_ci		u32 x = cursor->image.dx;
157362306a36Sopenharmony_ci		u32 y = cursor->image.dy;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		info->cursor.image.dx = x;
157662306a36Sopenharmony_ci		info->cursor.image.dy = y;
157762306a36Sopenharmony_ci		write_le32(NEOREG_CURSX, x, par);
157862306a36Sopenharmony_ci		write_le32(NEOREG_CURSY, y, par);
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETSIZE) {
158262306a36Sopenharmony_ci		info->cursor.image.height = cursor->image.height;
158362306a36Sopenharmony_ci		info->cursor.image.width = cursor->image.width;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETHOT)
158762306a36Sopenharmony_ci		info->cursor.hot = cursor->hot;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETCMAP) {
159062306a36Sopenharmony_ci		if (cursor->image.depth == 1) {
159162306a36Sopenharmony_ci			u32 fg = cursor->image.fg_color;
159262306a36Sopenharmony_ci			u32 bg = cursor->image.bg_color;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci			info->cursor.image.fg_color = fg;
159562306a36Sopenharmony_ci			info->cursor.image.bg_color = bg;
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci			fg = ((fg & 0xff0000) >> 16) | ((fg & 0xff) << 16) | (fg & 0xff00);
159862306a36Sopenharmony_ci			bg = ((bg & 0xff0000) >> 16) | ((bg & 0xff) << 16) | (bg & 0xff00);
159962306a36Sopenharmony_ci			write_le32(NEOREG_CURSFGCOLOR, fg, par);
160062306a36Sopenharmony_ci			write_le32(NEOREG_CURSBGCOLOR, bg, par);
160162306a36Sopenharmony_ci		}
160262306a36Sopenharmony_ci	}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETSHAPE)
160562306a36Sopenharmony_ci		fb_load_cursor_image(info);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	if (info->cursor.enable)
160862306a36Sopenharmony_ci		write_le32(NEOREG_CURSCNTL, NEO_CURS_ENABLE, par);
160962306a36Sopenharmony_ci	return 0;
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci*/
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic const struct fb_ops neofb_ops = {
161462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
161562306a36Sopenharmony_ci	.fb_open	= neofb_open,
161662306a36Sopenharmony_ci	.fb_release	= neofb_release,
161762306a36Sopenharmony_ci	.fb_check_var	= neofb_check_var,
161862306a36Sopenharmony_ci	.fb_set_par	= neofb_set_par,
161962306a36Sopenharmony_ci	.fb_setcolreg	= neofb_setcolreg,
162062306a36Sopenharmony_ci	.fb_pan_display	= neofb_pan_display,
162162306a36Sopenharmony_ci	.fb_blank	= neofb_blank,
162262306a36Sopenharmony_ci	.fb_sync	= neofb_sync,
162362306a36Sopenharmony_ci	.fb_fillrect	= neofb_fillrect,
162462306a36Sopenharmony_ci	.fb_copyarea	= neofb_copyarea,
162562306a36Sopenharmony_ci	.fb_imageblit	= neofb_imageblit,
162662306a36Sopenharmony_ci};
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_cistatic struct fb_videomode mode800x480 = {
163162306a36Sopenharmony_ci	.xres           = 800,
163262306a36Sopenharmony_ci	.yres           = 480,
163362306a36Sopenharmony_ci	.pixclock       = 25000,
163462306a36Sopenharmony_ci	.left_margin    = 88,
163562306a36Sopenharmony_ci	.right_margin   = 40,
163662306a36Sopenharmony_ci	.upper_margin   = 23,
163762306a36Sopenharmony_ci	.lower_margin   = 1,
163862306a36Sopenharmony_ci	.hsync_len      = 128,
163962306a36Sopenharmony_ci	.vsync_len      = 4,
164062306a36Sopenharmony_ci	.sync           = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
164162306a36Sopenharmony_ci	.vmode          = FB_VMODE_NONINTERLACED
164262306a36Sopenharmony_ci};
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic int neo_map_mmio(struct fb_info *info, struct pci_dev *dev)
164562306a36Sopenharmony_ci{
164662306a36Sopenharmony_ci	struct neofb_par *par = info->par;
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	DBG("neo_map_mmio");
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	switch (info->fix.accel) {
165162306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2070:
165262306a36Sopenharmony_ci			info->fix.mmio_start = pci_resource_start(dev, 0)+
165362306a36Sopenharmony_ci				0x100000;
165462306a36Sopenharmony_ci			break;
165562306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2090:
165662306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2093:
165762306a36Sopenharmony_ci			info->fix.mmio_start = pci_resource_start(dev, 0)+
165862306a36Sopenharmony_ci				0x200000;
165962306a36Sopenharmony_ci			break;
166062306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2160:
166162306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2097:
166262306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2200:
166362306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2230:
166462306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2360:
166562306a36Sopenharmony_ci		case FB_ACCEL_NEOMAGIC_NM2380:
166662306a36Sopenharmony_ci			info->fix.mmio_start = pci_resource_start(dev, 1);
166762306a36Sopenharmony_ci			break;
166862306a36Sopenharmony_ci		default:
166962306a36Sopenharmony_ci			info->fix.mmio_start = pci_resource_start(dev, 0);
167062306a36Sopenharmony_ci	}
167162306a36Sopenharmony_ci	info->fix.mmio_len = MMIO_SIZE;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	if (!request_mem_region
167462306a36Sopenharmony_ci	    (info->fix.mmio_start, MMIO_SIZE, "memory mapped I/O")) {
167562306a36Sopenharmony_ci		printk("neofb: memory mapped IO in use\n");
167662306a36Sopenharmony_ci		return -EBUSY;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	par->mmio_vbase = ioremap(info->fix.mmio_start, MMIO_SIZE);
168062306a36Sopenharmony_ci	if (!par->mmio_vbase) {
168162306a36Sopenharmony_ci		printk("neofb: unable to map memory mapped IO\n");
168262306a36Sopenharmony_ci		release_mem_region(info->fix.mmio_start,
168362306a36Sopenharmony_ci				   info->fix.mmio_len);
168462306a36Sopenharmony_ci		return -ENOMEM;
168562306a36Sopenharmony_ci	} else
168662306a36Sopenharmony_ci		printk(KERN_INFO "neofb: mapped io at %p\n",
168762306a36Sopenharmony_ci		       par->mmio_vbase);
168862306a36Sopenharmony_ci	return 0;
168962306a36Sopenharmony_ci}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_cistatic void neo_unmap_mmio(struct fb_info *info)
169262306a36Sopenharmony_ci{
169362306a36Sopenharmony_ci	struct neofb_par *par = info->par;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	DBG("neo_unmap_mmio");
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	iounmap(par->mmio_vbase);
169862306a36Sopenharmony_ci	par->mmio_vbase = NULL;
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	release_mem_region(info->fix.mmio_start,
170162306a36Sopenharmony_ci			   info->fix.mmio_len);
170262306a36Sopenharmony_ci}
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_cistatic int neo_map_video(struct fb_info *info, struct pci_dev *dev,
170562306a36Sopenharmony_ci			 int video_len)
170662306a36Sopenharmony_ci{
170762306a36Sopenharmony_ci	//unsigned long addr;
170862306a36Sopenharmony_ci	struct neofb_par *par = info->par;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	DBG("neo_map_video");
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	info->fix.smem_start = pci_resource_start(dev, 0);
171362306a36Sopenharmony_ci	info->fix.smem_len = video_len;
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	if (!request_mem_region(info->fix.smem_start, info->fix.smem_len,
171662306a36Sopenharmony_ci				"frame buffer")) {
171762306a36Sopenharmony_ci		printk("neofb: frame buffer in use\n");
171862306a36Sopenharmony_ci		return -EBUSY;
171962306a36Sopenharmony_ci	}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	info->screen_base =
172262306a36Sopenharmony_ci	    ioremap_wc(info->fix.smem_start, info->fix.smem_len);
172362306a36Sopenharmony_ci	if (!info->screen_base) {
172462306a36Sopenharmony_ci		printk("neofb: unable to map screen memory\n");
172562306a36Sopenharmony_ci		release_mem_region(info->fix.smem_start,
172662306a36Sopenharmony_ci				   info->fix.smem_len);
172762306a36Sopenharmony_ci		return -ENOMEM;
172862306a36Sopenharmony_ci	} else
172962306a36Sopenharmony_ci		printk(KERN_INFO "neofb: mapped framebuffer at %p\n",
173062306a36Sopenharmony_ci		       info->screen_base);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	par->wc_cookie = arch_phys_wc_add(info->fix.smem_start,
173362306a36Sopenharmony_ci					  pci_resource_len(dev, 0));
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci	/* Clear framebuffer, it's all white in memory after boot */
173662306a36Sopenharmony_ci	memset_io(info->screen_base, 0, info->fix.smem_len);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	/* Allocate Cursor drawing pad.
173962306a36Sopenharmony_ci	info->fix.smem_len -= PAGE_SIZE;
174062306a36Sopenharmony_ci	addr = info->fix.smem_start + info->fix.smem_len;
174162306a36Sopenharmony_ci	write_le32(NEOREG_CURSMEMPOS, ((0x000f & (addr >> 10)) << 8) |
174262306a36Sopenharmony_ci					((0x0ff0 & (addr >> 10)) >> 4), par);
174362306a36Sopenharmony_ci	addr = (unsigned long) info->screen_base + info->fix.smem_len;
174462306a36Sopenharmony_ci	info->sprite.addr = (u8 *) addr; */
174562306a36Sopenharmony_ci	return 0;
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cistatic void neo_unmap_video(struct fb_info *info)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct neofb_par *par = info->par;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	DBG("neo_unmap_video");
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
175562306a36Sopenharmony_ci	iounmap(info->screen_base);
175662306a36Sopenharmony_ci	info->screen_base = NULL;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	release_mem_region(info->fix.smem_start,
175962306a36Sopenharmony_ci			   info->fix.smem_len);
176062306a36Sopenharmony_ci}
176162306a36Sopenharmony_ci
176262306a36Sopenharmony_cistatic int neo_scan_monitor(struct fb_info *info)
176362306a36Sopenharmony_ci{
176462306a36Sopenharmony_ci	struct neofb_par *par = info->par;
176562306a36Sopenharmony_ci	unsigned char type, display;
176662306a36Sopenharmony_ci	int w;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	// Eventually we will have i2c support.
176962306a36Sopenharmony_ci	info->monspecs.modedb = kmalloc(sizeof(struct fb_videomode), GFP_KERNEL);
177062306a36Sopenharmony_ci	if (!info->monspecs.modedb)
177162306a36Sopenharmony_ci		return -ENOMEM;
177262306a36Sopenharmony_ci	info->monspecs.modedb_len = 1;
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	/* Determine the panel type */
177562306a36Sopenharmony_ci	vga_wgfx(NULL, 0x09, 0x26);
177662306a36Sopenharmony_ci	type = vga_rgfx(NULL, 0x21);
177762306a36Sopenharmony_ci	display = vga_rgfx(NULL, 0x20);
177862306a36Sopenharmony_ci	if (!par->internal_display && !par->external_display) {
177962306a36Sopenharmony_ci		par->internal_display = display & 2 || !(display & 3) ? 1 : 0;
178062306a36Sopenharmony_ci		par->external_display = display & 1;
178162306a36Sopenharmony_ci		printk (KERN_INFO "Autodetected %s display\n",
178262306a36Sopenharmony_ci			par->internal_display && par->external_display ? "simultaneous" :
178362306a36Sopenharmony_ci			par->internal_display ? "internal" : "external");
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	/* Determine panel width -- used in NeoValidMode. */
178762306a36Sopenharmony_ci	w = vga_rgfx(NULL, 0x20);
178862306a36Sopenharmony_ci	vga_wgfx(NULL, 0x09, 0x00);
178962306a36Sopenharmony_ci	switch ((w & 0x18) >> 3) {
179062306a36Sopenharmony_ci	case 0x00:
179162306a36Sopenharmony_ci		// 640x480@60
179262306a36Sopenharmony_ci		par->NeoPanelWidth = 640;
179362306a36Sopenharmony_ci		par->NeoPanelHeight = 480;
179462306a36Sopenharmony_ci		memcpy(info->monspecs.modedb, &vesa_modes[3], sizeof(struct fb_videomode));
179562306a36Sopenharmony_ci		break;
179662306a36Sopenharmony_ci	case 0x01:
179762306a36Sopenharmony_ci		par->NeoPanelWidth = 800;
179862306a36Sopenharmony_ci		if (par->libretto) {
179962306a36Sopenharmony_ci			par->NeoPanelHeight = 480;
180062306a36Sopenharmony_ci			memcpy(info->monspecs.modedb, &mode800x480, sizeof(struct fb_videomode));
180162306a36Sopenharmony_ci		} else {
180262306a36Sopenharmony_ci			// 800x600@60
180362306a36Sopenharmony_ci			par->NeoPanelHeight = 600;
180462306a36Sopenharmony_ci			memcpy(info->monspecs.modedb, &vesa_modes[8], sizeof(struct fb_videomode));
180562306a36Sopenharmony_ci		}
180662306a36Sopenharmony_ci		break;
180762306a36Sopenharmony_ci	case 0x02:
180862306a36Sopenharmony_ci		// 1024x768@60
180962306a36Sopenharmony_ci		par->NeoPanelWidth = 1024;
181062306a36Sopenharmony_ci		par->NeoPanelHeight = 768;
181162306a36Sopenharmony_ci		memcpy(info->monspecs.modedb, &vesa_modes[13], sizeof(struct fb_videomode));
181262306a36Sopenharmony_ci		break;
181362306a36Sopenharmony_ci	case 0x03:
181462306a36Sopenharmony_ci		/* 1280x1024@60 panel support needs to be added */
181562306a36Sopenharmony_ci#ifdef NOT_DONE
181662306a36Sopenharmony_ci		par->NeoPanelWidth = 1280;
181762306a36Sopenharmony_ci		par->NeoPanelHeight = 1024;
181862306a36Sopenharmony_ci		memcpy(info->monspecs.modedb, &vesa_modes[20], sizeof(struct fb_videomode));
181962306a36Sopenharmony_ci		break;
182062306a36Sopenharmony_ci#else
182162306a36Sopenharmony_ci		printk(KERN_ERR
182262306a36Sopenharmony_ci		       "neofb: Only 640x480, 800x600/480 and 1024x768 panels are currently supported\n");
182362306a36Sopenharmony_ci		kfree(info->monspecs.modedb);
182462306a36Sopenharmony_ci		return -1;
182562306a36Sopenharmony_ci#endif
182662306a36Sopenharmony_ci	default:
182762306a36Sopenharmony_ci		// 640x480@60
182862306a36Sopenharmony_ci		par->NeoPanelWidth = 640;
182962306a36Sopenharmony_ci		par->NeoPanelHeight = 480;
183062306a36Sopenharmony_ci		memcpy(info->monspecs.modedb, &vesa_modes[3], sizeof(struct fb_videomode));
183162306a36Sopenharmony_ci		break;
183262306a36Sopenharmony_ci	}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	printk(KERN_INFO "Panel is a %dx%d %s %s display\n",
183562306a36Sopenharmony_ci	       par->NeoPanelWidth,
183662306a36Sopenharmony_ci	       par->NeoPanelHeight,
183762306a36Sopenharmony_ci	       (type & 0x02) ? "color" : "monochrome",
183862306a36Sopenharmony_ci	       (type & 0x10) ? "TFT" : "dual scan");
183962306a36Sopenharmony_ci	return 0;
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cistatic int neo_init_hw(struct fb_info *info)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	struct neofb_par *par = info->par;
184562306a36Sopenharmony_ci	int videoRam = 896;
184662306a36Sopenharmony_ci	int maxClock = 65000;
184762306a36Sopenharmony_ci	int CursorOff = 0x100;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	DBG("neo_init_hw");
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	neoUnlock();
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci#if 0
185462306a36Sopenharmony_ci	printk(KERN_DEBUG "--- Neo extended register dump ---\n");
185562306a36Sopenharmony_ci	for (int w = 0; w < 0x85; w++)
185662306a36Sopenharmony_ci		printk(KERN_DEBUG "CR %p: %p\n", (void *) w,
185762306a36Sopenharmony_ci		       (void *) vga_rcrt(NULL, w));
185862306a36Sopenharmony_ci	for (int w = 0; w < 0xC7; w++)
185962306a36Sopenharmony_ci		printk(KERN_DEBUG "GR %p: %p\n", (void *) w,
186062306a36Sopenharmony_ci		       (void *) vga_rgfx(NULL, w));
186162306a36Sopenharmony_ci#endif
186262306a36Sopenharmony_ci	switch (info->fix.accel) {
186362306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2070:
186462306a36Sopenharmony_ci		videoRam = 896;
186562306a36Sopenharmony_ci		maxClock = 65000;
186662306a36Sopenharmony_ci		break;
186762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2090:
186862306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2093:
186962306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2097:
187062306a36Sopenharmony_ci		videoRam = 1152;
187162306a36Sopenharmony_ci		maxClock = 80000;
187262306a36Sopenharmony_ci		break;
187362306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2160:
187462306a36Sopenharmony_ci		videoRam = 2048;
187562306a36Sopenharmony_ci		maxClock = 90000;
187662306a36Sopenharmony_ci		break;
187762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2200:
187862306a36Sopenharmony_ci		videoRam = 2560;
187962306a36Sopenharmony_ci		maxClock = 110000;
188062306a36Sopenharmony_ci		break;
188162306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2230:
188262306a36Sopenharmony_ci		videoRam = 3008;
188362306a36Sopenharmony_ci		maxClock = 110000;
188462306a36Sopenharmony_ci		break;
188562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2360:
188662306a36Sopenharmony_ci		videoRam = 4096;
188762306a36Sopenharmony_ci		maxClock = 110000;
188862306a36Sopenharmony_ci		break;
188962306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2380:
189062306a36Sopenharmony_ci		videoRam = 6144;
189162306a36Sopenharmony_ci		maxClock = 110000;
189262306a36Sopenharmony_ci		break;
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci	switch (info->fix.accel) {
189562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2070:
189662306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2090:
189762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2093:
189862306a36Sopenharmony_ci		CursorOff = 0x100;
189962306a36Sopenharmony_ci		break;
190062306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2097:
190162306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2160:
190262306a36Sopenharmony_ci		CursorOff = 0x100;
190362306a36Sopenharmony_ci		break;
190462306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2200:
190562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2230:
190662306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2360:
190762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2380:
190862306a36Sopenharmony_ci		CursorOff = 0x1000;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci		par->neo2200 = (Neo2200 __iomem *) par->mmio_vbase;
191162306a36Sopenharmony_ci		break;
191262306a36Sopenharmony_ci	}
191362306a36Sopenharmony_ci/*
191462306a36Sopenharmony_ci	info->sprite.size = CursorMem;
191562306a36Sopenharmony_ci	info->sprite.scan_align = 1;
191662306a36Sopenharmony_ci	info->sprite.buf_align = 1;
191762306a36Sopenharmony_ci	info->sprite.flags = FB_PIXMAP_IO;
191862306a36Sopenharmony_ci	info->sprite.outbuf = neofb_draw_cursor;
191962306a36Sopenharmony_ci*/
192062306a36Sopenharmony_ci	par->maxClock = maxClock;
192162306a36Sopenharmony_ci	par->cursorOff = CursorOff;
192262306a36Sopenharmony_ci	return videoRam * 1024;
192362306a36Sopenharmony_ci}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_cistatic struct fb_info *neo_alloc_fb_info(struct pci_dev *dev,
192762306a36Sopenharmony_ci					 const struct pci_device_id *id)
192862306a36Sopenharmony_ci{
192962306a36Sopenharmony_ci	struct fb_info *info;
193062306a36Sopenharmony_ci	struct neofb_par *par;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct neofb_par), &dev->dev);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci	if (!info)
193562306a36Sopenharmony_ci		return NULL;
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	par = info->par;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	info->fix.accel = id->driver_data;
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	par->pci_burst = !nopciburst;
194262306a36Sopenharmony_ci	par->lcd_stretch = !nostretch;
194362306a36Sopenharmony_ci	par->libretto = libretto;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	par->internal_display = internal;
194662306a36Sopenharmony_ci	par->external_display = external;
194762306a36Sopenharmony_ci	info->flags = FBINFO_HWACCEL_YPAN;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	switch (info->fix.accel) {
195062306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2070:
195162306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph128", sizeof(info->fix.id));
195262306a36Sopenharmony_ci		break;
195362306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2090:
195462306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph128V", sizeof(info->fix.id));
195562306a36Sopenharmony_ci		break;
195662306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2093:
195762306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph128ZV", sizeof(info->fix.id));
195862306a36Sopenharmony_ci		break;
195962306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2097:
196062306a36Sopenharmony_ci		strscpy(info->fix.id, "Mag.Graph128ZV+", sizeof(info->fix.id));
196162306a36Sopenharmony_ci		break;
196262306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2160:
196362306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph128XD", sizeof(info->fix.id));
196462306a36Sopenharmony_ci		break;
196562306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2200:
196662306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph256AV", sizeof(info->fix.id));
196762306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_IMAGEBLIT |
196862306a36Sopenharmony_ci		               FBINFO_HWACCEL_COPYAREA |
196962306a36Sopenharmony_ci                	       FBINFO_HWACCEL_FILLRECT;
197062306a36Sopenharmony_ci		break;
197162306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2230:
197262306a36Sopenharmony_ci		strscpy(info->fix.id, "Mag.Graph256AV+", sizeof(info->fix.id));
197362306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_IMAGEBLIT |
197462306a36Sopenharmony_ci		               FBINFO_HWACCEL_COPYAREA |
197562306a36Sopenharmony_ci                	       FBINFO_HWACCEL_FILLRECT;
197662306a36Sopenharmony_ci		break;
197762306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2360:
197862306a36Sopenharmony_ci		strscpy(info->fix.id, "MagicGraph256ZX", sizeof(info->fix.id));
197962306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_IMAGEBLIT |
198062306a36Sopenharmony_ci		               FBINFO_HWACCEL_COPYAREA |
198162306a36Sopenharmony_ci                	       FBINFO_HWACCEL_FILLRECT;
198262306a36Sopenharmony_ci		break;
198362306a36Sopenharmony_ci	case FB_ACCEL_NEOMAGIC_NM2380:
198462306a36Sopenharmony_ci		strscpy(info->fix.id, "Mag.Graph256XL+", sizeof(info->fix.id));
198562306a36Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_IMAGEBLIT |
198662306a36Sopenharmony_ci		               FBINFO_HWACCEL_COPYAREA |
198762306a36Sopenharmony_ci                	       FBINFO_HWACCEL_FILLRECT;
198862306a36Sopenharmony_ci		break;
198962306a36Sopenharmony_ci	}
199062306a36Sopenharmony_ci
199162306a36Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
199262306a36Sopenharmony_ci	info->fix.type_aux = 0;
199362306a36Sopenharmony_ci	info->fix.xpanstep = 0;
199462306a36Sopenharmony_ci	info->fix.ypanstep = 4;
199562306a36Sopenharmony_ci	info->fix.ywrapstep = 0;
199662306a36Sopenharmony_ci	info->fix.accel = id->driver_data;
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	info->fbops = &neofb_ops;
199962306a36Sopenharmony_ci	info->pseudo_palette = par->palette;
200062306a36Sopenharmony_ci	return info;
200162306a36Sopenharmony_ci}
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_cistatic void neo_free_fb_info(struct fb_info *info)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	if (info) {
200662306a36Sopenharmony_ci		/*
200762306a36Sopenharmony_ci		 * Free the colourmap
200862306a36Sopenharmony_ci		 */
200962306a36Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
201062306a36Sopenharmony_ci		framebuffer_release(info);
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci}
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci/* --------------------------------------------------------------------- */
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_cistatic int neofb_probe(struct pci_dev *dev, const struct pci_device_id *id)
201762306a36Sopenharmony_ci{
201862306a36Sopenharmony_ci	struct fb_info *info;
201962306a36Sopenharmony_ci	u_int h_sync, v_sync;
202062306a36Sopenharmony_ci	int video_len, err;
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	DBG("neofb_probe");
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	err = aperture_remove_conflicting_pci_devices(dev, "neofb");
202562306a36Sopenharmony_ci	if (err)
202662306a36Sopenharmony_ci		return err;
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	err = pci_enable_device(dev);
202962306a36Sopenharmony_ci	if (err)
203062306a36Sopenharmony_ci		return err;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	err = -ENOMEM;
203362306a36Sopenharmony_ci	info = neo_alloc_fb_info(dev, id);
203462306a36Sopenharmony_ci	if (!info)
203562306a36Sopenharmony_ci		return err;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	err = neo_map_mmio(info, dev);
203862306a36Sopenharmony_ci	if (err)
203962306a36Sopenharmony_ci		goto err_map_mmio;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	err = neo_scan_monitor(info);
204262306a36Sopenharmony_ci	if (err)
204362306a36Sopenharmony_ci		goto err_scan_monitor;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	video_len = neo_init_hw(info);
204662306a36Sopenharmony_ci	if (video_len < 0) {
204762306a36Sopenharmony_ci		err = video_len;
204862306a36Sopenharmony_ci		goto err_init_hw;
204962306a36Sopenharmony_ci	}
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	err = neo_map_video(info, dev, video_len);
205262306a36Sopenharmony_ci	if (err)
205362306a36Sopenharmony_ci		goto err_init_hw;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	if (!fb_find_mode(&info->var, info, mode_option, NULL, 0,
205662306a36Sopenharmony_ci			info->monspecs.modedb, 16)) {
205762306a36Sopenharmony_ci		printk(KERN_ERR "neofb: Unable to find usable video mode.\n");
205862306a36Sopenharmony_ci		err = -EINVAL;
205962306a36Sopenharmony_ci		goto err_map_video;
206062306a36Sopenharmony_ci	}
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	/*
206362306a36Sopenharmony_ci	 * Calculate the hsync and vsync frequencies.  Note that
206462306a36Sopenharmony_ci	 * we split the 1e12 constant up so that we can preserve
206562306a36Sopenharmony_ci	 * the precision and fit the results into 32-bit registers.
206662306a36Sopenharmony_ci	 *  (1953125000 * 512 = 1e12)
206762306a36Sopenharmony_ci	 */
206862306a36Sopenharmony_ci	h_sync = 1953125000 / info->var.pixclock;
206962306a36Sopenharmony_ci	h_sync =
207062306a36Sopenharmony_ci	    h_sync * 512 / (info->var.xres + info->var.left_margin +
207162306a36Sopenharmony_ci			    info->var.right_margin + info->var.hsync_len);
207262306a36Sopenharmony_ci	v_sync =
207362306a36Sopenharmony_ci	    h_sync / (info->var.yres + info->var.upper_margin +
207462306a36Sopenharmony_ci		      info->var.lower_margin + info->var.vsync_len);
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	printk(KERN_INFO "neofb v" NEOFB_VERSION
207762306a36Sopenharmony_ci	       ": %dkB VRAM, using %dx%d, %d.%03dkHz, %dHz\n",
207862306a36Sopenharmony_ci	       info->fix.smem_len >> 10, info->var.xres,
207962306a36Sopenharmony_ci	       info->var.yres, h_sync / 1000, h_sync % 1000, v_sync);
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	err = fb_alloc_cmap(&info->cmap, 256, 0);
208262306a36Sopenharmony_ci	if (err < 0)
208362306a36Sopenharmony_ci		goto err_map_video;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	err = register_framebuffer(info);
208662306a36Sopenharmony_ci	if (err < 0)
208762306a36Sopenharmony_ci		goto err_reg_fb;
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	fb_info(info, "%s frame buffer device\n", info->fix.id);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	/*
209262306a36Sopenharmony_ci	 * Our driver data
209362306a36Sopenharmony_ci	 */
209462306a36Sopenharmony_ci	pci_set_drvdata(dev, info);
209562306a36Sopenharmony_ci	return 0;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_cierr_reg_fb:
209862306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
209962306a36Sopenharmony_cierr_map_video:
210062306a36Sopenharmony_ci	neo_unmap_video(info);
210162306a36Sopenharmony_cierr_init_hw:
210262306a36Sopenharmony_ci	fb_destroy_modedb(info->monspecs.modedb);
210362306a36Sopenharmony_cierr_scan_monitor:
210462306a36Sopenharmony_ci	neo_unmap_mmio(info);
210562306a36Sopenharmony_cierr_map_mmio:
210662306a36Sopenharmony_ci	neo_free_fb_info(info);
210762306a36Sopenharmony_ci	return err;
210862306a36Sopenharmony_ci}
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_cistatic void neofb_remove(struct pci_dev *dev)
211162306a36Sopenharmony_ci{
211262306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(dev);
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci	DBG("neofb_remove");
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (info) {
211762306a36Sopenharmony_ci		unregister_framebuffer(info);
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci		neo_unmap_video(info);
212062306a36Sopenharmony_ci		fb_destroy_modedb(info->monspecs.modedb);
212162306a36Sopenharmony_ci		neo_unmap_mmio(info);
212262306a36Sopenharmony_ci		neo_free_fb_info(info);
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_cistatic const struct pci_device_id neofb_devices[] = {
212762306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2070,
212862306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2070},
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2090,
213162306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2090},
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2093,
213462306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2093},
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2097,
213762306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2097},
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2160,
214062306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2160},
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2200,
214362306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2200},
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2230,
214662306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2230},
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2360,
214962306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2360},
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	{PCI_VENDOR_ID_NEOMAGIC, PCI_CHIP_NM2380,
215262306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, FB_ACCEL_NEOMAGIC_NM2380},
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	{0, 0, 0, 0, 0, 0, 0}
215562306a36Sopenharmony_ci};
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, neofb_devices);
215862306a36Sopenharmony_ci
215962306a36Sopenharmony_cistatic struct pci_driver neofb_driver = {
216062306a36Sopenharmony_ci	.name =		"neofb",
216162306a36Sopenharmony_ci	.id_table =	neofb_devices,
216262306a36Sopenharmony_ci	.probe =	neofb_probe,
216362306a36Sopenharmony_ci	.remove =	neofb_remove,
216462306a36Sopenharmony_ci};
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci/* ************************* init in-kernel code ************************** */
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci#ifndef MODULE
216962306a36Sopenharmony_cistatic int __init neofb_setup(char *options)
217062306a36Sopenharmony_ci{
217162306a36Sopenharmony_ci	char *this_opt;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci	DBG("neofb_setup");
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_ci	if (!options || !*options)
217662306a36Sopenharmony_ci		return 0;
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
217962306a36Sopenharmony_ci		if (!*this_opt)
218062306a36Sopenharmony_ci			continue;
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci		if (!strncmp(this_opt, "internal", 8))
218362306a36Sopenharmony_ci			internal = 1;
218462306a36Sopenharmony_ci		else if (!strncmp(this_opt, "external", 8))
218562306a36Sopenharmony_ci			external = 1;
218662306a36Sopenharmony_ci		else if (!strncmp(this_opt, "nostretch", 9))
218762306a36Sopenharmony_ci			nostretch = 1;
218862306a36Sopenharmony_ci		else if (!strncmp(this_opt, "nopciburst", 10))
218962306a36Sopenharmony_ci			nopciburst = 1;
219062306a36Sopenharmony_ci		else if (!strncmp(this_opt, "libretto", 8))
219162306a36Sopenharmony_ci			libretto = 1;
219262306a36Sopenharmony_ci		else
219362306a36Sopenharmony_ci			mode_option = this_opt;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci	return 0;
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci#endif  /*  MODULE  */
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_cistatic int __init neofb_init(void)
220062306a36Sopenharmony_ci{
220162306a36Sopenharmony_ci#ifndef MODULE
220262306a36Sopenharmony_ci	char *option = NULL;
220362306a36Sopenharmony_ci#endif
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	if (fb_modesetting_disabled("neofb"))
220662306a36Sopenharmony_ci		return -ENODEV;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci#ifndef MODULE
220962306a36Sopenharmony_ci	if (fb_get_options("neofb", &option))
221062306a36Sopenharmony_ci		return -ENODEV;
221162306a36Sopenharmony_ci	neofb_setup(option);
221262306a36Sopenharmony_ci#endif
221362306a36Sopenharmony_ci	return pci_register_driver(&neofb_driver);
221462306a36Sopenharmony_ci}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_cimodule_init(neofb_init);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci#ifdef MODULE
221962306a36Sopenharmony_cistatic void __exit neofb_exit(void)
222062306a36Sopenharmony_ci{
222162306a36Sopenharmony_ci	pci_unregister_driver(&neofb_driver);
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cimodule_exit(neofb_exit);
222562306a36Sopenharmony_ci#endif				/* MODULE */
2226