162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * tdfxfb.c
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Author: Hannu Mallat <hmallat@cc.hut.fi>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright © 1999 Hannu Mallat
962306a36Sopenharmony_ci * All rights reserved
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Created      : Thu Sep 23 18:17:43 1999, hmallat
1262306a36Sopenharmony_ci * Last modified: Tue Nov  2 21:19:47 1999, hmallat
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * I2C part copied from the i2c-voodoo3.c driver by:
1562306a36Sopenharmony_ci * Frodo Looijaard <frodol@dds.nl>,
1662306a36Sopenharmony_ci * Philip Edelbrock <phil@netroedge.com>,
1762306a36Sopenharmony_ci * Ralph Metzler <rjkm@thp.uni-koeln.de>, and
1862306a36Sopenharmony_ci * Mark D. Studebaker <mdsxyz123@yahoo.com>
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Lots of the information here comes from the Daryll Strauss' Banshee
2162306a36Sopenharmony_ci * patches to the XF86 server, and the rest comes from the 3dfx
2262306a36Sopenharmony_ci * Banshee specification. I'm very much indebted to Daryll for his
2362306a36Sopenharmony_ci * work on the X server.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * Voodoo3 support was contributed Harold Oga. Lots of additions
2662306a36Sopenharmony_ci * (proper acceleration, 24 bpp, hardware cursor) and bug fixes by Attila
2762306a36Sopenharmony_ci * Kesmarki. Thanks guys!
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Voodoo1 and Voodoo2 support aren't relevant to this driver as they
3062306a36Sopenharmony_ci * behave very differently from the Voodoo3/4/5. For anyone wanting to
3162306a36Sopenharmony_ci * use frame buffer on the Voodoo1/2, see the sstfb driver (which is
3262306a36Sopenharmony_ci * located at http://www.sourceforge.net/projects/sstfb).
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * While I _am_ grateful to 3Dfx for releasing the specs for Banshee,
3562306a36Sopenharmony_ci * I do wish the next version is a bit more complete. Without the XF86
3662306a36Sopenharmony_ci * patches I couldn't have gotten even this far... for instance, the
3762306a36Sopenharmony_ci * extensions to the VGA register set go completely unmentioned in the
3862306a36Sopenharmony_ci * spec! Also, lots of references are made to the 'SST core', but no
3962306a36Sopenharmony_ci * spec is publicly available, AFAIK.
4062306a36Sopenharmony_ci *
4162306a36Sopenharmony_ci * The structure of this driver comes pretty much from the Permedia
4262306a36Sopenharmony_ci * driver by Ilario Nardinocchi, which in turn is based on skeletonfb.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * TODO:
4562306a36Sopenharmony_ci * - multihead support (basically need to support an array of fb_infos)
4662306a36Sopenharmony_ci * - support other architectures (PPC, Alpha); does the fact that the VGA
4762306a36Sopenharmony_ci *   core can be accessed only thru I/O (not memory mapped) complicate
4862306a36Sopenharmony_ci *   things?
4962306a36Sopenharmony_ci *
5062306a36Sopenharmony_ci * Version history:
5162306a36Sopenharmony_ci *
5262306a36Sopenharmony_ci * 0.1.4 (released 2002-05-28)	ported over to new fbdev api by James Simmons
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * 0.1.3 (released 1999-11-02)	added Attila's panning support, code
5562306a36Sopenharmony_ci *				reorg, hwcursor address page size alignment
5662306a36Sopenharmony_ci *				(for mmapping both frame buffer and regs),
5762306a36Sopenharmony_ci *				and my changes to get rid of hardcoded
5862306a36Sopenharmony_ci *				VGA i/o register locations (uses PCI
5962306a36Sopenharmony_ci *				configuration info now)
6062306a36Sopenharmony_ci * 0.1.2 (released 1999-10-19)	added Attila Kesmarki's bug fixes and
6162306a36Sopenharmony_ci *				improvements
6262306a36Sopenharmony_ci * 0.1.1 (released 1999-10-07)	added Voodoo3 support by Harold Oga.
6362306a36Sopenharmony_ci * 0.1.0 (released 1999-10-06)	initial version
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#include <linux/aperture.h>
6862306a36Sopenharmony_ci#include <linux/module.h>
6962306a36Sopenharmony_ci#include <linux/kernel.h>
7062306a36Sopenharmony_ci#include <linux/errno.h>
7162306a36Sopenharmony_ci#include <linux/string.h>
7262306a36Sopenharmony_ci#include <linux/mm.h>
7362306a36Sopenharmony_ci#include <linux/slab.h>
7462306a36Sopenharmony_ci#include <linux/fb.h>
7562306a36Sopenharmony_ci#include <linux/init.h>
7662306a36Sopenharmony_ci#include <linux/pci.h>
7762306a36Sopenharmony_ci#include <asm/io.h>
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#include <video/tdfx.h>
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b)
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define BANSHEE_MAX_PIXCLOCK 270000
8462306a36Sopenharmony_ci#define VOODOO3_MAX_PIXCLOCK 300000
8562306a36Sopenharmony_ci#define VOODOO5_MAX_PIXCLOCK 350000
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic const struct fb_fix_screeninfo tdfx_fix = {
8862306a36Sopenharmony_ci	.type =		FB_TYPE_PACKED_PIXELS,
8962306a36Sopenharmony_ci	.visual =	FB_VISUAL_PSEUDOCOLOR,
9062306a36Sopenharmony_ci	.ypanstep =	1,
9162306a36Sopenharmony_ci	.ywrapstep =	1,
9262306a36Sopenharmony_ci	.accel =	FB_ACCEL_3DFX_BANSHEE
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic const struct fb_var_screeninfo tdfx_var = {
9662306a36Sopenharmony_ci	/* "640x480, 8 bpp @ 60 Hz */
9762306a36Sopenharmony_ci	.xres =		640,
9862306a36Sopenharmony_ci	.yres =		480,
9962306a36Sopenharmony_ci	.xres_virtual =	640,
10062306a36Sopenharmony_ci	.yres_virtual =	1024,
10162306a36Sopenharmony_ci	.bits_per_pixel = 8,
10262306a36Sopenharmony_ci	.red =		{0, 8, 0},
10362306a36Sopenharmony_ci	.blue =		{0, 8, 0},
10462306a36Sopenharmony_ci	.green =	{0, 8, 0},
10562306a36Sopenharmony_ci	.activate =	FB_ACTIVATE_NOW,
10662306a36Sopenharmony_ci	.height =	-1,
10762306a36Sopenharmony_ci	.width =	-1,
10862306a36Sopenharmony_ci	.accel_flags =	FB_ACCELF_TEXT,
10962306a36Sopenharmony_ci	.pixclock =	39722,
11062306a36Sopenharmony_ci	.left_margin =	40,
11162306a36Sopenharmony_ci	.right_margin =	24,
11262306a36Sopenharmony_ci	.upper_margin =	32,
11362306a36Sopenharmony_ci	.lower_margin =	11,
11462306a36Sopenharmony_ci	.hsync_len =	96,
11562306a36Sopenharmony_ci	.vsync_len =	2,
11662306a36Sopenharmony_ci	.vmode =	FB_VMODE_NONINTERLACED
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/*
12062306a36Sopenharmony_ci * PCI driver prototypes
12162306a36Sopenharmony_ci */
12262306a36Sopenharmony_cistatic int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id);
12362306a36Sopenharmony_cistatic void tdfxfb_remove(struct pci_dev *pdev);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct pci_device_id tdfxfb_id_table[] = {
12662306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_BANSHEE,
12762306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
12862306a36Sopenharmony_ci	  0xff0000, 0 },
12962306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO3,
13062306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
13162306a36Sopenharmony_ci	  0xff0000, 0 },
13262306a36Sopenharmony_ci	{ PCI_VENDOR_ID_3DFX, PCI_DEVICE_ID_3DFX_VOODOO5,
13362306a36Sopenharmony_ci	  PCI_ANY_ID, PCI_ANY_ID, PCI_BASE_CLASS_DISPLAY << 16,
13462306a36Sopenharmony_ci	  0xff0000, 0 },
13562306a36Sopenharmony_ci	{ 0, }
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic struct pci_driver tdfxfb_driver = {
13962306a36Sopenharmony_ci	.name		= "tdfxfb",
14062306a36Sopenharmony_ci	.id_table	= tdfxfb_id_table,
14162306a36Sopenharmony_ci	.probe		= tdfxfb_probe,
14262306a36Sopenharmony_ci	.remove		= tdfxfb_remove,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tdfxfb_id_table);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * Driver data
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_cistatic int nopan;
15162306a36Sopenharmony_cistatic int nowrap = 1;      /* not implemented (yet) */
15262306a36Sopenharmony_cistatic int hwcursor = 1;
15362306a36Sopenharmony_cistatic char *mode_option;
15462306a36Sopenharmony_cistatic bool nomtrr;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* -------------------------------------------------------------------------
15762306a36Sopenharmony_ci *			Hardware-specific funcions
15862306a36Sopenharmony_ci * ------------------------------------------------------------------------- */
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic inline u8 vga_inb(struct tdfx_par *par, u32 reg)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	return inb(par->iobase + reg - 0x300);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_cistatic inline void vga_outb(struct tdfx_par *par, u32 reg, u8 val)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	outb(val, par->iobase + reg - 0x300);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic inline void gra_outb(struct tdfx_par *par, u32 idx, u8 val)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	vga_outb(par, GRA_I, idx);
17362306a36Sopenharmony_ci	wmb();
17462306a36Sopenharmony_ci	vga_outb(par, GRA_D, val);
17562306a36Sopenharmony_ci	wmb();
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic inline void seq_outb(struct tdfx_par *par, u32 idx, u8 val)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	vga_outb(par, SEQ_I, idx);
18162306a36Sopenharmony_ci	wmb();
18262306a36Sopenharmony_ci	vga_outb(par, SEQ_D, val);
18362306a36Sopenharmony_ci	wmb();
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic inline u8 seq_inb(struct tdfx_par *par, u32 idx)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	vga_outb(par, SEQ_I, idx);
18962306a36Sopenharmony_ci	mb();
19062306a36Sopenharmony_ci	return vga_inb(par, SEQ_D);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic inline void crt_outb(struct tdfx_par *par, u32 idx, u8 val)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	vga_outb(par, CRT_I, idx);
19662306a36Sopenharmony_ci	wmb();
19762306a36Sopenharmony_ci	vga_outb(par, CRT_D, val);
19862306a36Sopenharmony_ci	wmb();
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic inline u8 crt_inb(struct tdfx_par *par, u32 idx)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	vga_outb(par, CRT_I, idx);
20462306a36Sopenharmony_ci	mb();
20562306a36Sopenharmony_ci	return vga_inb(par, CRT_D);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic inline void att_outb(struct tdfx_par *par, u32 idx, u8 val)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	vga_inb(par, IS1_R);
21162306a36Sopenharmony_ci	vga_outb(par, ATT_IW, idx);
21262306a36Sopenharmony_ci	vga_outb(par, ATT_IW, val);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic inline void vga_disable_video(struct tdfx_par *par)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	unsigned char s;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	s = seq_inb(par, 0x01) | 0x20;
22062306a36Sopenharmony_ci	seq_outb(par, 0x00, 0x01);
22162306a36Sopenharmony_ci	seq_outb(par, 0x01, s);
22262306a36Sopenharmony_ci	seq_outb(par, 0x00, 0x03);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistatic inline void vga_enable_video(struct tdfx_par *par)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	unsigned char s;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	s = seq_inb(par, 0x01) & 0xdf;
23062306a36Sopenharmony_ci	seq_outb(par, 0x00, 0x01);
23162306a36Sopenharmony_ci	seq_outb(par, 0x01, s);
23262306a36Sopenharmony_ci	seq_outb(par, 0x00, 0x03);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic inline void vga_enable_palette(struct tdfx_par *par)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	vga_inb(par, IS1_R);
23862306a36Sopenharmony_ci	mb();
23962306a36Sopenharmony_ci	vga_outb(par, ATT_IW, 0x20);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic inline u32 tdfx_inl(struct tdfx_par *par, unsigned int reg)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	return readl(par->regbase_virt + reg);
24562306a36Sopenharmony_ci}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic inline void tdfx_outl(struct tdfx_par *par, unsigned int reg, u32 val)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	writel(val, par->regbase_virt + reg);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic inline void banshee_make_room(struct tdfx_par *par, int size)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	/* Note: The Voodoo3's onboard FIFO has 32 slots. This loop
25562306a36Sopenharmony_ci	 * won't quit if you ask for more. */
25662306a36Sopenharmony_ci	while ((tdfx_inl(par, STATUS) & 0x1f) < size - 1)
25762306a36Sopenharmony_ci		cpu_relax();
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int banshee_wait_idle(struct fb_info *info)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
26362306a36Sopenharmony_ci	int i = 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	banshee_make_room(par, 1);
26662306a36Sopenharmony_ci	tdfx_outl(par, COMMAND_3D, COMMAND_3D_NOP);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	do {
26962306a36Sopenharmony_ci		if ((tdfx_inl(par, STATUS) & STATUS_BUSY) == 0)
27062306a36Sopenharmony_ci			i++;
27162306a36Sopenharmony_ci	} while (i < 3);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Set the color of a palette entry in 8bpp mode
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistatic inline void do_setpalentry(struct tdfx_par *par, unsigned regno, u32 c)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	banshee_make_room(par, 2);
28262306a36Sopenharmony_ci	tdfx_outl(par, DACADDR, regno);
28362306a36Sopenharmony_ci	/* read after write makes it working */
28462306a36Sopenharmony_ci	tdfx_inl(par, DACADDR);
28562306a36Sopenharmony_ci	tdfx_outl(par, DACDATA, c);
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic u32 do_calc_pll(int freq, int *freq_out)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int m, n, k, best_m, best_n, best_k, best_error;
29162306a36Sopenharmony_ci	int fref = 14318;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	best_error = freq;
29462306a36Sopenharmony_ci	best_n = best_m = best_k = 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	for (k = 3; k >= 0; k--) {
29762306a36Sopenharmony_ci		for (m = 63; m >= 0; m--) {
29862306a36Sopenharmony_ci			/*
29962306a36Sopenharmony_ci			 * Estimate value of n that produces target frequency
30062306a36Sopenharmony_ci			 * with current m and k
30162306a36Sopenharmony_ci			 */
30262306a36Sopenharmony_ci			int n_estimated = ((freq * (m + 2) << k) / fref) - 2;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci			/* Search neighborhood of estimated n */
30562306a36Sopenharmony_ci			for (n = max(0, n_estimated);
30662306a36Sopenharmony_ci				n <= min(255, n_estimated + 1);
30762306a36Sopenharmony_ci				n++) {
30862306a36Sopenharmony_ci				/*
30962306a36Sopenharmony_ci				 * Calculate PLL freqency with current m, k and
31062306a36Sopenharmony_ci				 * estimated n
31162306a36Sopenharmony_ci				 */
31262306a36Sopenharmony_ci				int f = (fref * (n + 2) / (m + 2)) >> k;
31362306a36Sopenharmony_ci				int error = abs(f - freq);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci				/*
31662306a36Sopenharmony_ci				 * If this is the closest we've come to the
31762306a36Sopenharmony_ci				 * target frequency then remember n, m and k
31862306a36Sopenharmony_ci				 */
31962306a36Sopenharmony_ci				if (error < best_error) {
32062306a36Sopenharmony_ci					best_error = error;
32162306a36Sopenharmony_ci					best_n = n;
32262306a36Sopenharmony_ci					best_m = m;
32362306a36Sopenharmony_ci					best_k = k;
32462306a36Sopenharmony_ci				}
32562306a36Sopenharmony_ci			}
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	n = best_n;
33062306a36Sopenharmony_ci	m = best_m;
33162306a36Sopenharmony_ci	k = best_k;
33262306a36Sopenharmony_ci	*freq_out = (fref * (n + 2) / (m + 2)) >> k;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return (n << 8) | (m << 2) | k;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void do_write_regs(struct fb_info *info, struct banshee_reg *reg)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
34062306a36Sopenharmony_ci	int i;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	banshee_wait_idle(info);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	tdfx_outl(par, MISCINIT1, tdfx_inl(par, MISCINIT1) | 0x01);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	crt_outb(par, 0x11, crt_inb(par, 0x11) & 0x7f); /* CRT unprotect */
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	banshee_make_room(par, 3);
34962306a36Sopenharmony_ci	tdfx_outl(par, VGAINIT1, reg->vgainit1 & 0x001FFFFF);
35062306a36Sopenharmony_ci	tdfx_outl(par, VIDPROCCFG, reg->vidcfg & ~0x00000001);
35162306a36Sopenharmony_ci#if 0
35262306a36Sopenharmony_ci	tdfx_outl(par, PLLCTRL1, reg->mempll);
35362306a36Sopenharmony_ci	tdfx_outl(par, PLLCTRL2, reg->gfxpll);
35462306a36Sopenharmony_ci#endif
35562306a36Sopenharmony_ci	tdfx_outl(par, PLLCTRL0, reg->vidpll);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	vga_outb(par, MISC_W, reg->misc[0x00] | 0x01);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	for (i = 0; i < 5; i++)
36062306a36Sopenharmony_ci		seq_outb(par, i, reg->seq[i]);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	for (i = 0; i < 25; i++)
36362306a36Sopenharmony_ci		crt_outb(par, i, reg->crt[i]);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	for (i = 0; i < 9; i++)
36662306a36Sopenharmony_ci		gra_outb(par, i, reg->gra[i]);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	for (i = 0; i < 21; i++)
36962306a36Sopenharmony_ci		att_outb(par, i, reg->att[i]);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	crt_outb(par, 0x1a, reg->ext[0]);
37262306a36Sopenharmony_ci	crt_outb(par, 0x1b, reg->ext[1]);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	vga_enable_palette(par);
37562306a36Sopenharmony_ci	vga_enable_video(par);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	banshee_make_room(par, 9);
37862306a36Sopenharmony_ci	tdfx_outl(par, VGAINIT0, reg->vgainit0);
37962306a36Sopenharmony_ci	tdfx_outl(par, DACMODE, reg->dacmode);
38062306a36Sopenharmony_ci	tdfx_outl(par, VIDDESKSTRIDE, reg->stride);
38162306a36Sopenharmony_ci	tdfx_outl(par, HWCURPATADDR, reg->curspataddr);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	tdfx_outl(par, VIDSCREENSIZE, reg->screensize);
38462306a36Sopenharmony_ci	tdfx_outl(par, VIDDESKSTART, reg->startaddr);
38562306a36Sopenharmony_ci	tdfx_outl(par, VIDPROCCFG, reg->vidcfg);
38662306a36Sopenharmony_ci	tdfx_outl(par, VGAINIT1, reg->vgainit1);
38762306a36Sopenharmony_ci	tdfx_outl(par, MISCINIT0, reg->miscinit0);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	banshee_make_room(par, 8);
39062306a36Sopenharmony_ci	tdfx_outl(par, SRCBASE, reg->startaddr);
39162306a36Sopenharmony_ci	tdfx_outl(par, DSTBASE, reg->startaddr);
39262306a36Sopenharmony_ci	tdfx_outl(par, COMMANDEXTRA_2D, 0);
39362306a36Sopenharmony_ci	tdfx_outl(par, CLIP0MIN, 0);
39462306a36Sopenharmony_ci	tdfx_outl(par, CLIP0MAX, 0x0fff0fff);
39562306a36Sopenharmony_ci	tdfx_outl(par, CLIP1MIN, 0);
39662306a36Sopenharmony_ci	tdfx_outl(par, CLIP1MAX, 0x0fff0fff);
39762306a36Sopenharmony_ci	tdfx_outl(par, SRCXY, 0);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	banshee_wait_idle(info);
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic unsigned long do_lfb_size(struct tdfx_par *par, unsigned short dev_id)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	u32 draminit0 = tdfx_inl(par, DRAMINIT0);
40562306a36Sopenharmony_ci	u32 draminit1 = tdfx_inl(par, DRAMINIT1);
40662306a36Sopenharmony_ci	u32 miscinit1;
40762306a36Sopenharmony_ci	int num_chips = (draminit0 & DRAMINIT0_SGRAM_NUM) ? 8 : 4;
40862306a36Sopenharmony_ci	int chip_size; /* in MB */
40962306a36Sopenharmony_ci	int has_sgram = draminit1 & DRAMINIT1_MEM_SDRAM;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (dev_id < PCI_DEVICE_ID_3DFX_VOODOO5) {
41262306a36Sopenharmony_ci		/* Banshee/Voodoo3 */
41362306a36Sopenharmony_ci		chip_size = 2;
41462306a36Sopenharmony_ci		if (has_sgram && !(draminit0 & DRAMINIT0_SGRAM_TYPE))
41562306a36Sopenharmony_ci			chip_size = 1;
41662306a36Sopenharmony_ci	} else {
41762306a36Sopenharmony_ci		/* Voodoo4/5 */
41862306a36Sopenharmony_ci		has_sgram = 0;
41962306a36Sopenharmony_ci		chip_size = draminit0 & DRAMINIT0_SGRAM_TYPE_MASK;
42062306a36Sopenharmony_ci		chip_size = 1 << (chip_size >> DRAMINIT0_SGRAM_TYPE_SHIFT);
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	/* disable block writes for SDRAM */
42462306a36Sopenharmony_ci	miscinit1 = tdfx_inl(par, MISCINIT1);
42562306a36Sopenharmony_ci	miscinit1 |= has_sgram ? 0 : MISCINIT1_2DBLOCK_DIS;
42662306a36Sopenharmony_ci	miscinit1 |= MISCINIT1_CLUT_INV;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	banshee_make_room(par, 1);
42962306a36Sopenharmony_ci	tdfx_outl(par, MISCINIT1, miscinit1);
43062306a36Sopenharmony_ci	return num_chips * chip_size * 1024l * 1024;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
43862306a36Sopenharmony_ci	u32 lpitch;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (var->bits_per_pixel != 8  && var->bits_per_pixel != 16 &&
44162306a36Sopenharmony_ci	    var->bits_per_pixel != 24 && var->bits_per_pixel != 32) {
44262306a36Sopenharmony_ci		DPRINTK("depth not supported: %u\n", var->bits_per_pixel);
44362306a36Sopenharmony_ci		return -EINVAL;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	if (var->xres != var->xres_virtual)
44762306a36Sopenharmony_ci		var->xres_virtual = var->xres;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (var->yres > var->yres_virtual)
45062306a36Sopenharmony_ci		var->yres_virtual = var->yres;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (var->xoffset) {
45362306a36Sopenharmony_ci		DPRINTK("xoffset not supported\n");
45462306a36Sopenharmony_ci		return -EINVAL;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	var->yoffset = 0;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	/*
45962306a36Sopenharmony_ci	 * Banshee doesn't support interlace, but Voodoo4/5 and probably
46062306a36Sopenharmony_ci	 * Voodoo3 do.
46162306a36Sopenharmony_ci	 * no direct information about device id now?
46262306a36Sopenharmony_ci	 *  use max_pixclock for this...
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	if (((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) &&
46562306a36Sopenharmony_ci	    (par->max_pixclock < VOODOO3_MAX_PIXCLOCK)) {
46662306a36Sopenharmony_ci		DPRINTK("interlace not supported\n");
46762306a36Sopenharmony_ci		return -EINVAL;
46862306a36Sopenharmony_ci	}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (info->monspecs.hfmax && info->monspecs.vfmax &&
47162306a36Sopenharmony_ci	    info->monspecs.dclkmax && fb_validate_mode(var, info) < 0) {
47262306a36Sopenharmony_ci		DPRINTK("mode outside monitor's specs\n");
47362306a36Sopenharmony_ci		return -EINVAL;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	var->xres = (var->xres + 15) & ~15; /* could sometimes be 8 */
47762306a36Sopenharmony_ci	lpitch = var->xres * ((var->bits_per_pixel + 7) >> 3);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (var->xres < 320 || var->xres > 2048) {
48062306a36Sopenharmony_ci		DPRINTK("width not supported: %u\n", var->xres);
48162306a36Sopenharmony_ci		return -EINVAL;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (var->yres < 200 || var->yres > 2048) {
48562306a36Sopenharmony_ci		DPRINTK("height not supported: %u\n", var->yres);
48662306a36Sopenharmony_ci		return -EINVAL;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (lpitch * var->yres_virtual > info->fix.smem_len) {
49062306a36Sopenharmony_ci		var->yres_virtual = info->fix.smem_len / lpitch;
49162306a36Sopenharmony_ci		if (var->yres_virtual < var->yres) {
49262306a36Sopenharmony_ci			DPRINTK("no memory for screen (%ux%ux%u)\n",
49362306a36Sopenharmony_ci				var->xres, var->yres_virtual,
49462306a36Sopenharmony_ci				var->bits_per_pixel);
49562306a36Sopenharmony_ci			return -EINVAL;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (PICOS2KHZ(var->pixclock) > par->max_pixclock) {
50062306a36Sopenharmony_ci		DPRINTK("pixclock too high (%ldKHz)\n",
50162306a36Sopenharmony_ci			PICOS2KHZ(var->pixclock));
50262306a36Sopenharmony_ci		return -EINVAL;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	var->transp.offset = 0;
50662306a36Sopenharmony_ci	var->transp.length = 0;
50762306a36Sopenharmony_ci	switch (var->bits_per_pixel) {
50862306a36Sopenharmony_ci	case 8:
50962306a36Sopenharmony_ci		var->red.length = 8;
51062306a36Sopenharmony_ci		var->red.offset = 0;
51162306a36Sopenharmony_ci		var->green = var->red;
51262306a36Sopenharmony_ci		var->blue = var->red;
51362306a36Sopenharmony_ci		break;
51462306a36Sopenharmony_ci	case 16:
51562306a36Sopenharmony_ci		var->red.offset   = 11;
51662306a36Sopenharmony_ci		var->red.length   = 5;
51762306a36Sopenharmony_ci		var->green.offset = 5;
51862306a36Sopenharmony_ci		var->green.length = 6;
51962306a36Sopenharmony_ci		var->blue.offset  = 0;
52062306a36Sopenharmony_ci		var->blue.length  = 5;
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci	case 32:
52362306a36Sopenharmony_ci		var->transp.offset = 24;
52462306a36Sopenharmony_ci		var->transp.length = 8;
52562306a36Sopenharmony_ci		fallthrough;
52662306a36Sopenharmony_ci	case 24:
52762306a36Sopenharmony_ci		var->red.offset = 16;
52862306a36Sopenharmony_ci		var->green.offset = 8;
52962306a36Sopenharmony_ci		var->blue.offset = 0;
53062306a36Sopenharmony_ci		var->red.length = var->green.length = var->blue.length = 8;
53162306a36Sopenharmony_ci		break;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	var->width = -1;
53462306a36Sopenharmony_ci	var->height = -1;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	var->accel_flags = FB_ACCELF_TEXT;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	DPRINTK("Checking graphics mode at %dx%d depth %d\n",
53962306a36Sopenharmony_ci		var->xres, var->yres, var->bits_per_pixel);
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int tdfxfb_set_par(struct fb_info *info)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
54662306a36Sopenharmony_ci	u32 hdispend = info->var.xres;
54762306a36Sopenharmony_ci	u32 hsyncsta = hdispend + info->var.right_margin;
54862306a36Sopenharmony_ci	u32 hsyncend = hsyncsta + info->var.hsync_len;
54962306a36Sopenharmony_ci	u32 htotal   = hsyncend + info->var.left_margin;
55062306a36Sopenharmony_ci	u32 hd, hs, he, ht, hbs, hbe;
55162306a36Sopenharmony_ci	u32 vd, vs, ve, vt, vbs, vbe;
55262306a36Sopenharmony_ci	struct banshee_reg reg;
55362306a36Sopenharmony_ci	int fout, freq;
55462306a36Sopenharmony_ci	u32 wd;
55562306a36Sopenharmony_ci	u32 cpp = (info->var.bits_per_pixel + 7) >> 3;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	memset(&reg, 0, sizeof(reg));
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	reg.vidcfg = VIDCFG_VIDPROC_ENABLE | VIDCFG_DESK_ENABLE |
56062306a36Sopenharmony_ci		     VIDCFG_CURS_X11 |
56162306a36Sopenharmony_ci		     ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) |
56262306a36Sopenharmony_ci		     (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	/* PLL settings */
56562306a36Sopenharmony_ci	freq = PICOS2KHZ(info->var.pixclock);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	reg.vidcfg &= ~VIDCFG_2X;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (freq > par->max_pixclock / 2) {
57062306a36Sopenharmony_ci		freq = freq > par->max_pixclock ? par->max_pixclock : freq;
57162306a36Sopenharmony_ci		reg.dacmode |= DACMODE_2X;
57262306a36Sopenharmony_ci		reg.vidcfg  |= VIDCFG_2X;
57362306a36Sopenharmony_ci		hdispend >>= 1;
57462306a36Sopenharmony_ci		hsyncsta >>= 1;
57562306a36Sopenharmony_ci		hsyncend >>= 1;
57662306a36Sopenharmony_ci		htotal   >>= 1;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	wd = (hdispend >> 3) - 1;
58062306a36Sopenharmony_ci	hd  = wd;
58162306a36Sopenharmony_ci	hs  = (hsyncsta >> 3) - 1;
58262306a36Sopenharmony_ci	he  = (hsyncend >> 3) - 1;
58362306a36Sopenharmony_ci	ht  = (htotal >> 3) - 1;
58462306a36Sopenharmony_ci	hbs = hd;
58562306a36Sopenharmony_ci	hbe = ht;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) {
58862306a36Sopenharmony_ci		vd = (info->var.yres << 1) - 1;
58962306a36Sopenharmony_ci		vs  = vd + (info->var.lower_margin << 1);
59062306a36Sopenharmony_ci		ve  = vs + (info->var.vsync_len << 1);
59162306a36Sopenharmony_ci		vt = ve + (info->var.upper_margin << 1) - 1;
59262306a36Sopenharmony_ci		reg.screensize = info->var.xres | (info->var.yres << 13);
59362306a36Sopenharmony_ci		reg.vidcfg |= VIDCFG_HALF_MODE;
59462306a36Sopenharmony_ci		reg.crt[0x09] = 0x80;
59562306a36Sopenharmony_ci	} else {
59662306a36Sopenharmony_ci		vd = info->var.yres - 1;
59762306a36Sopenharmony_ci		vs  = vd + info->var.lower_margin;
59862306a36Sopenharmony_ci		ve  = vs + info->var.vsync_len;
59962306a36Sopenharmony_ci		vt = ve + info->var.upper_margin - 1;
60062306a36Sopenharmony_ci		reg.screensize = info->var.xres | (info->var.yres << 12);
60162306a36Sopenharmony_ci		reg.vidcfg &= ~VIDCFG_HALF_MODE;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci	vbs = vd;
60462306a36Sopenharmony_ci	vbe = vt;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	/* this is all pretty standard VGA register stuffing */
60762306a36Sopenharmony_ci	reg.misc[0x00] = 0x0f |
60862306a36Sopenharmony_ci			(info->var.xres < 400 ? 0xa0 :
60962306a36Sopenharmony_ci			 info->var.xres < 480 ? 0x60 :
61062306a36Sopenharmony_ci			 info->var.xres < 768 ? 0xe0 : 0x20);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	reg.gra[0x05] = 0x40;
61362306a36Sopenharmony_ci	reg.gra[0x06] = 0x05;
61462306a36Sopenharmony_ci	reg.gra[0x07] = 0x0f;
61562306a36Sopenharmony_ci	reg.gra[0x08] = 0xff;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	reg.att[0x00] = 0x00;
61862306a36Sopenharmony_ci	reg.att[0x01] = 0x01;
61962306a36Sopenharmony_ci	reg.att[0x02] = 0x02;
62062306a36Sopenharmony_ci	reg.att[0x03] = 0x03;
62162306a36Sopenharmony_ci	reg.att[0x04] = 0x04;
62262306a36Sopenharmony_ci	reg.att[0x05] = 0x05;
62362306a36Sopenharmony_ci	reg.att[0x06] = 0x06;
62462306a36Sopenharmony_ci	reg.att[0x07] = 0x07;
62562306a36Sopenharmony_ci	reg.att[0x08] = 0x08;
62662306a36Sopenharmony_ci	reg.att[0x09] = 0x09;
62762306a36Sopenharmony_ci	reg.att[0x0a] = 0x0a;
62862306a36Sopenharmony_ci	reg.att[0x0b] = 0x0b;
62962306a36Sopenharmony_ci	reg.att[0x0c] = 0x0c;
63062306a36Sopenharmony_ci	reg.att[0x0d] = 0x0d;
63162306a36Sopenharmony_ci	reg.att[0x0e] = 0x0e;
63262306a36Sopenharmony_ci	reg.att[0x0f] = 0x0f;
63362306a36Sopenharmony_ci	reg.att[0x10] = 0x41;
63462306a36Sopenharmony_ci	reg.att[0x12] = 0x0f;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	reg.seq[0x00] = 0x03;
63762306a36Sopenharmony_ci	reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */
63862306a36Sopenharmony_ci	reg.seq[0x02] = 0x0f;
63962306a36Sopenharmony_ci	reg.seq[0x03] = 0x00;
64062306a36Sopenharmony_ci	reg.seq[0x04] = 0x0e;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	reg.crt[0x00] = ht - 4;
64362306a36Sopenharmony_ci	reg.crt[0x01] = hd;
64462306a36Sopenharmony_ci	reg.crt[0x02] = hbs;
64562306a36Sopenharmony_ci	reg.crt[0x03] = 0x80 | (hbe & 0x1f);
64662306a36Sopenharmony_ci	reg.crt[0x04] = hs;
64762306a36Sopenharmony_ci	reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f);
64862306a36Sopenharmony_ci	reg.crt[0x06] = vt;
64962306a36Sopenharmony_ci	reg.crt[0x07] = ((vs & 0x200) >> 2) |
65062306a36Sopenharmony_ci			((vd & 0x200) >> 3) |
65162306a36Sopenharmony_ci			((vt & 0x200) >> 4) | 0x10 |
65262306a36Sopenharmony_ci			((vbs & 0x100) >> 5) |
65362306a36Sopenharmony_ci			((vs & 0x100) >> 6) |
65462306a36Sopenharmony_ci			((vd & 0x100) >> 7) |
65562306a36Sopenharmony_ci			((vt & 0x100) >> 8);
65662306a36Sopenharmony_ci	reg.crt[0x09] |= 0x40 | ((vbs & 0x200) >> 4);
65762306a36Sopenharmony_ci	reg.crt[0x10] = vs;
65862306a36Sopenharmony_ci	reg.crt[0x11] = (ve & 0x0f) | 0x20;
65962306a36Sopenharmony_ci	reg.crt[0x12] = vd;
66062306a36Sopenharmony_ci	reg.crt[0x13] = wd;
66162306a36Sopenharmony_ci	reg.crt[0x15] = vbs;
66262306a36Sopenharmony_ci	reg.crt[0x16] = vbe + 1;
66362306a36Sopenharmony_ci	reg.crt[0x17] = 0xc3;
66462306a36Sopenharmony_ci	reg.crt[0x18] = 0xff;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* Banshee's nonvga stuff */
66762306a36Sopenharmony_ci	reg.ext[0x00] = (((ht & 0x100) >> 8) |
66862306a36Sopenharmony_ci			((hd & 0x100) >> 6) |
66962306a36Sopenharmony_ci			((hbs & 0x100) >> 4) |
67062306a36Sopenharmony_ci			((hbe & 0x40) >> 1) |
67162306a36Sopenharmony_ci			((hs & 0x100) >> 2) |
67262306a36Sopenharmony_ci			((he & 0x20) << 2));
67362306a36Sopenharmony_ci	reg.ext[0x01] = (((vt & 0x400) >> 10) |
67462306a36Sopenharmony_ci			((vd & 0x400) >> 8) |
67562306a36Sopenharmony_ci			((vbs & 0x400) >> 6) |
67662306a36Sopenharmony_ci			((vbe & 0x400) >> 4));
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	reg.vgainit0 =	VGAINIT0_8BIT_DAC     |
67962306a36Sopenharmony_ci			VGAINIT0_EXT_ENABLE   |
68062306a36Sopenharmony_ci			VGAINIT0_WAKEUP_3C3   |
68162306a36Sopenharmony_ci			VGAINIT0_ALT_READBACK |
68262306a36Sopenharmony_ci			VGAINIT0_EXTSHIFTOUT;
68362306a36Sopenharmony_ci	reg.vgainit1 = tdfx_inl(par, VGAINIT1) & 0x1fffff;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (hwcursor)
68662306a36Sopenharmony_ci		reg.curspataddr = info->fix.smem_len;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	reg.cursloc   = 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	reg.cursc0    = 0;
69162306a36Sopenharmony_ci	reg.cursc1    = 0xffffff;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	reg.stride    = info->var.xres * cpp;
69462306a36Sopenharmony_ci	reg.startaddr = info->var.yoffset * reg.stride
69562306a36Sopenharmony_ci			+ info->var.xoffset * cpp;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	reg.vidpll = do_calc_pll(freq, &fout);
69862306a36Sopenharmony_ci#if 0
69962306a36Sopenharmony_ci	reg.mempll = do_calc_pll(..., &fout);
70062306a36Sopenharmony_ci	reg.gfxpll = do_calc_pll(..., &fout);
70162306a36Sopenharmony_ci#endif
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if ((info->var.vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
70462306a36Sopenharmony_ci		reg.vidcfg |= VIDCFG_INTERLACE;
70562306a36Sopenharmony_ci	reg.miscinit0 = tdfx_inl(par, MISCINIT0);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci#if defined(__BIG_ENDIAN)
70862306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
70962306a36Sopenharmony_ci	case 8:
71062306a36Sopenharmony_ci	case 24:
71162306a36Sopenharmony_ci		reg.miscinit0 &= ~(1 << 30);
71262306a36Sopenharmony_ci		reg.miscinit0 &= ~(1 << 31);
71362306a36Sopenharmony_ci		break;
71462306a36Sopenharmony_ci	case 16:
71562306a36Sopenharmony_ci		reg.miscinit0 |= (1 << 30);
71662306a36Sopenharmony_ci		reg.miscinit0 |= (1 << 31);
71762306a36Sopenharmony_ci		break;
71862306a36Sopenharmony_ci	case 32:
71962306a36Sopenharmony_ci		reg.miscinit0 |= (1 << 30);
72062306a36Sopenharmony_ci		reg.miscinit0 &= ~(1 << 31);
72162306a36Sopenharmony_ci		break;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci#endif
72462306a36Sopenharmony_ci	do_write_regs(info, &reg);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* Now change fb_fix_screeninfo according to changes in par */
72762306a36Sopenharmony_ci	info->fix.line_length = reg.stride;
72862306a36Sopenharmony_ci	info->fix.visual = (info->var.bits_per_pixel == 8)
72962306a36Sopenharmony_ci				? FB_VISUAL_PSEUDOCOLOR
73062306a36Sopenharmony_ci				: FB_VISUAL_TRUECOLOR;
73162306a36Sopenharmony_ci	DPRINTK("Graphics mode is now set at %dx%d depth %d\n",
73262306a36Sopenharmony_ci		info->var.xres, info->var.yres, info->var.bits_per_pixel);
73362306a36Sopenharmony_ci	return 0;
73462306a36Sopenharmony_ci}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci/* A handy macro shamelessly pinched from matroxfb */
73762306a36Sopenharmony_ci#define CNVT_TOHW(val, width) ((((val) << (width)) + 0x7FFF - (val)) >> 16)
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int tdfxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
74062306a36Sopenharmony_ci			    unsigned blue, unsigned transp,
74162306a36Sopenharmony_ci			    struct fb_info *info)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
74462306a36Sopenharmony_ci	u32 rgbcol;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	if (regno >= info->cmap.len || regno > 255)
74762306a36Sopenharmony_ci		return 1;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	/* grayscale works only partially under directcolor */
75062306a36Sopenharmony_ci	if (info->var.grayscale) {
75162306a36Sopenharmony_ci		/* grayscale = 0.30*R + 0.59*G + 0.11*B */
75262306a36Sopenharmony_ci		blue = (red * 77 + green * 151 + blue * 28) >> 8;
75362306a36Sopenharmony_ci		green = blue;
75462306a36Sopenharmony_ci		red = blue;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	switch (info->fix.visual) {
75862306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
75962306a36Sopenharmony_ci		rgbcol = (((u32)red   & 0xff00) << 8) |
76062306a36Sopenharmony_ci			 (((u32)green & 0xff00) << 0) |
76162306a36Sopenharmony_ci			 (((u32)blue  & 0xff00) >> 8);
76262306a36Sopenharmony_ci		do_setpalentry(par, regno, rgbcol);
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci	/* Truecolor has no hardware color palettes. */
76562306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
76662306a36Sopenharmony_ci		if (regno < 16) {
76762306a36Sopenharmony_ci			rgbcol = (CNVT_TOHW(red, info->var.red.length) <<
76862306a36Sopenharmony_ci				  info->var.red.offset) |
76962306a36Sopenharmony_ci				(CNVT_TOHW(green, info->var.green.length) <<
77062306a36Sopenharmony_ci				 info->var.green.offset) |
77162306a36Sopenharmony_ci				(CNVT_TOHW(blue, info->var.blue.length) <<
77262306a36Sopenharmony_ci				 info->var.blue.offset) |
77362306a36Sopenharmony_ci				(CNVT_TOHW(transp, info->var.transp.length) <<
77462306a36Sopenharmony_ci				 info->var.transp.offset);
77562306a36Sopenharmony_ci			par->palette[regno] = rgbcol;
77662306a36Sopenharmony_ci		}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci		break;
77962306a36Sopenharmony_ci	default:
78062306a36Sopenharmony_ci		DPRINTK("bad depth %u\n", info->var.bits_per_pixel);
78162306a36Sopenharmony_ci		break;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	return 0;
78562306a36Sopenharmony_ci}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
78862306a36Sopenharmony_cistatic int tdfxfb_blank(int blank, struct fb_info *info)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
79162306a36Sopenharmony_ci	int vgablank = 1;
79262306a36Sopenharmony_ci	u32 dacmode = tdfx_inl(par, DACMODE);
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	dacmode &= ~(BIT(1) | BIT(3));
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	switch (blank) {
79762306a36Sopenharmony_ci	case FB_BLANK_UNBLANK: /* Screen: On; HSync: On, VSync: On */
79862306a36Sopenharmony_ci		vgablank = 0;
79962306a36Sopenharmony_ci		break;
80062306a36Sopenharmony_ci	case FB_BLANK_NORMAL: /* Screen: Off; HSync: On, VSync: On */
80162306a36Sopenharmony_ci		break;
80262306a36Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND: /* Screen: Off; HSync: On, VSync: Off */
80362306a36Sopenharmony_ci		dacmode |= BIT(3);
80462306a36Sopenharmony_ci		break;
80562306a36Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND: /* Screen: Off; HSync: Off, VSync: On */
80662306a36Sopenharmony_ci		dacmode |= BIT(1);
80762306a36Sopenharmony_ci		break;
80862306a36Sopenharmony_ci	case FB_BLANK_POWERDOWN: /* Screen: Off; HSync: Off, VSync: Off */
80962306a36Sopenharmony_ci		dacmode |= BIT(1) | BIT(3);
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	banshee_make_room(par, 1);
81462306a36Sopenharmony_ci	tdfx_outl(par, DACMODE, dacmode);
81562306a36Sopenharmony_ci	if (vgablank)
81662306a36Sopenharmony_ci		vga_disable_video(par);
81762306a36Sopenharmony_ci	else
81862306a36Sopenharmony_ci		vga_enable_video(par);
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/*
82362306a36Sopenharmony_ci * Set the starting position of the visible screen to var->yoffset
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_cistatic int tdfxfb_pan_display(struct fb_var_screeninfo *var,
82662306a36Sopenharmony_ci			      struct fb_info *info)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
82962306a36Sopenharmony_ci	u32 addr = var->yoffset * info->fix.line_length;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (nopan || var->xoffset)
83262306a36Sopenharmony_ci		return -EINVAL;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	banshee_make_room(par, 1);
83562306a36Sopenharmony_ci	tdfx_outl(par, VIDDESKSTART, addr);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return 0;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_ACCEL
84162306a36Sopenharmony_ci/*
84262306a36Sopenharmony_ci * FillRect 2D command (solidfill or invert (via ROP_XOR))
84362306a36Sopenharmony_ci */
84462306a36Sopenharmony_cistatic void tdfxfb_fillrect(struct fb_info *info,
84562306a36Sopenharmony_ci			    const struct fb_fillrect *rect)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
84862306a36Sopenharmony_ci	u32 bpp = info->var.bits_per_pixel;
84962306a36Sopenharmony_ci	u32 stride = info->fix.line_length;
85062306a36Sopenharmony_ci	u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);
85162306a36Sopenharmony_ci	int tdfx_rop;
85262306a36Sopenharmony_ci	u32 dx = rect->dx;
85362306a36Sopenharmony_ci	u32 dy = rect->dy;
85462306a36Sopenharmony_ci	u32 dstbase = 0;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	if (rect->rop == ROP_COPY)
85762306a36Sopenharmony_ci		tdfx_rop = TDFX_ROP_COPY;
85862306a36Sopenharmony_ci	else
85962306a36Sopenharmony_ci		tdfx_rop = TDFX_ROP_XOR;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* assume always rect->height < 4096 */
86262306a36Sopenharmony_ci	if (dy + rect->height > 4095) {
86362306a36Sopenharmony_ci		dstbase = stride * dy;
86462306a36Sopenharmony_ci		dy = 0;
86562306a36Sopenharmony_ci	}
86662306a36Sopenharmony_ci	/* assume always rect->width < 4096 */
86762306a36Sopenharmony_ci	if (dx + rect->width > 4095) {
86862306a36Sopenharmony_ci		dstbase += dx * bpp >> 3;
86962306a36Sopenharmony_ci		dx = 0;
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	banshee_make_room(par, 6);
87262306a36Sopenharmony_ci	tdfx_outl(par, DSTFORMAT, fmt);
87362306a36Sopenharmony_ci	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR) {
87462306a36Sopenharmony_ci		tdfx_outl(par, COLORFORE, rect->color);
87562306a36Sopenharmony_ci	} else { /* FB_VISUAL_TRUECOLOR */
87662306a36Sopenharmony_ci		tdfx_outl(par, COLORFORE, par->palette[rect->color]);
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci	tdfx_outl(par, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24));
87962306a36Sopenharmony_ci	tdfx_outl(par, DSTBASE, dstbase);
88062306a36Sopenharmony_ci	tdfx_outl(par, DSTSIZE, rect->width | (rect->height << 16));
88162306a36Sopenharmony_ci	tdfx_outl(par, LAUNCH_2D, dx | (dy << 16));
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci/*
88562306a36Sopenharmony_ci * Screen-to-Screen BitBlt 2D command (for the bmove fb op.)
88662306a36Sopenharmony_ci */
88762306a36Sopenharmony_cistatic void tdfxfb_copyarea(struct fb_info *info,
88862306a36Sopenharmony_ci			    const struct fb_copyarea *area)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
89162306a36Sopenharmony_ci	u32 sx = area->sx, sy = area->sy, dx = area->dx, dy = area->dy;
89262306a36Sopenharmony_ci	u32 bpp = info->var.bits_per_pixel;
89362306a36Sopenharmony_ci	u32 stride = info->fix.line_length;
89462306a36Sopenharmony_ci	u32 blitcmd = COMMAND_2D_S2S_BITBLT | (TDFX_ROP_COPY << 24);
89562306a36Sopenharmony_ci	u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);
89662306a36Sopenharmony_ci	u32 dstbase = 0;
89762306a36Sopenharmony_ci	u32 srcbase = 0;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* assume always area->height < 4096 */
90062306a36Sopenharmony_ci	if (sy + area->height > 4095) {
90162306a36Sopenharmony_ci		srcbase = stride * sy;
90262306a36Sopenharmony_ci		sy = 0;
90362306a36Sopenharmony_ci	}
90462306a36Sopenharmony_ci	/* assume always area->width < 4096 */
90562306a36Sopenharmony_ci	if (sx + area->width > 4095) {
90662306a36Sopenharmony_ci		srcbase += sx * bpp >> 3;
90762306a36Sopenharmony_ci		sx = 0;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci	/* assume always area->height < 4096 */
91062306a36Sopenharmony_ci	if (dy + area->height > 4095) {
91162306a36Sopenharmony_ci		dstbase = stride * dy;
91262306a36Sopenharmony_ci		dy = 0;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci	/* assume always area->width < 4096 */
91562306a36Sopenharmony_ci	if (dx + area->width > 4095) {
91662306a36Sopenharmony_ci		dstbase += dx * bpp >> 3;
91762306a36Sopenharmony_ci		dx = 0;
91862306a36Sopenharmony_ci	}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (area->sx <= area->dx) {
92162306a36Sopenharmony_ci		/* -X */
92262306a36Sopenharmony_ci		blitcmd |= BIT(14);
92362306a36Sopenharmony_ci		sx += area->width - 1;
92462306a36Sopenharmony_ci		dx += area->width - 1;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci	if (area->sy <= area->dy) {
92762306a36Sopenharmony_ci		/* -Y */
92862306a36Sopenharmony_ci		blitcmd |= BIT(15);
92962306a36Sopenharmony_ci		sy += area->height - 1;
93062306a36Sopenharmony_ci		dy += area->height - 1;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	banshee_make_room(par, 8);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	tdfx_outl(par, SRCFORMAT, fmt);
93662306a36Sopenharmony_ci	tdfx_outl(par, DSTFORMAT, fmt);
93762306a36Sopenharmony_ci	tdfx_outl(par, COMMAND_2D, blitcmd);
93862306a36Sopenharmony_ci	tdfx_outl(par, DSTSIZE, area->width | (area->height << 16));
93962306a36Sopenharmony_ci	tdfx_outl(par, DSTXY, dx | (dy << 16));
94062306a36Sopenharmony_ci	tdfx_outl(par, SRCBASE, srcbase);
94162306a36Sopenharmony_ci	tdfx_outl(par, DSTBASE, dstbase);
94262306a36Sopenharmony_ci	tdfx_outl(par, LAUNCH_2D, sx | (sy << 16));
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
94862306a36Sopenharmony_ci	int size = image->height * ((image->width * image->depth + 7) >> 3);
94962306a36Sopenharmony_ci	int fifo_free;
95062306a36Sopenharmony_ci	int i, stride = info->fix.line_length;
95162306a36Sopenharmony_ci	u32 bpp = info->var.bits_per_pixel;
95262306a36Sopenharmony_ci	u32 dstfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13);
95362306a36Sopenharmony_ci	u8 *chardata = (u8 *) image->data;
95462306a36Sopenharmony_ci	u32 srcfmt;
95562306a36Sopenharmony_ci	u32 dx = image->dx;
95662306a36Sopenharmony_ci	u32 dy = image->dy;
95762306a36Sopenharmony_ci	u32 dstbase = 0;
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (image->depth != 1) {
96062306a36Sopenharmony_ci#ifdef BROKEN_CODE
96162306a36Sopenharmony_ci		banshee_make_room(par, 6 + ((size + 3) >> 2));
96262306a36Sopenharmony_ci		srcfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13) |
96362306a36Sopenharmony_ci			0x400000;
96462306a36Sopenharmony_ci#else
96562306a36Sopenharmony_ci		cfb_imageblit(info, image);
96662306a36Sopenharmony_ci#endif
96762306a36Sopenharmony_ci		return;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci	banshee_make_room(par, 9);
97062306a36Sopenharmony_ci	switch (info->fix.visual) {
97162306a36Sopenharmony_ci	case FB_VISUAL_PSEUDOCOLOR:
97262306a36Sopenharmony_ci		tdfx_outl(par, COLORFORE, image->fg_color);
97362306a36Sopenharmony_ci		tdfx_outl(par, COLORBACK, image->bg_color);
97462306a36Sopenharmony_ci		break;
97562306a36Sopenharmony_ci	case FB_VISUAL_TRUECOLOR:
97662306a36Sopenharmony_ci	default:
97762306a36Sopenharmony_ci		tdfx_outl(par, COLORFORE,
97862306a36Sopenharmony_ci			  par->palette[image->fg_color]);
97962306a36Sopenharmony_ci		tdfx_outl(par, COLORBACK,
98062306a36Sopenharmony_ci			  par->palette[image->bg_color]);
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
98362306a36Sopenharmony_ci	srcfmt = 0x400000 | BIT(20);
98462306a36Sopenharmony_ci#else
98562306a36Sopenharmony_ci	srcfmt = 0x400000;
98662306a36Sopenharmony_ci#endif
98762306a36Sopenharmony_ci	/* assume always image->height < 4096 */
98862306a36Sopenharmony_ci	if (dy + image->height > 4095) {
98962306a36Sopenharmony_ci		dstbase = stride * dy;
99062306a36Sopenharmony_ci		dy = 0;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci	/* assume always image->width < 4096 */
99362306a36Sopenharmony_ci	if (dx + image->width > 4095) {
99462306a36Sopenharmony_ci		dstbase += dx * bpp >> 3;
99562306a36Sopenharmony_ci		dx = 0;
99662306a36Sopenharmony_ci	}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	tdfx_outl(par, DSTBASE, dstbase);
99962306a36Sopenharmony_ci	tdfx_outl(par, SRCXY, 0);
100062306a36Sopenharmony_ci	tdfx_outl(par, DSTXY, dx | (dy << 16));
100162306a36Sopenharmony_ci	tdfx_outl(par, COMMAND_2D,
100262306a36Sopenharmony_ci		  COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24));
100362306a36Sopenharmony_ci	tdfx_outl(par, SRCFORMAT, srcfmt);
100462306a36Sopenharmony_ci	tdfx_outl(par, DSTFORMAT, dstfmt);
100562306a36Sopenharmony_ci	tdfx_outl(par, DSTSIZE, image->width | (image->height << 16));
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	/* A count of how many free FIFO entries we've requested.
100862306a36Sopenharmony_ci	 * When this goes negative, we need to request more. */
100962306a36Sopenharmony_ci	fifo_free = 0;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	/* Send four bytes at a time of data */
101262306a36Sopenharmony_ci	for (i = (size >> 2); i > 0; i--) {
101362306a36Sopenharmony_ci		if (--fifo_free < 0) {
101462306a36Sopenharmony_ci			fifo_free = 31;
101562306a36Sopenharmony_ci			banshee_make_room(par, fifo_free);
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci		tdfx_outl(par, LAUNCH_2D, *(u32 *)chardata);
101862306a36Sopenharmony_ci		chardata += 4;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/* Send the leftovers now */
102262306a36Sopenharmony_ci	banshee_make_room(par, 3);
102362306a36Sopenharmony_ci	switch (size % 4) {
102462306a36Sopenharmony_ci	case 0:
102562306a36Sopenharmony_ci		break;
102662306a36Sopenharmony_ci	case 1:
102762306a36Sopenharmony_ci		tdfx_outl(par, LAUNCH_2D, *chardata);
102862306a36Sopenharmony_ci		break;
102962306a36Sopenharmony_ci	case 2:
103062306a36Sopenharmony_ci		tdfx_outl(par, LAUNCH_2D, *(u16 *)chardata);
103162306a36Sopenharmony_ci		break;
103262306a36Sopenharmony_ci	case 3:
103362306a36Sopenharmony_ci		tdfx_outl(par, LAUNCH_2D,
103462306a36Sopenharmony_ci			*(u16 *)chardata | (chardata[3] << 24));
103562306a36Sopenharmony_ci		break;
103662306a36Sopenharmony_ci	}
103762306a36Sopenharmony_ci}
103862306a36Sopenharmony_ci#endif /* CONFIG_FB_3DFX_ACCEL */
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
104162306a36Sopenharmony_ci{
104262306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
104362306a36Sopenharmony_ci	u32 vidcfg;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	if (!hwcursor)
104662306a36Sopenharmony_ci		return -EINVAL;	/* just to force soft_cursor() call */
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	/* Too large of a cursor or wrong bpp :-( */
104962306a36Sopenharmony_ci	if (cursor->image.width > 64 ||
105062306a36Sopenharmony_ci	    cursor->image.height > 64 ||
105162306a36Sopenharmony_ci	    cursor->image.depth > 1)
105262306a36Sopenharmony_ci		return -EINVAL;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	vidcfg = tdfx_inl(par, VIDPROCCFG);
105562306a36Sopenharmony_ci	if (cursor->enable)
105662306a36Sopenharmony_ci		tdfx_outl(par, VIDPROCCFG, vidcfg | VIDCFG_HWCURSOR_ENABLE);
105762306a36Sopenharmony_ci	else
105862306a36Sopenharmony_ci		tdfx_outl(par, VIDPROCCFG, vidcfg & ~VIDCFG_HWCURSOR_ENABLE);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/*
106162306a36Sopenharmony_ci	 * If the cursor is not be changed this means either we want the
106262306a36Sopenharmony_ci	 * current cursor state (if enable is set) or we want to query what
106362306a36Sopenharmony_ci	 * we can do with the cursor (if enable is not set)
106462306a36Sopenharmony_ci	 */
106562306a36Sopenharmony_ci	if (!cursor->set)
106662306a36Sopenharmony_ci		return 0;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	/* fix cursor color - XFree86 forgets to restore it properly */
106962306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETCMAP) {
107062306a36Sopenharmony_ci		struct fb_cmap cmap = info->cmap;
107162306a36Sopenharmony_ci		u32 bg_idx = cursor->image.bg_color;
107262306a36Sopenharmony_ci		u32 fg_idx = cursor->image.fg_color;
107362306a36Sopenharmony_ci		unsigned long bg_color, fg_color;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci		fg_color = (((u32)cmap.red[fg_idx]   & 0xff00) << 8) |
107662306a36Sopenharmony_ci			   (((u32)cmap.green[fg_idx] & 0xff00) << 0) |
107762306a36Sopenharmony_ci			   (((u32)cmap.blue[fg_idx]  & 0xff00) >> 8);
107862306a36Sopenharmony_ci		bg_color = (((u32)cmap.red[bg_idx]   & 0xff00) << 8) |
107962306a36Sopenharmony_ci			   (((u32)cmap.green[bg_idx] & 0xff00) << 0) |
108062306a36Sopenharmony_ci			   (((u32)cmap.blue[bg_idx]  & 0xff00) >> 8);
108162306a36Sopenharmony_ci		banshee_make_room(par, 2);
108262306a36Sopenharmony_ci		tdfx_outl(par, HWCURC0, bg_color);
108362306a36Sopenharmony_ci		tdfx_outl(par, HWCURC1, fg_color);
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (cursor->set & FB_CUR_SETPOS) {
108762306a36Sopenharmony_ci		int x = cursor->image.dx;
108862306a36Sopenharmony_ci		int y = cursor->image.dy - info->var.yoffset;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		x += 63;
109162306a36Sopenharmony_ci		y += 63;
109262306a36Sopenharmony_ci		banshee_make_room(par, 1);
109362306a36Sopenharmony_ci		tdfx_outl(par, HWCURLOC, (y << 16) + x);
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci	if (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) {
109662306a36Sopenharmony_ci		/*
109762306a36Sopenharmony_ci		 * Voodoo 3 and above cards use 2 monochrome cursor patterns.
109862306a36Sopenharmony_ci		 *    The reason is so the card can fetch 8 words at a time
109962306a36Sopenharmony_ci		 * and are stored on chip for use for the next 8 scanlines.
110062306a36Sopenharmony_ci		 * This reduces the number of times for access to draw the
110162306a36Sopenharmony_ci		 * cursor for each screen refresh.
110262306a36Sopenharmony_ci		 *    Each pattern is a bitmap of 64 bit wide and 64 bit high
110362306a36Sopenharmony_ci		 * (total of 8192 bits or 1024 bytes). The two patterns are
110462306a36Sopenharmony_ci		 * stored in such a way that pattern 0 always resides in the
110562306a36Sopenharmony_ci		 * lower half (least significant 64 bits) of a 128 bit word
110662306a36Sopenharmony_ci		 * and pattern 1 the upper half. If you examine the data of
110762306a36Sopenharmony_ci		 * the cursor image the graphics card uses then from the
110862306a36Sopenharmony_ci		 * beginning you see line one of pattern 0, line one of
110962306a36Sopenharmony_ci		 * pattern 1, line two of pattern 0, line two of pattern 1,
111062306a36Sopenharmony_ci		 * etc etc. The linear stride for the cursor is always 16 bytes
111162306a36Sopenharmony_ci		 * (128 bits) which is the maximum cursor width times two for
111262306a36Sopenharmony_ci		 * the two monochrome patterns.
111362306a36Sopenharmony_ci		 */
111462306a36Sopenharmony_ci		u8 __iomem *cursorbase = info->screen_base + info->fix.smem_len;
111562306a36Sopenharmony_ci		u8 *bitmap = (u8 *)cursor->image.data;
111662306a36Sopenharmony_ci		u8 *mask = (u8 *)cursor->mask;
111762306a36Sopenharmony_ci		int i;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		fb_memset_io(cursorbase, 0, 1024);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		for (i = 0; i < cursor->image.height; i++) {
112262306a36Sopenharmony_ci			int h = 0;
112362306a36Sopenharmony_ci			int j = (cursor->image.width + 7) >> 3;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci			for (; j > 0; j--) {
112662306a36Sopenharmony_ci				u8 data = *mask ^ *bitmap;
112762306a36Sopenharmony_ci				if (cursor->rop == ROP_COPY)
112862306a36Sopenharmony_ci					data = *mask & *bitmap;
112962306a36Sopenharmony_ci				/* Pattern 0. Copy the cursor mask to it */
113062306a36Sopenharmony_ci				fb_writeb(*mask, cursorbase + h);
113162306a36Sopenharmony_ci				mask++;
113262306a36Sopenharmony_ci				/* Pattern 1. Copy the cursor bitmap to it */
113362306a36Sopenharmony_ci				fb_writeb(data, cursorbase + h + 8);
113462306a36Sopenharmony_ci				bitmap++;
113562306a36Sopenharmony_ci				h++;
113662306a36Sopenharmony_ci			}
113762306a36Sopenharmony_ci			cursorbase += 16;
113862306a36Sopenharmony_ci		}
113962306a36Sopenharmony_ci	}
114062306a36Sopenharmony_ci	return 0;
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic const struct fb_ops tdfxfb_ops = {
114462306a36Sopenharmony_ci	.owner		= THIS_MODULE,
114562306a36Sopenharmony_ci	.fb_check_var	= tdfxfb_check_var,
114662306a36Sopenharmony_ci	.fb_set_par	= tdfxfb_set_par,
114762306a36Sopenharmony_ci	.fb_setcolreg	= tdfxfb_setcolreg,
114862306a36Sopenharmony_ci	.fb_blank	= tdfxfb_blank,
114962306a36Sopenharmony_ci	.fb_pan_display	= tdfxfb_pan_display,
115062306a36Sopenharmony_ci	.fb_sync	= banshee_wait_idle,
115162306a36Sopenharmony_ci	.fb_cursor	= tdfxfb_cursor,
115262306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_ACCEL
115362306a36Sopenharmony_ci	.fb_fillrect	= tdfxfb_fillrect,
115462306a36Sopenharmony_ci	.fb_copyarea	= tdfxfb_copyarea,
115562306a36Sopenharmony_ci	.fb_imageblit	= tdfxfb_imageblit,
115662306a36Sopenharmony_ci#else
115762306a36Sopenharmony_ci	.fb_fillrect	= cfb_fillrect,
115862306a36Sopenharmony_ci	.fb_copyarea	= cfb_copyarea,
115962306a36Sopenharmony_ci	.fb_imageblit	= cfb_imageblit,
116062306a36Sopenharmony_ci#endif
116162306a36Sopenharmony_ci};
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_I2C
116462306a36Sopenharmony_ci/* The voo GPIO registers don't have individual masks for each bit
116562306a36Sopenharmony_ci   so we always have to read before writing. */
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic void tdfxfb_i2c_setscl(void *data, int val)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
117062306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
117162306a36Sopenharmony_ci	unsigned int r;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	r = tdfx_inl(par, VIDSERPARPORT);
117462306a36Sopenharmony_ci	if (val)
117562306a36Sopenharmony_ci		r |= I2C_SCL_OUT;
117662306a36Sopenharmony_ci	else
117762306a36Sopenharmony_ci		r &= ~I2C_SCL_OUT;
117862306a36Sopenharmony_ci	tdfx_outl(par, VIDSERPARPORT, r);
117962306a36Sopenharmony_ci	tdfx_inl(par, VIDSERPARPORT);	/* flush posted write */
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic void tdfxfb_i2c_setsda(void *data, int val)
118362306a36Sopenharmony_ci{
118462306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
118562306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
118662306a36Sopenharmony_ci	unsigned int r;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	r = tdfx_inl(par, VIDSERPARPORT);
118962306a36Sopenharmony_ci	if (val)
119062306a36Sopenharmony_ci		r |= I2C_SDA_OUT;
119162306a36Sopenharmony_ci	else
119262306a36Sopenharmony_ci		r &= ~I2C_SDA_OUT;
119362306a36Sopenharmony_ci	tdfx_outl(par, VIDSERPARPORT, r);
119462306a36Sopenharmony_ci	tdfx_inl(par, VIDSERPARPORT);	/* flush posted write */
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci/* The GPIO pins are open drain, so the pins always remain outputs.
119862306a36Sopenharmony_ci   We rely on the i2c-algo-bit routines to set the pins high before
119962306a36Sopenharmony_ci   reading the input from other chips. */
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic int tdfxfb_i2c_getscl(void *data)
120262306a36Sopenharmony_ci{
120362306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
120462306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SCL_IN));
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_cistatic int tdfxfb_i2c_getsda(void *data)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
121262306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	return (0 != (tdfx_inl(par, VIDSERPARPORT) & I2C_SDA_IN));
121562306a36Sopenharmony_ci}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_cistatic void tdfxfb_ddc_setscl(void *data, int val)
121862306a36Sopenharmony_ci{
121962306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
122062306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
122162306a36Sopenharmony_ci	unsigned int r;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	r = tdfx_inl(par, VIDSERPARPORT);
122462306a36Sopenharmony_ci	if (val)
122562306a36Sopenharmony_ci		r |= DDC_SCL_OUT;
122662306a36Sopenharmony_ci	else
122762306a36Sopenharmony_ci		r &= ~DDC_SCL_OUT;
122862306a36Sopenharmony_ci	tdfx_outl(par, VIDSERPARPORT, r);
122962306a36Sopenharmony_ci	tdfx_inl(par, VIDSERPARPORT);	/* flush posted write */
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic void tdfxfb_ddc_setsda(void *data, int val)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
123562306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
123662306a36Sopenharmony_ci	unsigned int r;
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	r = tdfx_inl(par, VIDSERPARPORT);
123962306a36Sopenharmony_ci	if (val)
124062306a36Sopenharmony_ci		r |= DDC_SDA_OUT;
124162306a36Sopenharmony_ci	else
124262306a36Sopenharmony_ci		r &= ~DDC_SDA_OUT;
124362306a36Sopenharmony_ci	tdfx_outl(par, VIDSERPARPORT, r);
124462306a36Sopenharmony_ci	tdfx_inl(par, VIDSERPARPORT);	/* flush posted write */
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic int tdfxfb_ddc_getscl(void *data)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
125062306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SCL_IN));
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_cistatic int tdfxfb_ddc_getsda(void *data)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	struct tdfxfb_i2c_chan 	*chan = data;
125862306a36Sopenharmony_ci	struct tdfx_par 	*par = chan->par;
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	return (0 != (tdfx_inl(par, VIDSERPARPORT) & DDC_SDA_IN));
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic int tdfxfb_setup_ddc_bus(struct tdfxfb_i2c_chan *chan, const char *name,
126462306a36Sopenharmony_ci				struct device *dev)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	int rc;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));
126962306a36Sopenharmony_ci	chan->adapter.owner		= THIS_MODULE;
127062306a36Sopenharmony_ci	chan->adapter.class		= I2C_CLASS_DDC;
127162306a36Sopenharmony_ci	chan->adapter.algo_data		= &chan->algo;
127262306a36Sopenharmony_ci	chan->adapter.dev.parent	= dev;
127362306a36Sopenharmony_ci	chan->algo.setsda		= tdfxfb_ddc_setsda;
127462306a36Sopenharmony_ci	chan->algo.setscl		= tdfxfb_ddc_setscl;
127562306a36Sopenharmony_ci	chan->algo.getsda		= tdfxfb_ddc_getsda;
127662306a36Sopenharmony_ci	chan->algo.getscl		= tdfxfb_ddc_getscl;
127762306a36Sopenharmony_ci	chan->algo.udelay		= 10;
127862306a36Sopenharmony_ci	chan->algo.timeout		= msecs_to_jiffies(500);
127962306a36Sopenharmony_ci	chan->algo.data 		= chan;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	i2c_set_adapdata(&chan->adapter, chan);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	rc = i2c_bit_add_bus(&chan->adapter);
128462306a36Sopenharmony_ci	if (rc == 0)
128562306a36Sopenharmony_ci		DPRINTK("I2C bus %s registered.\n", name);
128662306a36Sopenharmony_ci	else
128762306a36Sopenharmony_ci		chan->par = NULL;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	return rc;
129062306a36Sopenharmony_ci}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_cistatic int tdfxfb_setup_i2c_bus(struct tdfxfb_i2c_chan *chan, const char *name,
129362306a36Sopenharmony_ci				struct device *dev)
129462306a36Sopenharmony_ci{
129562306a36Sopenharmony_ci	int rc;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));
129862306a36Sopenharmony_ci	chan->adapter.owner		= THIS_MODULE;
129962306a36Sopenharmony_ci	chan->adapter.algo_data		= &chan->algo;
130062306a36Sopenharmony_ci	chan->adapter.dev.parent	= dev;
130162306a36Sopenharmony_ci	chan->algo.setsda		= tdfxfb_i2c_setsda;
130262306a36Sopenharmony_ci	chan->algo.setscl		= tdfxfb_i2c_setscl;
130362306a36Sopenharmony_ci	chan->algo.getsda		= tdfxfb_i2c_getsda;
130462306a36Sopenharmony_ci	chan->algo.getscl		= tdfxfb_i2c_getscl;
130562306a36Sopenharmony_ci	chan->algo.udelay		= 10;
130662306a36Sopenharmony_ci	chan->algo.timeout		= msecs_to_jiffies(500);
130762306a36Sopenharmony_ci	chan->algo.data 		= chan;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	i2c_set_adapdata(&chan->adapter, chan);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	rc = i2c_bit_add_bus(&chan->adapter);
131262306a36Sopenharmony_ci	if (rc == 0)
131362306a36Sopenharmony_ci		DPRINTK("I2C bus %s registered.\n", name);
131462306a36Sopenharmony_ci	else
131562306a36Sopenharmony_ci		chan->par = NULL;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	return rc;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_cistatic void tdfxfb_create_i2c_busses(struct fb_info *info)
132162306a36Sopenharmony_ci{
132262306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	tdfx_outl(par, VIDINFORMAT, 0x8160);
132562306a36Sopenharmony_ci	tdfx_outl(par, VIDSERPARPORT, 0xcffc0020);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	par->chan[0].par = par;
132862306a36Sopenharmony_ci	par->chan[1].par = par;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	tdfxfb_setup_ddc_bus(&par->chan[0], "Voodoo3-DDC", info->device);
133162306a36Sopenharmony_ci	tdfxfb_setup_i2c_bus(&par->chan[1], "Voodoo3-I2C", info->device);
133262306a36Sopenharmony_ci}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_cistatic void tdfxfb_delete_i2c_busses(struct tdfx_par *par)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	if (par->chan[0].par)
133762306a36Sopenharmony_ci		i2c_del_adapter(&par->chan[0].adapter);
133862306a36Sopenharmony_ci	par->chan[0].par = NULL;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	if (par->chan[1].par)
134162306a36Sopenharmony_ci		i2c_del_adapter(&par->chan[1].adapter);
134262306a36Sopenharmony_ci	par->chan[1].par = NULL;
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic int tdfxfb_probe_i2c_connector(struct tdfx_par *par,
134662306a36Sopenharmony_ci				      struct fb_monspecs *specs)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	u8 *edid = NULL;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	DPRINTK("Probe DDC Bus\n");
135162306a36Sopenharmony_ci	if (par->chan[0].par)
135262306a36Sopenharmony_ci		edid = fb_ddc_read(&par->chan[0].adapter);
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	if (edid) {
135562306a36Sopenharmony_ci		fb_edid_to_monspecs(edid, specs);
135662306a36Sopenharmony_ci		kfree(edid);
135762306a36Sopenharmony_ci		return 0;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci	return 1;
136062306a36Sopenharmony_ci}
136162306a36Sopenharmony_ci#endif /* CONFIG_FB_3DFX_I2C */
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci/**
136462306a36Sopenharmony_ci *      tdfxfb_probe - Device Initializiation
136562306a36Sopenharmony_ci *
136662306a36Sopenharmony_ci *      @pdev:  PCI Device to initialize
136762306a36Sopenharmony_ci *      @id:    PCI Device ID
136862306a36Sopenharmony_ci *
136962306a36Sopenharmony_ci *      Initializes and allocates resources for PCI device @pdev.
137062306a36Sopenharmony_ci *
137162306a36Sopenharmony_ci */
137262306a36Sopenharmony_cistatic int tdfxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
137362306a36Sopenharmony_ci{
137462306a36Sopenharmony_ci	struct tdfx_par *default_par;
137562306a36Sopenharmony_ci	struct fb_info *info;
137662306a36Sopenharmony_ci	int err, lpitch;
137762306a36Sopenharmony_ci	struct fb_monspecs *specs;
137862306a36Sopenharmony_ci	bool found;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	err = aperture_remove_conflicting_pci_devices(pdev, "tdfxfb");
138162306a36Sopenharmony_ci	if (err)
138262306a36Sopenharmony_ci		return err;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	err = pci_enable_device(pdev);
138562306a36Sopenharmony_ci	if (err) {
138662306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: Can't enable pdev: %d\n", err);
138762306a36Sopenharmony_ci		return err;
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct tdfx_par), &pdev->dev);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	if (!info)
139362306a36Sopenharmony_ci		return -ENOMEM;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	default_par = info->par;
139662306a36Sopenharmony_ci	info->fix = tdfx_fix;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	/* Configure the default fb_fix_screeninfo first */
139962306a36Sopenharmony_ci	switch (pdev->device) {
140062306a36Sopenharmony_ci	case PCI_DEVICE_ID_3DFX_BANSHEE:
140162306a36Sopenharmony_ci		strcpy(info->fix.id, "3Dfx Banshee");
140262306a36Sopenharmony_ci		default_par->max_pixclock = BANSHEE_MAX_PIXCLOCK;
140362306a36Sopenharmony_ci		break;
140462306a36Sopenharmony_ci	case PCI_DEVICE_ID_3DFX_VOODOO3:
140562306a36Sopenharmony_ci		strcpy(info->fix.id, "3Dfx Voodoo3");
140662306a36Sopenharmony_ci		default_par->max_pixclock = VOODOO3_MAX_PIXCLOCK;
140762306a36Sopenharmony_ci		break;
140862306a36Sopenharmony_ci	case PCI_DEVICE_ID_3DFX_VOODOO5:
140962306a36Sopenharmony_ci		strcpy(info->fix.id, "3Dfx Voodoo5");
141062306a36Sopenharmony_ci		default_par->max_pixclock = VOODOO5_MAX_PIXCLOCK;
141162306a36Sopenharmony_ci		break;
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	info->fix.mmio_start = pci_resource_start(pdev, 0);
141562306a36Sopenharmony_ci	info->fix.mmio_len = pci_resource_len(pdev, 0);
141662306a36Sopenharmony_ci	if (!request_mem_region(info->fix.mmio_start, info->fix.mmio_len,
141762306a36Sopenharmony_ci				"tdfx regbase")) {
141862306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: Can't reserve regbase\n");
141962306a36Sopenharmony_ci		goto out_err;
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	default_par->regbase_virt =
142362306a36Sopenharmony_ci		ioremap(info->fix.mmio_start, info->fix.mmio_len);
142462306a36Sopenharmony_ci	if (!default_par->regbase_virt) {
142562306a36Sopenharmony_ci		printk(KERN_ERR "fb: Can't remap %s register area.\n",
142662306a36Sopenharmony_ci				info->fix.id);
142762306a36Sopenharmony_ci		goto out_err_regbase;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	info->fix.smem_start = pci_resource_start(pdev, 1);
143162306a36Sopenharmony_ci	info->fix.smem_len = do_lfb_size(default_par, pdev->device);
143262306a36Sopenharmony_ci	if (!info->fix.smem_len) {
143362306a36Sopenharmony_ci		printk(KERN_ERR "fb: Can't count %s memory.\n", info->fix.id);
143462306a36Sopenharmony_ci		goto out_err_regbase;
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	if (!request_mem_region(info->fix.smem_start,
143862306a36Sopenharmony_ci				pci_resource_len(pdev, 1), "tdfx smem")) {
143962306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: Can't reserve smem\n");
144062306a36Sopenharmony_ci		goto out_err_regbase;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	info->screen_base = ioremap_wc(info->fix.smem_start,
144462306a36Sopenharmony_ci				       info->fix.smem_len);
144562306a36Sopenharmony_ci	if (!info->screen_base) {
144662306a36Sopenharmony_ci		printk(KERN_ERR "fb: Can't remap %s framebuffer.\n",
144762306a36Sopenharmony_ci				info->fix.id);
144862306a36Sopenharmony_ci		goto out_err_screenbase;
144962306a36Sopenharmony_ci	}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci	default_par->iobase = pci_resource_start(pdev, 2);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	if (!request_region(pci_resource_start(pdev, 2),
145462306a36Sopenharmony_ci			    pci_resource_len(pdev, 2), "tdfx iobase")) {
145562306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: Can't reserve iobase\n");
145662306a36Sopenharmony_ci		goto out_err_screenbase;
145762306a36Sopenharmony_ci	}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	printk(KERN_INFO "fb: %s memory = %dK\n", info->fix.id,
146062306a36Sopenharmony_ci			info->fix.smem_len >> 10);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	if (!nomtrr)
146362306a36Sopenharmony_ci		default_par->wc_cookie= arch_phys_wc_add(info->fix.smem_start,
146462306a36Sopenharmony_ci							 info->fix.smem_len);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	info->fix.ypanstep	= nopan ? 0 : 1;
146762306a36Sopenharmony_ci	info->fix.ywrapstep	= nowrap ? 0 : 1;
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	info->fbops		= &tdfxfb_ops;
147062306a36Sopenharmony_ci	info->pseudo_palette	= default_par->palette;
147162306a36Sopenharmony_ci	info->flags		= FBINFO_HWACCEL_YPAN;
147262306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_ACCEL
147362306a36Sopenharmony_ci	info->flags		|= FBINFO_HWACCEL_FILLRECT |
147462306a36Sopenharmony_ci				   FBINFO_HWACCEL_COPYAREA |
147562306a36Sopenharmony_ci				   FBINFO_HWACCEL_IMAGEBLIT |
147662306a36Sopenharmony_ci				   FBINFO_READS_FAST;
147762306a36Sopenharmony_ci#endif
147862306a36Sopenharmony_ci	/* reserve 8192 bits for cursor */
147962306a36Sopenharmony_ci	/* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */
148062306a36Sopenharmony_ci	if (hwcursor)
148162306a36Sopenharmony_ci		info->fix.smem_len = (info->fix.smem_len - 1024) &
148262306a36Sopenharmony_ci					(PAGE_MASK << 1);
148362306a36Sopenharmony_ci	specs = &info->monspecs;
148462306a36Sopenharmony_ci	found = false;
148562306a36Sopenharmony_ci	info->var.bits_per_pixel = 8;
148662306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_I2C
148762306a36Sopenharmony_ci	tdfxfb_create_i2c_busses(info);
148862306a36Sopenharmony_ci	err = tdfxfb_probe_i2c_connector(default_par, specs);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	if (!err) {
149162306a36Sopenharmony_ci		if (specs->modedb == NULL)
149262306a36Sopenharmony_ci			DPRINTK("Unable to get Mode Database\n");
149362306a36Sopenharmony_ci		else {
149462306a36Sopenharmony_ci			const struct fb_videomode *m;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci			fb_videomode_to_modelist(specs->modedb,
149762306a36Sopenharmony_ci						 specs->modedb_len,
149862306a36Sopenharmony_ci						 &info->modelist);
149962306a36Sopenharmony_ci			m = fb_find_best_display(specs, &info->modelist);
150062306a36Sopenharmony_ci			if (m) {
150162306a36Sopenharmony_ci				fb_videomode_to_var(&info->var, m);
150262306a36Sopenharmony_ci				/* fill all other info->var's fields */
150362306a36Sopenharmony_ci				if (tdfxfb_check_var(&info->var, info) < 0)
150462306a36Sopenharmony_ci					info->var = tdfx_var;
150562306a36Sopenharmony_ci				else
150662306a36Sopenharmony_ci					found = true;
150762306a36Sopenharmony_ci			}
150862306a36Sopenharmony_ci		}
150962306a36Sopenharmony_ci	}
151062306a36Sopenharmony_ci#endif
151162306a36Sopenharmony_ci	if (!mode_option && !found)
151262306a36Sopenharmony_ci		mode_option = "640x480@60";
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (mode_option) {
151562306a36Sopenharmony_ci		err = fb_find_mode(&info->var, info, mode_option,
151662306a36Sopenharmony_ci				   specs->modedb, specs->modedb_len,
151762306a36Sopenharmony_ci				   NULL, info->var.bits_per_pixel);
151862306a36Sopenharmony_ci		if (!err || err == 4)
151962306a36Sopenharmony_ci			info->var = tdfx_var;
152062306a36Sopenharmony_ci	}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	if (found) {
152362306a36Sopenharmony_ci		fb_destroy_modedb(specs->modedb);
152462306a36Sopenharmony_ci		specs->modedb = NULL;
152562306a36Sopenharmony_ci	}
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	/* maximize virtual vertical length */
152862306a36Sopenharmony_ci	lpitch = info->var.xres_virtual * ((info->var.bits_per_pixel + 7) >> 3);
152962306a36Sopenharmony_ci	info->var.yres_virtual = info->fix.smem_len / lpitch;
153062306a36Sopenharmony_ci	if (info->var.yres_virtual < info->var.yres)
153162306a36Sopenharmony_ci		goto out_err_iobase;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
153462306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: Can't allocate color map\n");
153562306a36Sopenharmony_ci		goto out_err_iobase;
153662306a36Sopenharmony_ci	}
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	if (register_framebuffer(info) < 0) {
153962306a36Sopenharmony_ci		printk(KERN_ERR "tdfxfb: can't register framebuffer\n");
154062306a36Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
154162306a36Sopenharmony_ci		goto out_err_iobase;
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci	/*
154462306a36Sopenharmony_ci	 * Our driver data
154562306a36Sopenharmony_ci	 */
154662306a36Sopenharmony_ci	pci_set_drvdata(pdev, info);
154762306a36Sopenharmony_ci	return 0;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ciout_err_iobase:
155062306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_I2C
155162306a36Sopenharmony_ci	tdfxfb_delete_i2c_busses(default_par);
155262306a36Sopenharmony_ci#endif
155362306a36Sopenharmony_ci	arch_phys_wc_del(default_par->wc_cookie);
155462306a36Sopenharmony_ci	release_region(pci_resource_start(pdev, 2),
155562306a36Sopenharmony_ci		       pci_resource_len(pdev, 2));
155662306a36Sopenharmony_ciout_err_screenbase:
155762306a36Sopenharmony_ci	if (info->screen_base)
155862306a36Sopenharmony_ci		iounmap(info->screen_base);
155962306a36Sopenharmony_ci	release_mem_region(info->fix.smem_start, pci_resource_len(pdev, 1));
156062306a36Sopenharmony_ciout_err_regbase:
156162306a36Sopenharmony_ci	/*
156262306a36Sopenharmony_ci	 * Cleanup after anything that was remapped/allocated.
156362306a36Sopenharmony_ci	 */
156462306a36Sopenharmony_ci	if (default_par->regbase_virt)
156562306a36Sopenharmony_ci		iounmap(default_par->regbase_virt);
156662306a36Sopenharmony_ci	release_mem_region(info->fix.mmio_start, info->fix.mmio_len);
156762306a36Sopenharmony_ciout_err:
156862306a36Sopenharmony_ci	framebuffer_release(info);
156962306a36Sopenharmony_ci	return -ENXIO;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci#ifndef MODULE
157362306a36Sopenharmony_cistatic void __init tdfxfb_setup(char *options)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	char *this_opt;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	if (!options || !*options)
157862306a36Sopenharmony_ci		return;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	while ((this_opt = strsep(&options, ",")) != NULL) {
158162306a36Sopenharmony_ci		if (!*this_opt)
158262306a36Sopenharmony_ci			continue;
158362306a36Sopenharmony_ci		if (!strcmp(this_opt, "nopan")) {
158462306a36Sopenharmony_ci			nopan = 1;
158562306a36Sopenharmony_ci		} else if (!strcmp(this_opt, "nowrap")) {
158662306a36Sopenharmony_ci			nowrap = 1;
158762306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "hwcursor=", 9)) {
158862306a36Sopenharmony_ci			hwcursor = simple_strtoul(this_opt + 9, NULL, 0);
158962306a36Sopenharmony_ci		} else if (!strncmp(this_opt, "nomtrr", 6)) {
159062306a36Sopenharmony_ci			nomtrr = 1;
159162306a36Sopenharmony_ci		} else {
159262306a36Sopenharmony_ci			mode_option = this_opt;
159362306a36Sopenharmony_ci		}
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci}
159662306a36Sopenharmony_ci#endif
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci/**
159962306a36Sopenharmony_ci *      tdfxfb_remove - Device removal
160062306a36Sopenharmony_ci *
160162306a36Sopenharmony_ci *      @pdev:  PCI Device to cleanup
160262306a36Sopenharmony_ci *
160362306a36Sopenharmony_ci *      Releases all resources allocated during the course of the driver's
160462306a36Sopenharmony_ci *      lifetime for the PCI device @pdev.
160562306a36Sopenharmony_ci *
160662306a36Sopenharmony_ci */
160762306a36Sopenharmony_cistatic void tdfxfb_remove(struct pci_dev *pdev)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(pdev);
161062306a36Sopenharmony_ci	struct tdfx_par *par = info->par;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	unregister_framebuffer(info);
161362306a36Sopenharmony_ci#ifdef CONFIG_FB_3DFX_I2C
161462306a36Sopenharmony_ci	tdfxfb_delete_i2c_busses(par);
161562306a36Sopenharmony_ci#endif
161662306a36Sopenharmony_ci	arch_phys_wc_del(par->wc_cookie);
161762306a36Sopenharmony_ci	iounmap(par->regbase_virt);
161862306a36Sopenharmony_ci	iounmap(info->screen_base);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	/* Clean up after reserved regions */
162162306a36Sopenharmony_ci	release_region(pci_resource_start(pdev, 2),
162262306a36Sopenharmony_ci		       pci_resource_len(pdev, 2));
162362306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 1),
162462306a36Sopenharmony_ci			   pci_resource_len(pdev, 1));
162562306a36Sopenharmony_ci	release_mem_region(pci_resource_start(pdev, 0),
162662306a36Sopenharmony_ci			   pci_resource_len(pdev, 0));
162762306a36Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
162862306a36Sopenharmony_ci	framebuffer_release(info);
162962306a36Sopenharmony_ci}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_cistatic int __init tdfxfb_init(void)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci#ifndef MODULE
163462306a36Sopenharmony_ci	char *option = NULL;
163562306a36Sopenharmony_ci#endif
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (fb_modesetting_disabled("tdfxfb"))
163862306a36Sopenharmony_ci		return -ENODEV;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci#ifndef MODULE
164162306a36Sopenharmony_ci	if (fb_get_options("tdfxfb", &option))
164262306a36Sopenharmony_ci		return -ENODEV;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	tdfxfb_setup(option);
164562306a36Sopenharmony_ci#endif
164662306a36Sopenharmony_ci	return pci_register_driver(&tdfxfb_driver);
164762306a36Sopenharmony_ci}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_cistatic void __exit tdfxfb_exit(void)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	pci_unregister_driver(&tdfxfb_driver);
165262306a36Sopenharmony_ci}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ciMODULE_AUTHOR("Hannu Mallat <hmallat@cc.hut.fi>");
165562306a36Sopenharmony_ciMODULE_DESCRIPTION("3Dfx framebuffer device driver");
165662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_cimodule_param(hwcursor, int, 0644);
165962306a36Sopenharmony_ciMODULE_PARM_DESC(hwcursor, "Enable hardware cursor "
166062306a36Sopenharmony_ci			"(1=enable, 0=disable, default=1)");
166162306a36Sopenharmony_cimodule_param(mode_option, charp, 0);
166262306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
166362306a36Sopenharmony_cimodule_param(nomtrr, bool, 0);
166462306a36Sopenharmony_ciMODULE_PARM_DESC(nomtrr, "Disable MTRR support (default: enabled)");
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cimodule_init(tdfxfb_init);
166762306a36Sopenharmony_cimodule_exit(tdfxfb_exit);
1668