18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Frame buffer driver for Trident TGUI, Blade and Image series
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2001, 2002 - Jani Monoses   <jani@iv.ro>
68c2ecf20Sopenharmony_ci * Copyright 2009 Krzysztof Helt <krzysztof.h1@wp.pl>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * CREDITS:(in order of appearance)
98c2ecf20Sopenharmony_ci *	skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video
108c2ecf20Sopenharmony_ci *	Special thanks ;) to Mattia Crivellini <tia@mclink.it>
118c2ecf20Sopenharmony_ci *	much inspired by the XFree86 4.x Trident driver sources
128c2ecf20Sopenharmony_ci *	by Alan Hourihane the FreeVGA project
138c2ecf20Sopenharmony_ci *	Francesco Salvestrini <salvestrini@users.sf.net> XP support,
148c2ecf20Sopenharmony_ci *	code, suggestions
158c2ecf20Sopenharmony_ci * TODO:
168c2ecf20Sopenharmony_ci *	timing value tweaking so it looks good on every monitor in every mode
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/fb.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/pci.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/delay.h>
268c2ecf20Sopenharmony_ci#include <video/vga.h>
278c2ecf20Sopenharmony_ci#include <video/trident.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <linux/i2c.h>
308c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct tridentfb_par {
338c2ecf20Sopenharmony_ci	void __iomem *io_virt;	/* iospace virtual memory address */
348c2ecf20Sopenharmony_ci	u32 pseudo_pal[16];
358c2ecf20Sopenharmony_ci	int chip_id;
368c2ecf20Sopenharmony_ci	int flatpanel;
378c2ecf20Sopenharmony_ci	void (*init_accel) (struct tridentfb_par *, int, int);
388c2ecf20Sopenharmony_ci	void (*wait_engine) (struct tridentfb_par *);
398c2ecf20Sopenharmony_ci	void (*fill_rect)
408c2ecf20Sopenharmony_ci		(struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
418c2ecf20Sopenharmony_ci	void (*copy_rect)
428c2ecf20Sopenharmony_ci		(struct tridentfb_par *par, u32, u32, u32, u32, u32, u32);
438c2ecf20Sopenharmony_ci	void (*image_blit)
448c2ecf20Sopenharmony_ci		(struct tridentfb_par *par, const char*,
458c2ecf20Sopenharmony_ci		 u32, u32, u32, u32, u32, u32);
468c2ecf20Sopenharmony_ci	unsigned char eng_oper;	/* engine operation... */
478c2ecf20Sopenharmony_ci	bool ddc_registered;
488c2ecf20Sopenharmony_ci	struct i2c_adapter ddc_adapter;
498c2ecf20Sopenharmony_ci	struct i2c_algo_bit_data ddc_algo;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo tridentfb_fix = {
538c2ecf20Sopenharmony_ci	.id = "Trident",
548c2ecf20Sopenharmony_ci	.type = FB_TYPE_PACKED_PIXELS,
558c2ecf20Sopenharmony_ci	.ypanstep = 1,
568c2ecf20Sopenharmony_ci	.visual = FB_VISUAL_PSEUDOCOLOR,
578c2ecf20Sopenharmony_ci	.accel = FB_ACCEL_NONE,
588c2ecf20Sopenharmony_ci};
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* defaults which are normally overriden by user values */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* video mode */
638c2ecf20Sopenharmony_cistatic char *mode_option;
648c2ecf20Sopenharmony_cistatic int bpp = 8;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic int noaccel;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int center;
698c2ecf20Sopenharmony_cistatic int stretch;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int fp;
728c2ecf20Sopenharmony_cistatic int crt;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int memsize;
758c2ecf20Sopenharmony_cistatic int memdiff;
768c2ecf20Sopenharmony_cistatic int nativex;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0);
798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
808c2ecf20Sopenharmony_cimodule_param_named(mode, mode_option, charp, 0);
818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode, "Initial video mode e.g. '648x480-8@60' (deprecated)");
828c2ecf20Sopenharmony_cimodule_param(bpp, int, 0);
838c2ecf20Sopenharmony_cimodule_param(center, int, 0);
848c2ecf20Sopenharmony_cimodule_param(stretch, int, 0);
858c2ecf20Sopenharmony_cimodule_param(noaccel, int, 0);
868c2ecf20Sopenharmony_cimodule_param(memsize, int, 0);
878c2ecf20Sopenharmony_cimodule_param(memdiff, int, 0);
888c2ecf20Sopenharmony_cimodule_param(nativex, int, 0);
898c2ecf20Sopenharmony_cimodule_param(fp, int, 0);
908c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fp, "Define if flatpanel is connected");
918c2ecf20Sopenharmony_cimodule_param(crt, int, 0);
928c2ecf20Sopenharmony_ciMODULE_PARM_DESC(crt, "Define if CRT is connected");
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline int is_oldclock(int id)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	return	(id == TGUI9440) ||
978c2ecf20Sopenharmony_ci		(id == TGUI9660) ||
988c2ecf20Sopenharmony_ci		(id == CYBER9320);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic inline int is_oldprotect(int id)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	return	is_oldclock(id) ||
1048c2ecf20Sopenharmony_ci		(id == PROVIDIA9685) ||
1058c2ecf20Sopenharmony_ci		(id == CYBER9382) ||
1068c2ecf20Sopenharmony_ci		(id == CYBER9385);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic inline int is_blade(int id)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	return	(id == BLADE3D) ||
1128c2ecf20Sopenharmony_ci		(id == CYBERBLADEE4) ||
1138c2ecf20Sopenharmony_ci		(id == CYBERBLADEi7) ||
1148c2ecf20Sopenharmony_ci		(id == CYBERBLADEi7D) ||
1158c2ecf20Sopenharmony_ci		(id == CYBERBLADEi1) ||
1168c2ecf20Sopenharmony_ci		(id == CYBERBLADEi1D) ||
1178c2ecf20Sopenharmony_ci		(id == CYBERBLADEAi1) ||
1188c2ecf20Sopenharmony_ci		(id == CYBERBLADEAi1D);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic inline int is_xp(int id)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	return	(id == CYBERBLADEXPAi1) ||
1248c2ecf20Sopenharmony_ci		(id == CYBERBLADEXPm8) ||
1258c2ecf20Sopenharmony_ci		(id == CYBERBLADEXPm16);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic inline int is3Dchip(int id)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	return	is_blade(id) || is_xp(id) ||
1318c2ecf20Sopenharmony_ci		(id == CYBER9397) || (id == CYBER9397DVD) ||
1328c2ecf20Sopenharmony_ci		(id == CYBER9520) || (id == CYBER9525DVD) ||
1338c2ecf20Sopenharmony_ci		(id == IMAGE975) || (id == IMAGE985);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic inline int iscyber(int id)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	switch (id) {
1398c2ecf20Sopenharmony_ci	case CYBER9388:
1408c2ecf20Sopenharmony_ci	case CYBER9382:
1418c2ecf20Sopenharmony_ci	case CYBER9385:
1428c2ecf20Sopenharmony_ci	case CYBER9397:
1438c2ecf20Sopenharmony_ci	case CYBER9397DVD:
1448c2ecf20Sopenharmony_ci	case CYBER9520:
1458c2ecf20Sopenharmony_ci	case CYBER9525DVD:
1468c2ecf20Sopenharmony_ci	case CYBERBLADEE4:
1478c2ecf20Sopenharmony_ci	case CYBERBLADEi7D:
1488c2ecf20Sopenharmony_ci	case CYBERBLADEi1:
1498c2ecf20Sopenharmony_ci	case CYBERBLADEi1D:
1508c2ecf20Sopenharmony_ci	case CYBERBLADEAi1:
1518c2ecf20Sopenharmony_ci	case CYBERBLADEAi1D:
1528c2ecf20Sopenharmony_ci	case CYBERBLADEXPAi1:
1538c2ecf20Sopenharmony_ci		return 1;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	case CYBER9320:
1568c2ecf20Sopenharmony_ci	case CYBERBLADEi7:	/* VIA MPV4 integrated version */
1578c2ecf20Sopenharmony_ci	default:
1588c2ecf20Sopenharmony_ci		/* case CYBERBLDAEXPm8:  Strange */
1598c2ecf20Sopenharmony_ci		/* case CYBERBLDAEXPm16: Strange */
1608c2ecf20Sopenharmony_ci		return 0;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic inline void t_outb(struct tridentfb_par *p, u8 val, u16 reg)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	fb_writeb(val, p->io_virt + reg);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic inline u8 t_inb(struct tridentfb_par *p, u16 reg)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	return fb_readb(p->io_virt + reg);
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic inline void writemmr(struct tridentfb_par *par, u16 r, u32 v)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	fb_writel(v, par->io_virt + r);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic inline u32 readmmr(struct tridentfb_par *par, u16 r)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	return fb_readl(par->io_virt + r);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci#define DDC_SDA_TGUI		BIT(0)
1858c2ecf20Sopenharmony_ci#define DDC_SCL_TGUI		BIT(1)
1868c2ecf20Sopenharmony_ci#define DDC_SCL_DRIVE_TGUI	BIT(2)
1878c2ecf20Sopenharmony_ci#define DDC_SDA_DRIVE_TGUI	BIT(3)
1888c2ecf20Sopenharmony_ci#define DDC_MASK_TGUI		(DDC_SCL_DRIVE_TGUI | DDC_SDA_DRIVE_TGUI)
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void tridentfb_ddc_setscl_tgui(void *data, int val)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
1938c2ecf20Sopenharmony_ci	u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (val)
1968c2ecf20Sopenharmony_ci		reg &= ~DDC_SCL_DRIVE_TGUI; /* disable drive - don't drive hi */
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		reg |= DDC_SCL_DRIVE_TGUI; /* drive low */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	vga_mm_wcrt(par->io_virt, I2C, reg);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic void tridentfb_ddc_setsda_tgui(void *data, int val)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2068c2ecf20Sopenharmony_ci	u8 reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK_TGUI;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (val)
2098c2ecf20Sopenharmony_ci		reg &= ~DDC_SDA_DRIVE_TGUI; /* disable drive - don't drive hi */
2108c2ecf20Sopenharmony_ci	else
2118c2ecf20Sopenharmony_ci		reg |= DDC_SDA_DRIVE_TGUI; /* drive low */
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	vga_mm_wcrt(par->io_virt, I2C, reg);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic int tridentfb_ddc_getsda_tgui(void *data)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_TGUI);
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci#define DDC_SDA_IN	BIT(0)
2248c2ecf20Sopenharmony_ci#define DDC_SCL_OUT	BIT(1)
2258c2ecf20Sopenharmony_ci#define DDC_SDA_OUT	BIT(3)
2268c2ecf20Sopenharmony_ci#define DDC_SCL_IN	BIT(6)
2278c2ecf20Sopenharmony_ci#define DDC_MASK	(DDC_SCL_OUT | DDC_SDA_OUT)
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void tridentfb_ddc_setscl(void *data, int val)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2328c2ecf20Sopenharmony_ci	unsigned char reg;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
2358c2ecf20Sopenharmony_ci	if (val)
2368c2ecf20Sopenharmony_ci		reg |= DDC_SCL_OUT;
2378c2ecf20Sopenharmony_ci	else
2388c2ecf20Sopenharmony_ci		reg &= ~DDC_SCL_OUT;
2398c2ecf20Sopenharmony_ci	vga_mm_wcrt(par->io_virt, I2C, reg);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void tridentfb_ddc_setsda(void *data, int val)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2458c2ecf20Sopenharmony_ci	unsigned char reg;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	reg = vga_mm_rcrt(par->io_virt, I2C) & DDC_MASK;
2488c2ecf20Sopenharmony_ci	if (!val)
2498c2ecf20Sopenharmony_ci		reg |= DDC_SDA_OUT;
2508c2ecf20Sopenharmony_ci	else
2518c2ecf20Sopenharmony_ci		reg &= ~DDC_SDA_OUT;
2528c2ecf20Sopenharmony_ci	vga_mm_wcrt(par->io_virt, I2C, reg);
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int tridentfb_ddc_getscl(void *data)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SCL_IN);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int tridentfb_ddc_getsda(void *data)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct tridentfb_par *par = data;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return !!(vga_mm_rcrt(par->io_virt, I2C) & DDC_SDA_IN);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int tridentfb_setup_ddc_bus(struct fb_info *info)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	strlcpy(par->ddc_adapter.name, info->fix.id,
2748c2ecf20Sopenharmony_ci		sizeof(par->ddc_adapter.name));
2758c2ecf20Sopenharmony_ci	par->ddc_adapter.owner		= THIS_MODULE;
2768c2ecf20Sopenharmony_ci	par->ddc_adapter.class		= I2C_CLASS_DDC;
2778c2ecf20Sopenharmony_ci	par->ddc_adapter.algo_data	= &par->ddc_algo;
2788c2ecf20Sopenharmony_ci	par->ddc_adapter.dev.parent	= info->device;
2798c2ecf20Sopenharmony_ci	if (is_oldclock(par->chip_id)) { /* not sure if this check is OK */
2808c2ecf20Sopenharmony_ci		par->ddc_algo.setsda	= tridentfb_ddc_setsda_tgui;
2818c2ecf20Sopenharmony_ci		par->ddc_algo.setscl	= tridentfb_ddc_setscl_tgui;
2828c2ecf20Sopenharmony_ci		par->ddc_algo.getsda	= tridentfb_ddc_getsda_tgui;
2838c2ecf20Sopenharmony_ci		/* no getscl */
2848c2ecf20Sopenharmony_ci	} else {
2858c2ecf20Sopenharmony_ci		par->ddc_algo.setsda	= tridentfb_ddc_setsda;
2868c2ecf20Sopenharmony_ci		par->ddc_algo.setscl	= tridentfb_ddc_setscl;
2878c2ecf20Sopenharmony_ci		par->ddc_algo.getsda	= tridentfb_ddc_getsda;
2888c2ecf20Sopenharmony_ci		par->ddc_algo.getscl	= tridentfb_ddc_getscl;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	par->ddc_algo.udelay		= 10;
2918c2ecf20Sopenharmony_ci	par->ddc_algo.timeout		= 20;
2928c2ecf20Sopenharmony_ci	par->ddc_algo.data		= par;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	i2c_set_adapdata(&par->ddc_adapter, par);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return i2c_bit_add_bus(&par->ddc_adapter);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci/*
3008c2ecf20Sopenharmony_ci * Blade specific acceleration.
3018c2ecf20Sopenharmony_ci */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci#define point(x, y) ((y) << 16 | (x))
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic void blade_init_accel(struct tridentfb_par *par, int pitch, int bpp)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	int v1 = (pitch >> 3) << 20;
3088c2ecf20Sopenharmony_ci	int tmp = bpp == 24 ? 2 : (bpp >> 4);
3098c2ecf20Sopenharmony_ci	int v2 = v1 | (tmp << 29);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	writemmr(par, 0x21C0, v2);
3128c2ecf20Sopenharmony_ci	writemmr(par, 0x21C4, v2);
3138c2ecf20Sopenharmony_ci	writemmr(par, 0x21B8, v2);
3148c2ecf20Sopenharmony_ci	writemmr(par, 0x21BC, v2);
3158c2ecf20Sopenharmony_ci	writemmr(par, 0x21D0, v1);
3168c2ecf20Sopenharmony_ci	writemmr(par, 0x21D4, v1);
3178c2ecf20Sopenharmony_ci	writemmr(par, 0x21C8, v1);
3188c2ecf20Sopenharmony_ci	writemmr(par, 0x21CC, v1);
3198c2ecf20Sopenharmony_ci	writemmr(par, 0x216C, 0);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void blade_wait_engine(struct tridentfb_par *par)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	while (readmmr(par, STATUS) & 0xFA800000)
3258c2ecf20Sopenharmony_ci		cpu_relax();
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void blade_fill_rect(struct tridentfb_par *par,
3298c2ecf20Sopenharmony_ci			    u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	writemmr(par, COLOR, c);
3328c2ecf20Sopenharmony_ci	writemmr(par, ROP, rop ? ROP_X : ROP_S);
3338c2ecf20Sopenharmony_ci	writemmr(par, CMD, 0x20000000 | 1 << 19 | 1 << 4 | 2 << 2);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	writemmr(par, DST1, point(x, y));
3368c2ecf20Sopenharmony_ci	writemmr(par, DST2, point(x + w - 1, y + h - 1));
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic void blade_image_blit(struct tridentfb_par *par, const char *data,
3408c2ecf20Sopenharmony_ci			     u32 x, u32 y, u32 w, u32 h, u32 c, u32 b)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	unsigned size = ((w + 31) >> 5) * h;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	writemmr(par, COLOR, c);
3458c2ecf20Sopenharmony_ci	writemmr(par, BGCOLOR, b);
3468c2ecf20Sopenharmony_ci	writemmr(par, CMD, 0xa0000000 | 3 << 19);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	writemmr(par, DST1, point(x, y));
3498c2ecf20Sopenharmony_ci	writemmr(par, DST2, point(x + w - 1, y + h - 1));
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	iowrite32_rep(par->io_virt + 0x10000, data, size);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic void blade_copy_rect(struct tridentfb_par *par,
3558c2ecf20Sopenharmony_ci			    u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	int direction = 2;
3588c2ecf20Sopenharmony_ci	u32 s1 = point(x1, y1);
3598c2ecf20Sopenharmony_ci	u32 s2 = point(x1 + w - 1, y1 + h - 1);
3608c2ecf20Sopenharmony_ci	u32 d1 = point(x2, y2);
3618c2ecf20Sopenharmony_ci	u32 d2 = point(x2 + w - 1, y2 + h - 1);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
3648c2ecf20Sopenharmony_ci		direction = 0;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	writemmr(par, ROP, ROP_S);
3678c2ecf20Sopenharmony_ci	writemmr(par, CMD, 0xE0000000 | 1 << 19 | 1 << 4 | 1 << 2 | direction);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	writemmr(par, SRC1, direction ? s2 : s1);
3708c2ecf20Sopenharmony_ci	writemmr(par, SRC2, direction ? s1 : s2);
3718c2ecf20Sopenharmony_ci	writemmr(par, DST1, direction ? d2 : d1);
3728c2ecf20Sopenharmony_ci	writemmr(par, DST2, direction ? d1 : d2);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/*
3768c2ecf20Sopenharmony_ci * BladeXP specific acceleration functions
3778c2ecf20Sopenharmony_ci */
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic void xp_init_accel(struct tridentfb_par *par, int pitch, int bpp)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	unsigned char x = bpp == 24 ? 3 : (bpp >> 4);
3828c2ecf20Sopenharmony_ci	int v1 = pitch << (bpp == 24 ? 20 : (18 + x));
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	switch (pitch << (bpp >> 3)) {
3858c2ecf20Sopenharmony_ci	case 8192:
3868c2ecf20Sopenharmony_ci	case 512:
3878c2ecf20Sopenharmony_ci		x |= 0x00;
3888c2ecf20Sopenharmony_ci		break;
3898c2ecf20Sopenharmony_ci	case 1024:
3908c2ecf20Sopenharmony_ci		x |= 0x04;
3918c2ecf20Sopenharmony_ci		break;
3928c2ecf20Sopenharmony_ci	case 2048:
3938c2ecf20Sopenharmony_ci		x |= 0x08;
3948c2ecf20Sopenharmony_ci		break;
3958c2ecf20Sopenharmony_ci	case 4096:
3968c2ecf20Sopenharmony_ci		x |= 0x0C;
3978c2ecf20Sopenharmony_ci		break;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	t_outb(par, x, 0x2125);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	par->eng_oper = x | 0x40;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	writemmr(par, 0x2154, v1);
4058c2ecf20Sopenharmony_ci	writemmr(par, 0x2150, v1);
4068c2ecf20Sopenharmony_ci	t_outb(par, 3, 0x2126);
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_cistatic void xp_wait_engine(struct tridentfb_par *par)
4108c2ecf20Sopenharmony_ci{
4118c2ecf20Sopenharmony_ci	int count = 0;
4128c2ecf20Sopenharmony_ci	int timeout = 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	while (t_inb(par, STATUS) & 0x80) {
4158c2ecf20Sopenharmony_ci		count++;
4168c2ecf20Sopenharmony_ci		if (count == 10000000) {
4178c2ecf20Sopenharmony_ci			/* Timeout */
4188c2ecf20Sopenharmony_ci			count = 9990000;
4198c2ecf20Sopenharmony_ci			timeout++;
4208c2ecf20Sopenharmony_ci			if (timeout == 8) {
4218c2ecf20Sopenharmony_ci				/* Reset engine */
4228c2ecf20Sopenharmony_ci				t_outb(par, 0x00, STATUS);
4238c2ecf20Sopenharmony_ci				return;
4248c2ecf20Sopenharmony_ci			}
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci		cpu_relax();
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void xp_fill_rect(struct tridentfb_par *par,
4318c2ecf20Sopenharmony_ci			 u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	writemmr(par, 0x2127, ROP_P);
4348c2ecf20Sopenharmony_ci	writemmr(par, 0x2158, c);
4358c2ecf20Sopenharmony_ci	writemmr(par, DRAWFL, 0x4000);
4368c2ecf20Sopenharmony_ci	writemmr(par, OLDDIM, point(h, w));
4378c2ecf20Sopenharmony_ci	writemmr(par, OLDDST, point(y, x));
4388c2ecf20Sopenharmony_ci	t_outb(par, 0x01, OLDCMD);
4398c2ecf20Sopenharmony_ci	t_outb(par, par->eng_oper, 0x2125);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic void xp_copy_rect(struct tridentfb_par *par,
4438c2ecf20Sopenharmony_ci			 u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	u32 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
4468c2ecf20Sopenharmony_ci	int direction = 0x0004;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if ((x1 < x2) && (y1 == y2)) {
4498c2ecf20Sopenharmony_ci		direction |= 0x0200;
4508c2ecf20Sopenharmony_ci		x1_tmp = x1 + w - 1;
4518c2ecf20Sopenharmony_ci		x2_tmp = x2 + w - 1;
4528c2ecf20Sopenharmony_ci	} else {
4538c2ecf20Sopenharmony_ci		x1_tmp = x1;
4548c2ecf20Sopenharmony_ci		x2_tmp = x2;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (y1 < y2) {
4588c2ecf20Sopenharmony_ci		direction |= 0x0100;
4598c2ecf20Sopenharmony_ci		y1_tmp = y1 + h - 1;
4608c2ecf20Sopenharmony_ci		y2_tmp = y2 + h - 1;
4618c2ecf20Sopenharmony_ci	} else {
4628c2ecf20Sopenharmony_ci		y1_tmp = y1;
4638c2ecf20Sopenharmony_ci		y2_tmp = y2;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	writemmr(par, DRAWFL, direction);
4678c2ecf20Sopenharmony_ci	t_outb(par, ROP_S, 0x2127);
4688c2ecf20Sopenharmony_ci	writemmr(par, OLDSRC, point(y1_tmp, x1_tmp));
4698c2ecf20Sopenharmony_ci	writemmr(par, OLDDST, point(y2_tmp, x2_tmp));
4708c2ecf20Sopenharmony_ci	writemmr(par, OLDDIM, point(h, w));
4718c2ecf20Sopenharmony_ci	t_outb(par, 0x01, OLDCMD);
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * Image specific acceleration functions
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic void image_init_accel(struct tridentfb_par *par, int pitch, int bpp)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	int tmp = bpp == 24 ? 2: (bpp >> 4);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0xF0000000);
4828c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x40000000 | tmp);
4838c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x80000000);
4848c2ecf20Sopenharmony_ci	writemmr(par, 0x2144, 0x00000000);
4858c2ecf20Sopenharmony_ci	writemmr(par, 0x2148, 0x00000000);
4868c2ecf20Sopenharmony_ci	writemmr(par, 0x2150, 0x00000000);
4878c2ecf20Sopenharmony_ci	writemmr(par, 0x2154, 0x00000000);
4888c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x60000000 | (pitch << 16) | pitch);
4898c2ecf20Sopenharmony_ci	writemmr(par, 0x216C, 0x00000000);
4908c2ecf20Sopenharmony_ci	writemmr(par, 0x2170, 0x00000000);
4918c2ecf20Sopenharmony_ci	writemmr(par, 0x217C, 0x00000000);
4928c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x10000000);
4938c2ecf20Sopenharmony_ci	writemmr(par, 0x2130, (2047 << 16) | 2047);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic void image_wait_engine(struct tridentfb_par *par)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	while (readmmr(par, 0x2164) & 0xF0000000)
4998c2ecf20Sopenharmony_ci		cpu_relax();
5008c2ecf20Sopenharmony_ci}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_cistatic void image_fill_rect(struct tridentfb_par *par,
5038c2ecf20Sopenharmony_ci			    u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
5048c2ecf20Sopenharmony_ci{
5058c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x80000000);
5068c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x90000000 | ROP_S);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	writemmr(par, 0x2144, c);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	writemmr(par, DST1, point(x, y));
5118c2ecf20Sopenharmony_ci	writemmr(par, DST2, point(x + w - 1, y + h - 1));
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	writemmr(par, 0x2124, 0x80000000 | 3 << 22 | 1 << 10 | 1 << 9);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void image_copy_rect(struct tridentfb_par *par,
5178c2ecf20Sopenharmony_ci			    u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	int direction = 0x4;
5208c2ecf20Sopenharmony_ci	u32 s1 = point(x1, y1);
5218c2ecf20Sopenharmony_ci	u32 s2 = point(x1 + w - 1, y1 + h - 1);
5228c2ecf20Sopenharmony_ci	u32 d1 = point(x2, y2);
5238c2ecf20Sopenharmony_ci	u32 d2 = point(x2 + w - 1, y2 + h - 1);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if ((y1 > y2) || ((y1 == y2) && (x1 > x2)))
5268c2ecf20Sopenharmony_ci		direction = 0;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x80000000);
5298c2ecf20Sopenharmony_ci	writemmr(par, 0x2120, 0x90000000 | ROP_S);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	writemmr(par, SRC1, direction ? s2 : s1);
5328c2ecf20Sopenharmony_ci	writemmr(par, SRC2, direction ? s1 : s2);
5338c2ecf20Sopenharmony_ci	writemmr(par, DST1, direction ? d2 : d1);
5348c2ecf20Sopenharmony_ci	writemmr(par, DST2, direction ? d1 : d2);
5358c2ecf20Sopenharmony_ci	writemmr(par, 0x2124,
5368c2ecf20Sopenharmony_ci		 0x80000000 | 1 << 22 | 1 << 10 | 1 << 7 | direction);
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci/*
5408c2ecf20Sopenharmony_ci * TGUI 9440/96XX acceleration
5418c2ecf20Sopenharmony_ci */
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic void tgui_init_accel(struct tridentfb_par *par, int pitch, int bpp)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	unsigned char x = bpp == 24 ? 3 : (bpp >> 4);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* disable clipping */
5488c2ecf20Sopenharmony_ci	writemmr(par, 0x2148, 0);
5498c2ecf20Sopenharmony_ci	writemmr(par, 0x214C, point(4095, 2047));
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	switch ((pitch * bpp) / 8) {
5528c2ecf20Sopenharmony_ci	case 8192:
5538c2ecf20Sopenharmony_ci	case 512:
5548c2ecf20Sopenharmony_ci		x |= 0x00;
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci	case 1024:
5578c2ecf20Sopenharmony_ci		x |= 0x04;
5588c2ecf20Sopenharmony_ci		break;
5598c2ecf20Sopenharmony_ci	case 2048:
5608c2ecf20Sopenharmony_ci		x |= 0x08;
5618c2ecf20Sopenharmony_ci		break;
5628c2ecf20Sopenharmony_ci	case 4096:
5638c2ecf20Sopenharmony_ci		x |= 0x0C;
5648c2ecf20Sopenharmony_ci		break;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	fb_writew(x, par->io_virt + 0x2122);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic void tgui_fill_rect(struct tridentfb_par *par,
5718c2ecf20Sopenharmony_ci			   u32 x, u32 y, u32 w, u32 h, u32 c, u32 rop)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	t_outb(par, ROP_P, 0x2127);
5748c2ecf20Sopenharmony_ci	writemmr(par, OLDCLR, c);
5758c2ecf20Sopenharmony_ci	writemmr(par, DRAWFL, 0x4020);
5768c2ecf20Sopenharmony_ci	writemmr(par, OLDDIM, point(w - 1, h - 1));
5778c2ecf20Sopenharmony_ci	writemmr(par, OLDDST, point(x, y));
5788c2ecf20Sopenharmony_ci	t_outb(par, 1, OLDCMD);
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void tgui_copy_rect(struct tridentfb_par *par,
5828c2ecf20Sopenharmony_ci			   u32 x1, u32 y1, u32 x2, u32 y2, u32 w, u32 h)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	int flags = 0;
5858c2ecf20Sopenharmony_ci	u16 x1_tmp, x2_tmp, y1_tmp, y2_tmp;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if ((x1 < x2) && (y1 == y2)) {
5888c2ecf20Sopenharmony_ci		flags |= 0x0200;
5898c2ecf20Sopenharmony_ci		x1_tmp = x1 + w - 1;
5908c2ecf20Sopenharmony_ci		x2_tmp = x2 + w - 1;
5918c2ecf20Sopenharmony_ci	} else {
5928c2ecf20Sopenharmony_ci		x1_tmp = x1;
5938c2ecf20Sopenharmony_ci		x2_tmp = x2;
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (y1 < y2) {
5978c2ecf20Sopenharmony_ci		flags |= 0x0100;
5988c2ecf20Sopenharmony_ci		y1_tmp = y1 + h - 1;
5998c2ecf20Sopenharmony_ci		y2_tmp = y2 + h - 1;
6008c2ecf20Sopenharmony_ci	} else {
6018c2ecf20Sopenharmony_ci		y1_tmp = y1;
6028c2ecf20Sopenharmony_ci		y2_tmp = y2;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	writemmr(par, DRAWFL, 0x4 | flags);
6068c2ecf20Sopenharmony_ci	t_outb(par, ROP_S, 0x2127);
6078c2ecf20Sopenharmony_ci	writemmr(par, OLDSRC, point(x1_tmp, y1_tmp));
6088c2ecf20Sopenharmony_ci	writemmr(par, OLDDST, point(x2_tmp, y2_tmp));
6098c2ecf20Sopenharmony_ci	writemmr(par, OLDDIM, point(w - 1, h - 1));
6108c2ecf20Sopenharmony_ci	t_outb(par, 1, OLDCMD);
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci/*
6148c2ecf20Sopenharmony_ci * Accel functions called by the upper layers
6158c2ecf20Sopenharmony_ci */
6168c2ecf20Sopenharmony_cistatic void tridentfb_fillrect(struct fb_info *info,
6178c2ecf20Sopenharmony_ci			       const struct fb_fillrect *fr)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
6208c2ecf20Sopenharmony_ci	int col;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	if (info->flags & FBINFO_HWACCEL_DISABLED) {
6238c2ecf20Sopenharmony_ci		cfb_fillrect(info, fr);
6248c2ecf20Sopenharmony_ci		return;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 8) {
6278c2ecf20Sopenharmony_ci		col = fr->color;
6288c2ecf20Sopenharmony_ci		col |= col << 8;
6298c2ecf20Sopenharmony_ci		col |= col << 16;
6308c2ecf20Sopenharmony_ci	} else
6318c2ecf20Sopenharmony_ci		col = ((u32 *)(info->pseudo_palette))[fr->color];
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	par->wait_engine(par);
6348c2ecf20Sopenharmony_ci	par->fill_rect(par, fr->dx, fr->dy, fr->width,
6358c2ecf20Sopenharmony_ci		       fr->height, col, fr->rop);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void tridentfb_imageblit(struct fb_info *info,
6398c2ecf20Sopenharmony_ci				const struct fb_image *img)
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
6428c2ecf20Sopenharmony_ci	int col, bgcol;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	if ((info->flags & FBINFO_HWACCEL_DISABLED) || img->depth != 1) {
6458c2ecf20Sopenharmony_ci		cfb_imageblit(info, img);
6468c2ecf20Sopenharmony_ci		return;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci	if (info->var.bits_per_pixel == 8) {
6498c2ecf20Sopenharmony_ci		col = img->fg_color;
6508c2ecf20Sopenharmony_ci		col |= col << 8;
6518c2ecf20Sopenharmony_ci		col |= col << 16;
6528c2ecf20Sopenharmony_ci		bgcol = img->bg_color;
6538c2ecf20Sopenharmony_ci		bgcol |= bgcol << 8;
6548c2ecf20Sopenharmony_ci		bgcol |= bgcol << 16;
6558c2ecf20Sopenharmony_ci	} else {
6568c2ecf20Sopenharmony_ci		col = ((u32 *)(info->pseudo_palette))[img->fg_color];
6578c2ecf20Sopenharmony_ci		bgcol = ((u32 *)(info->pseudo_palette))[img->bg_color];
6588c2ecf20Sopenharmony_ci	}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	par->wait_engine(par);
6618c2ecf20Sopenharmony_ci	if (par->image_blit)
6628c2ecf20Sopenharmony_ci		par->image_blit(par, img->data, img->dx, img->dy,
6638c2ecf20Sopenharmony_ci				img->width, img->height, col, bgcol);
6648c2ecf20Sopenharmony_ci	else
6658c2ecf20Sopenharmony_ci		cfb_imageblit(info, img);
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic void tridentfb_copyarea(struct fb_info *info,
6698c2ecf20Sopenharmony_ci			       const struct fb_copyarea *ca)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (info->flags & FBINFO_HWACCEL_DISABLED) {
6748c2ecf20Sopenharmony_ci		cfb_copyarea(info, ca);
6758c2ecf20Sopenharmony_ci		return;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci	par->wait_engine(par);
6788c2ecf20Sopenharmony_ci	par->copy_rect(par, ca->sx, ca->sy, ca->dx, ca->dy,
6798c2ecf20Sopenharmony_ci		       ca->width, ca->height);
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic int tridentfb_sync(struct fb_info *info)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (!(info->flags & FBINFO_HWACCEL_DISABLED))
6878c2ecf20Sopenharmony_ci		par->wait_engine(par);
6888c2ecf20Sopenharmony_ci	return 0;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci/*
6928c2ecf20Sopenharmony_ci * Hardware access functions
6938c2ecf20Sopenharmony_ci */
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_cistatic inline unsigned char read3X4(struct tridentfb_par *par, int reg)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	return vga_mm_rcrt(par->io_virt, reg);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic inline void write3X4(struct tridentfb_par *par, int reg,
7018c2ecf20Sopenharmony_ci			    unsigned char val)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	vga_mm_wcrt(par->io_virt, reg, val);
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic inline unsigned char read3CE(struct tridentfb_par *par,
7078c2ecf20Sopenharmony_ci				    unsigned char reg)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	return vga_mm_rgfx(par->io_virt, reg);
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_cistatic inline void writeAttr(struct tridentfb_par *par, int reg,
7138c2ecf20Sopenharmony_ci			     unsigned char val)
7148c2ecf20Sopenharmony_ci{
7158c2ecf20Sopenharmony_ci	fb_readb(par->io_virt + VGA_IS1_RC);	/* flip-flop to index */
7168c2ecf20Sopenharmony_ci	vga_mm_wattr(par->io_virt, reg, val);
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic inline void write3CE(struct tridentfb_par *par, int reg,
7208c2ecf20Sopenharmony_ci			    unsigned char val)
7218c2ecf20Sopenharmony_ci{
7228c2ecf20Sopenharmony_ci	vga_mm_wgfx(par->io_virt, reg, val);
7238c2ecf20Sopenharmony_ci}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_cistatic void enable_mmio(struct tridentfb_par *par)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	/* Goto New Mode */
7288c2ecf20Sopenharmony_ci	vga_io_rseq(0x0B);
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	/* Unprotect registers */
7318c2ecf20Sopenharmony_ci	vga_io_wseq(NewMode1, 0x80);
7328c2ecf20Sopenharmony_ci	if (!is_oldprotect(par->chip_id))
7338c2ecf20Sopenharmony_ci		vga_io_wseq(Protection, 0x92);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	/* Enable MMIO */
7368c2ecf20Sopenharmony_ci	outb(PCIReg, 0x3D4);
7378c2ecf20Sopenharmony_ci	outb(inb(0x3D5) | 0x01, 0x3D5);
7388c2ecf20Sopenharmony_ci}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic void disable_mmio(struct tridentfb_par *par)
7418c2ecf20Sopenharmony_ci{
7428c2ecf20Sopenharmony_ci	/* Goto New Mode */
7438c2ecf20Sopenharmony_ci	vga_mm_rseq(par->io_virt, 0x0B);
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	/* Unprotect registers */
7468c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, NewMode1, 0x80);
7478c2ecf20Sopenharmony_ci	if (!is_oldprotect(par->chip_id))
7488c2ecf20Sopenharmony_ci		vga_mm_wseq(par->io_virt, Protection, 0x92);
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	/* Disable MMIO */
7518c2ecf20Sopenharmony_ci	t_outb(par, PCIReg, 0x3D4);
7528c2ecf20Sopenharmony_ci	t_outb(par, t_inb(par, 0x3D5) & ~0x01, 0x3D5);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic inline void crtc_unlock(struct tridentfb_par *par)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_SYNC_END,
7588c2ecf20Sopenharmony_ci		 read3X4(par, VGA_CRTC_V_SYNC_END) & 0x7F);
7598c2ecf20Sopenharmony_ci}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci/*  Return flat panel's maximum x resolution */
7628c2ecf20Sopenharmony_cistatic int get_nativex(struct tridentfb_par *par)
7638c2ecf20Sopenharmony_ci{
7648c2ecf20Sopenharmony_ci	int x, y, tmp;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (nativex)
7678c2ecf20Sopenharmony_ci		return nativex;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	tmp = (read3CE(par, VertStretch) >> 4) & 3;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	switch (tmp) {
7728c2ecf20Sopenharmony_ci	case 0:
7738c2ecf20Sopenharmony_ci		x = 1280; y = 1024;
7748c2ecf20Sopenharmony_ci		break;
7758c2ecf20Sopenharmony_ci	case 2:
7768c2ecf20Sopenharmony_ci		x = 1024; y = 768;
7778c2ecf20Sopenharmony_ci		break;
7788c2ecf20Sopenharmony_ci	case 3:
7798c2ecf20Sopenharmony_ci		x = 800; y = 600;
7808c2ecf20Sopenharmony_ci		break;
7818c2ecf20Sopenharmony_ci	case 1:
7828c2ecf20Sopenharmony_ci	default:
7838c2ecf20Sopenharmony_ci		x = 640;  y = 480;
7848c2ecf20Sopenharmony_ci		break;
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	output("%dx%d flat panel found\n", x, y);
7888c2ecf20Sopenharmony_ci	return x;
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci/* Set pitch */
7928c2ecf20Sopenharmony_cistatic inline void set_lwidth(struct tridentfb_par *par, int width)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_OFFSET, width & 0xFF);
7958c2ecf20Sopenharmony_ci	/* chips older than TGUI9660 have only 1 width bit in AddColReg */
7968c2ecf20Sopenharmony_ci	/* touching the other one breaks I2C/DDC */
7978c2ecf20Sopenharmony_ci	if (par->chip_id == TGUI9440 || par->chip_id == CYBER9320)
7988c2ecf20Sopenharmony_ci		write3X4(par, AddColReg,
7998c2ecf20Sopenharmony_ci		     (read3X4(par, AddColReg) & 0xEF) | ((width & 0x100) >> 4));
8008c2ecf20Sopenharmony_ci	else
8018c2ecf20Sopenharmony_ci		write3X4(par, AddColReg,
8028c2ecf20Sopenharmony_ci		     (read3X4(par, AddColReg) & 0xCF) | ((width & 0x300) >> 4));
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci/* For resolutions smaller than FP resolution stretch */
8068c2ecf20Sopenharmony_cistatic void screen_stretch(struct tridentfb_par *par)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	if (par->chip_id != CYBERBLADEXPAi1)
8098c2ecf20Sopenharmony_ci		write3CE(par, BiosReg, 0);
8108c2ecf20Sopenharmony_ci	else
8118c2ecf20Sopenharmony_ci		write3CE(par, BiosReg, 8);
8128c2ecf20Sopenharmony_ci	write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 1);
8138c2ecf20Sopenharmony_ci	write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 1);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/* For resolutions smaller than FP resolution center */
8178c2ecf20Sopenharmony_cistatic inline void screen_center(struct tridentfb_par *par)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	write3CE(par, VertStretch, (read3CE(par, VertStretch) & 0x7C) | 0x80);
8208c2ecf20Sopenharmony_ci	write3CE(par, HorStretch, (read3CE(par, HorStretch) & 0x7C) | 0x80);
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci/* Address of first shown pixel in display memory */
8248c2ecf20Sopenharmony_cistatic void set_screen_start(struct tridentfb_par *par, int base)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	u8 tmp;
8278c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_START_LO, base & 0xFF);
8288c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_START_HI, (base & 0xFF00) >> 8);
8298c2ecf20Sopenharmony_ci	tmp = read3X4(par, CRTCModuleTest) & 0xDF;
8308c2ecf20Sopenharmony_ci	write3X4(par, CRTCModuleTest, tmp | ((base & 0x10000) >> 11));
8318c2ecf20Sopenharmony_ci	tmp = read3X4(par, CRTHiOrd) & 0xF8;
8328c2ecf20Sopenharmony_ci	write3X4(par, CRTHiOrd, tmp | ((base & 0xE0000) >> 17));
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci/* Set dotclock frequency */
8368c2ecf20Sopenharmony_cistatic void set_vclk(struct tridentfb_par *par, unsigned long freq)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	int m, n, k;
8398c2ecf20Sopenharmony_ci	unsigned long fi, d, di;
8408c2ecf20Sopenharmony_ci	unsigned char best_m = 0, best_n = 0, best_k = 0;
8418c2ecf20Sopenharmony_ci	unsigned char hi, lo;
8428c2ecf20Sopenharmony_ci	unsigned char shift = !is_oldclock(par->chip_id) ? 2 : 1;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	d = 20000;
8458c2ecf20Sopenharmony_ci	for (k = shift; k >= 0; k--)
8468c2ecf20Sopenharmony_ci		for (m = 1; m < 32; m++) {
8478c2ecf20Sopenharmony_ci			n = ((m + 2) << shift) - 8;
8488c2ecf20Sopenharmony_ci			for (n = (n < 0 ? 0 : n); n < 122; n++) {
8498c2ecf20Sopenharmony_ci				fi = ((14318l * (n + 8)) / (m + 2)) >> k;
8508c2ecf20Sopenharmony_ci				di = abs(fi - freq);
8518c2ecf20Sopenharmony_ci				if (di < d || (di == d && k == best_k)) {
8528c2ecf20Sopenharmony_ci					d = di;
8538c2ecf20Sopenharmony_ci					best_n = n;
8548c2ecf20Sopenharmony_ci					best_m = m;
8558c2ecf20Sopenharmony_ci					best_k = k;
8568c2ecf20Sopenharmony_ci				}
8578c2ecf20Sopenharmony_ci				if (fi > freq)
8588c2ecf20Sopenharmony_ci					break;
8598c2ecf20Sopenharmony_ci			}
8608c2ecf20Sopenharmony_ci		}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (is_oldclock(par->chip_id)) {
8638c2ecf20Sopenharmony_ci		lo = best_n | (best_m << 7);
8648c2ecf20Sopenharmony_ci		hi = (best_m >> 1) | (best_k << 4);
8658c2ecf20Sopenharmony_ci	} else {
8668c2ecf20Sopenharmony_ci		lo = best_n;
8678c2ecf20Sopenharmony_ci		hi = best_m | (best_k << 6);
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	if (is3Dchip(par->chip_id)) {
8718c2ecf20Sopenharmony_ci		vga_mm_wseq(par->io_virt, ClockHigh, hi);
8728c2ecf20Sopenharmony_ci		vga_mm_wseq(par->io_virt, ClockLow, lo);
8738c2ecf20Sopenharmony_ci	} else {
8748c2ecf20Sopenharmony_ci		t_outb(par, lo, 0x43C8);
8758c2ecf20Sopenharmony_ci		t_outb(par, hi, 0x43C9);
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci	debug("VCLK = %X %X\n", hi, lo);
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci/* Set number of lines for flat panels*/
8818c2ecf20Sopenharmony_cistatic void set_number_of_lines(struct tridentfb_par *par, int lines)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	int tmp = read3CE(par, CyberEnhance) & 0x8F;
8848c2ecf20Sopenharmony_ci	if (lines > 1024)
8858c2ecf20Sopenharmony_ci		tmp |= 0x50;
8868c2ecf20Sopenharmony_ci	else if (lines > 768)
8878c2ecf20Sopenharmony_ci		tmp |= 0x30;
8888c2ecf20Sopenharmony_ci	else if (lines > 600)
8898c2ecf20Sopenharmony_ci		tmp |= 0x20;
8908c2ecf20Sopenharmony_ci	else if (lines > 480)
8918c2ecf20Sopenharmony_ci		tmp |= 0x10;
8928c2ecf20Sopenharmony_ci	write3CE(par, CyberEnhance, tmp);
8938c2ecf20Sopenharmony_ci}
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci/*
8968c2ecf20Sopenharmony_ci * If we see that FP is active we assume we have one.
8978c2ecf20Sopenharmony_ci * Otherwise we have a CRT display. User can override.
8988c2ecf20Sopenharmony_ci */
8998c2ecf20Sopenharmony_cistatic int is_flatpanel(struct tridentfb_par *par)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	if (fp)
9028c2ecf20Sopenharmony_ci		return 1;
9038c2ecf20Sopenharmony_ci	if (crt || !iscyber(par->chip_id))
9048c2ecf20Sopenharmony_ci		return 0;
9058c2ecf20Sopenharmony_ci	return (read3CE(par, FPConfig) & 0x10) ? 1 : 0;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci/* Try detecting the video memory size */
9098c2ecf20Sopenharmony_cistatic unsigned int get_memsize(struct tridentfb_par *par)
9108c2ecf20Sopenharmony_ci{
9118c2ecf20Sopenharmony_ci	unsigned char tmp, tmp2;
9128c2ecf20Sopenharmony_ci	unsigned int k;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	/* If memory size provided by user */
9158c2ecf20Sopenharmony_ci	if (memsize)
9168c2ecf20Sopenharmony_ci		k = memsize * Kb;
9178c2ecf20Sopenharmony_ci	else
9188c2ecf20Sopenharmony_ci		switch (par->chip_id) {
9198c2ecf20Sopenharmony_ci		case CYBER9525DVD:
9208c2ecf20Sopenharmony_ci			k = 2560 * Kb;
9218c2ecf20Sopenharmony_ci			break;
9228c2ecf20Sopenharmony_ci		default:
9238c2ecf20Sopenharmony_ci			tmp = read3X4(par, SPR) & 0x0F;
9248c2ecf20Sopenharmony_ci			switch (tmp) {
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci			case 0x01:
9278c2ecf20Sopenharmony_ci				k = 512 * Kb;
9288c2ecf20Sopenharmony_ci				break;
9298c2ecf20Sopenharmony_ci			case 0x02:
9308c2ecf20Sopenharmony_ci				k = 6 * Mb;	/* XP */
9318c2ecf20Sopenharmony_ci				break;
9328c2ecf20Sopenharmony_ci			case 0x03:
9338c2ecf20Sopenharmony_ci				k = 1 * Mb;
9348c2ecf20Sopenharmony_ci				break;
9358c2ecf20Sopenharmony_ci			case 0x04:
9368c2ecf20Sopenharmony_ci				k = 8 * Mb;
9378c2ecf20Sopenharmony_ci				break;
9388c2ecf20Sopenharmony_ci			case 0x06:
9398c2ecf20Sopenharmony_ci				k = 10 * Mb;	/* XP */
9408c2ecf20Sopenharmony_ci				break;
9418c2ecf20Sopenharmony_ci			case 0x07:
9428c2ecf20Sopenharmony_ci				k = 2 * Mb;
9438c2ecf20Sopenharmony_ci				break;
9448c2ecf20Sopenharmony_ci			case 0x08:
9458c2ecf20Sopenharmony_ci				k = 12 * Mb;	/* XP */
9468c2ecf20Sopenharmony_ci				break;
9478c2ecf20Sopenharmony_ci			case 0x0A:
9488c2ecf20Sopenharmony_ci				k = 14 * Mb;	/* XP */
9498c2ecf20Sopenharmony_ci				break;
9508c2ecf20Sopenharmony_ci			case 0x0C:
9518c2ecf20Sopenharmony_ci				k = 16 * Mb;	/* XP */
9528c2ecf20Sopenharmony_ci				break;
9538c2ecf20Sopenharmony_ci			case 0x0E:		/* XP */
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci				tmp2 = vga_mm_rseq(par->io_virt, 0xC1);
9568c2ecf20Sopenharmony_ci				switch (tmp2) {
9578c2ecf20Sopenharmony_ci				case 0x00:
9588c2ecf20Sopenharmony_ci					k = 20 * Mb;
9598c2ecf20Sopenharmony_ci					break;
9608c2ecf20Sopenharmony_ci				case 0x01:
9618c2ecf20Sopenharmony_ci					k = 24 * Mb;
9628c2ecf20Sopenharmony_ci					break;
9638c2ecf20Sopenharmony_ci				case 0x10:
9648c2ecf20Sopenharmony_ci					k = 28 * Mb;
9658c2ecf20Sopenharmony_ci					break;
9668c2ecf20Sopenharmony_ci				case 0x11:
9678c2ecf20Sopenharmony_ci					k = 32 * Mb;
9688c2ecf20Sopenharmony_ci					break;
9698c2ecf20Sopenharmony_ci				default:
9708c2ecf20Sopenharmony_ci					k = 1 * Mb;
9718c2ecf20Sopenharmony_ci					break;
9728c2ecf20Sopenharmony_ci				}
9738c2ecf20Sopenharmony_ci				break;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci			case 0x0F:
9768c2ecf20Sopenharmony_ci				k = 4 * Mb;
9778c2ecf20Sopenharmony_ci				break;
9788c2ecf20Sopenharmony_ci			default:
9798c2ecf20Sopenharmony_ci				k = 1 * Mb;
9808c2ecf20Sopenharmony_ci				break;
9818c2ecf20Sopenharmony_ci			}
9828c2ecf20Sopenharmony_ci		}
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	k -= memdiff * Kb;
9858c2ecf20Sopenharmony_ci	output("framebuffer size = %d Kb\n", k / Kb);
9868c2ecf20Sopenharmony_ci	return k;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci/* See if we can handle the video mode described in var */
9908c2ecf20Sopenharmony_cistatic int tridentfb_check_var(struct fb_var_screeninfo *var,
9918c2ecf20Sopenharmony_ci			       struct fb_info *info)
9928c2ecf20Sopenharmony_ci{
9938c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
9948c2ecf20Sopenharmony_ci	int bpp = var->bits_per_pixel;
9958c2ecf20Sopenharmony_ci	int line_length;
9968c2ecf20Sopenharmony_ci	int ramdac = 230000; /* 230MHz for most 3D chips */
9978c2ecf20Sopenharmony_ci	debug("enter\n");
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	/* check color depth */
10008c2ecf20Sopenharmony_ci	if (bpp == 24)
10018c2ecf20Sopenharmony_ci		bpp = var->bits_per_pixel = 32;
10028c2ecf20Sopenharmony_ci	if (bpp != 8 && bpp != 16 && bpp != 32)
10038c2ecf20Sopenharmony_ci		return -EINVAL;
10048c2ecf20Sopenharmony_ci	if (par->chip_id == TGUI9440 && bpp == 32)
10058c2ecf20Sopenharmony_ci		return -EINVAL;
10068c2ecf20Sopenharmony_ci	/* check whether resolution fits on panel and in memory */
10078c2ecf20Sopenharmony_ci	if (par->flatpanel && nativex && var->xres > nativex)
10088c2ecf20Sopenharmony_ci		return -EINVAL;
10098c2ecf20Sopenharmony_ci	/* various resolution checks */
10108c2ecf20Sopenharmony_ci	var->xres = (var->xres + 7) & ~0x7;
10118c2ecf20Sopenharmony_ci	if (var->xres > var->xres_virtual)
10128c2ecf20Sopenharmony_ci		var->xres_virtual = var->xres;
10138c2ecf20Sopenharmony_ci	if (var->yres > var->yres_virtual)
10148c2ecf20Sopenharmony_ci		var->yres_virtual = var->yres;
10158c2ecf20Sopenharmony_ci	if (var->xres_virtual > 4095 || var->yres > 2048)
10168c2ecf20Sopenharmony_ci		return -EINVAL;
10178c2ecf20Sopenharmony_ci	/* prevent from position overflow for acceleration */
10188c2ecf20Sopenharmony_ci	if (var->yres_virtual > 0xffff)
10198c2ecf20Sopenharmony_ci		return -EINVAL;
10208c2ecf20Sopenharmony_ci	line_length = var->xres_virtual * bpp / 8;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (!is3Dchip(par->chip_id) &&
10238c2ecf20Sopenharmony_ci	    !(info->flags & FBINFO_HWACCEL_DISABLED)) {
10248c2ecf20Sopenharmony_ci		/* acceleration requires line length to be power of 2 */
10258c2ecf20Sopenharmony_ci		if (line_length <= 512)
10268c2ecf20Sopenharmony_ci			var->xres_virtual = 512 * 8 / bpp;
10278c2ecf20Sopenharmony_ci		else if (line_length <= 1024)
10288c2ecf20Sopenharmony_ci			var->xres_virtual = 1024 * 8 / bpp;
10298c2ecf20Sopenharmony_ci		else if (line_length <= 2048)
10308c2ecf20Sopenharmony_ci			var->xres_virtual = 2048 * 8 / bpp;
10318c2ecf20Sopenharmony_ci		else if (line_length <= 4096)
10328c2ecf20Sopenharmony_ci			var->xres_virtual = 4096 * 8 / bpp;
10338c2ecf20Sopenharmony_ci		else if (line_length <= 8192)
10348c2ecf20Sopenharmony_ci			var->xres_virtual = 8192 * 8 / bpp;
10358c2ecf20Sopenharmony_ci		else
10368c2ecf20Sopenharmony_ci			return -EINVAL;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci		line_length = var->xres_virtual * bpp / 8;
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* datasheet specifies how to set panning only up to 4 MB */
10428c2ecf20Sopenharmony_ci	if (line_length * (var->yres_virtual - var->yres) > (4 << 20))
10438c2ecf20Sopenharmony_ci		var->yres_virtual = ((4 << 20) / line_length) + var->yres;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	if (line_length * var->yres_virtual > info->fix.smem_len)
10468c2ecf20Sopenharmony_ci		return -EINVAL;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	switch (bpp) {
10498c2ecf20Sopenharmony_ci	case 8:
10508c2ecf20Sopenharmony_ci		var->red.offset = 0;
10518c2ecf20Sopenharmony_ci		var->red.length = 8;
10528c2ecf20Sopenharmony_ci		var->green = var->red;
10538c2ecf20Sopenharmony_ci		var->blue = var->red;
10548c2ecf20Sopenharmony_ci		break;
10558c2ecf20Sopenharmony_ci	case 16:
10568c2ecf20Sopenharmony_ci		var->red.offset = 11;
10578c2ecf20Sopenharmony_ci		var->green.offset = 5;
10588c2ecf20Sopenharmony_ci		var->blue.offset = 0;
10598c2ecf20Sopenharmony_ci		var->red.length = 5;
10608c2ecf20Sopenharmony_ci		var->green.length = 6;
10618c2ecf20Sopenharmony_ci		var->blue.length = 5;
10628c2ecf20Sopenharmony_ci		break;
10638c2ecf20Sopenharmony_ci	case 32:
10648c2ecf20Sopenharmony_ci		var->red.offset = 16;
10658c2ecf20Sopenharmony_ci		var->green.offset = 8;
10668c2ecf20Sopenharmony_ci		var->blue.offset = 0;
10678c2ecf20Sopenharmony_ci		var->red.length = 8;
10688c2ecf20Sopenharmony_ci		var->green.length = 8;
10698c2ecf20Sopenharmony_ci		var->blue.length = 8;
10708c2ecf20Sopenharmony_ci		break;
10718c2ecf20Sopenharmony_ci	default:
10728c2ecf20Sopenharmony_ci		return -EINVAL;
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	if (is_xp(par->chip_id))
10768c2ecf20Sopenharmony_ci		ramdac = 350000;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	switch (par->chip_id) {
10798c2ecf20Sopenharmony_ci	case TGUI9440:
10808c2ecf20Sopenharmony_ci		ramdac = (bpp >= 16) ? 45000 : 90000;
10818c2ecf20Sopenharmony_ci		break;
10828c2ecf20Sopenharmony_ci	case CYBER9320:
10838c2ecf20Sopenharmony_ci	case TGUI9660:
10848c2ecf20Sopenharmony_ci		ramdac = 135000;
10858c2ecf20Sopenharmony_ci		break;
10868c2ecf20Sopenharmony_ci	case PROVIDIA9685:
10878c2ecf20Sopenharmony_ci	case CYBER9388:
10888c2ecf20Sopenharmony_ci	case CYBER9382:
10898c2ecf20Sopenharmony_ci	case CYBER9385:
10908c2ecf20Sopenharmony_ci		ramdac = 170000;
10918c2ecf20Sopenharmony_ci		break;
10928c2ecf20Sopenharmony_ci	}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	/* The clock is doubled for 32 bpp */
10958c2ecf20Sopenharmony_ci	if (bpp == 32)
10968c2ecf20Sopenharmony_ci		ramdac /= 2;
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	if (PICOS2KHZ(var->pixclock) > ramdac)
10998c2ecf20Sopenharmony_ci		return -EINVAL;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	debug("exit\n");
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return 0;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci/* Pan the display */
11088c2ecf20Sopenharmony_cistatic int tridentfb_pan_display(struct fb_var_screeninfo *var,
11098c2ecf20Sopenharmony_ci				 struct fb_info *info)
11108c2ecf20Sopenharmony_ci{
11118c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
11128c2ecf20Sopenharmony_ci	unsigned int offset;
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	debug("enter\n");
11158c2ecf20Sopenharmony_ci	offset = (var->xoffset + (var->yoffset * info->var.xres_virtual))
11168c2ecf20Sopenharmony_ci		* info->var.bits_per_pixel / 32;
11178c2ecf20Sopenharmony_ci	set_screen_start(par, offset);
11188c2ecf20Sopenharmony_ci	debug("exit\n");
11198c2ecf20Sopenharmony_ci	return 0;
11208c2ecf20Sopenharmony_ci}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_cistatic inline void shadowmode_on(struct tridentfb_par *par)
11238c2ecf20Sopenharmony_ci{
11248c2ecf20Sopenharmony_ci	write3CE(par, CyberControl, read3CE(par, CyberControl) | 0x81);
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic inline void shadowmode_off(struct tridentfb_par *par)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	write3CE(par, CyberControl, read3CE(par, CyberControl) & 0x7E);
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci/* Set the hardware to the requested video mode */
11338c2ecf20Sopenharmony_cistatic int tridentfb_set_par(struct fb_info *info)
11348c2ecf20Sopenharmony_ci{
11358c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
11368c2ecf20Sopenharmony_ci	u32 htotal, hdispend, hsyncstart, hsyncend, hblankstart, hblankend;
11378c2ecf20Sopenharmony_ci	u32 vtotal, vdispend, vsyncstart, vsyncend, vblankstart, vblankend;
11388c2ecf20Sopenharmony_ci	struct fb_var_screeninfo *var = &info->var;
11398c2ecf20Sopenharmony_ci	int bpp = var->bits_per_pixel;
11408c2ecf20Sopenharmony_ci	unsigned char tmp;
11418c2ecf20Sopenharmony_ci	unsigned long vclk;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	debug("enter\n");
11448c2ecf20Sopenharmony_ci	hdispend = var->xres / 8 - 1;
11458c2ecf20Sopenharmony_ci	hsyncstart = (var->xres + var->right_margin) / 8;
11468c2ecf20Sopenharmony_ci	hsyncend = (var->xres + var->right_margin + var->hsync_len) / 8;
11478c2ecf20Sopenharmony_ci	htotal = (var->xres + var->left_margin + var->right_margin +
11488c2ecf20Sopenharmony_ci		  var->hsync_len) / 8 - 5;
11498c2ecf20Sopenharmony_ci	hblankstart = hdispend + 1;
11508c2ecf20Sopenharmony_ci	hblankend = htotal + 3;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	vdispend = var->yres - 1;
11538c2ecf20Sopenharmony_ci	vsyncstart = var->yres + var->lower_margin;
11548c2ecf20Sopenharmony_ci	vsyncend = vsyncstart + var->vsync_len;
11558c2ecf20Sopenharmony_ci	vtotal = var->upper_margin + vsyncend - 2;
11568c2ecf20Sopenharmony_ci	vblankstart = vdispend + 1;
11578c2ecf20Sopenharmony_ci	vblankend = vtotal;
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	if (info->var.vmode & FB_VMODE_INTERLACED) {
11608c2ecf20Sopenharmony_ci		vtotal /= 2;
11618c2ecf20Sopenharmony_ci		vdispend /= 2;
11628c2ecf20Sopenharmony_ci		vsyncstart /= 2;
11638c2ecf20Sopenharmony_ci		vsyncend /= 2;
11648c2ecf20Sopenharmony_ci		vblankstart /= 2;
11658c2ecf20Sopenharmony_ci		vblankend /= 2;
11668c2ecf20Sopenharmony_ci	}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	enable_mmio(par);
11698c2ecf20Sopenharmony_ci	crtc_unlock(par);
11708c2ecf20Sopenharmony_ci	write3CE(par, CyberControl, 8);
11718c2ecf20Sopenharmony_ci	tmp = 0xEB;
11728c2ecf20Sopenharmony_ci	if (var->sync & FB_SYNC_HOR_HIGH_ACT)
11738c2ecf20Sopenharmony_ci		tmp &= ~0x40;
11748c2ecf20Sopenharmony_ci	if (var->sync & FB_SYNC_VERT_HIGH_ACT)
11758c2ecf20Sopenharmony_ci		tmp &= ~0x80;
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (par->flatpanel && var->xres < nativex) {
11788c2ecf20Sopenharmony_ci		/*
11798c2ecf20Sopenharmony_ci		 * on flat panels with native size larger
11808c2ecf20Sopenharmony_ci		 * than requested resolution decide whether
11818c2ecf20Sopenharmony_ci		 * we stretch or center
11828c2ecf20Sopenharmony_ci		 */
11838c2ecf20Sopenharmony_ci		t_outb(par, tmp | 0xC0, VGA_MIS_W);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		shadowmode_on(par);
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		if (center)
11888c2ecf20Sopenharmony_ci			screen_center(par);
11898c2ecf20Sopenharmony_ci		else if (stretch)
11908c2ecf20Sopenharmony_ci			screen_stretch(par);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	} else {
11938c2ecf20Sopenharmony_ci		t_outb(par, tmp, VGA_MIS_W);
11948c2ecf20Sopenharmony_ci		write3CE(par, CyberControl, 8);
11958c2ecf20Sopenharmony_ci	}
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	/* vertical timing values */
11988c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_TOTAL, vtotal & 0xFF);
11998c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_DISP_END, vdispend & 0xFF);
12008c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_SYNC_START, vsyncstart & 0xFF);
12018c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_SYNC_END, (vsyncend & 0x0F));
12028c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_BLANK_START, vblankstart & 0xFF);
12038c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_V_BLANK_END, vblankend & 0xFF);
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	/* horizontal timing values */
12068c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_TOTAL, htotal & 0xFF);
12078c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_DISP, hdispend & 0xFF);
12088c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_SYNC_START, hsyncstart & 0xFF);
12098c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_SYNC_END,
12108c2ecf20Sopenharmony_ci		 (hsyncend & 0x1F) | ((hblankend & 0x20) << 2));
12118c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_BLANK_START, hblankstart & 0xFF);
12128c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_H_BLANK_END, hblankend & 0x1F);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	/* higher bits of vertical timing values */
12158c2ecf20Sopenharmony_ci	tmp = 0x10;
12168c2ecf20Sopenharmony_ci	if (vtotal & 0x100) tmp |= 0x01;
12178c2ecf20Sopenharmony_ci	if (vdispend & 0x100) tmp |= 0x02;
12188c2ecf20Sopenharmony_ci	if (vsyncstart & 0x100) tmp |= 0x04;
12198c2ecf20Sopenharmony_ci	if (vblankstart & 0x100) tmp |= 0x08;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	if (vtotal & 0x200) tmp |= 0x20;
12228c2ecf20Sopenharmony_ci	if (vdispend & 0x200) tmp |= 0x40;
12238c2ecf20Sopenharmony_ci	if (vsyncstart & 0x200) tmp |= 0x80;
12248c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_OVERFLOW, tmp);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	tmp = read3X4(par, CRTHiOrd) & 0x07;
12278c2ecf20Sopenharmony_ci	tmp |= 0x08;	/* line compare bit 10 */
12288c2ecf20Sopenharmony_ci	if (vtotal & 0x400) tmp |= 0x80;
12298c2ecf20Sopenharmony_ci	if (vblankstart & 0x400) tmp |= 0x40;
12308c2ecf20Sopenharmony_ci	if (vsyncstart & 0x400) tmp |= 0x20;
12318c2ecf20Sopenharmony_ci	if (vdispend & 0x400) tmp |= 0x10;
12328c2ecf20Sopenharmony_ci	write3X4(par, CRTHiOrd, tmp);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	tmp = (htotal >> 8) & 0x01;
12358c2ecf20Sopenharmony_ci	tmp |= (hdispend >> 7) & 0x02;
12368c2ecf20Sopenharmony_ci	tmp |= (hsyncstart >> 5) & 0x08;
12378c2ecf20Sopenharmony_ci	tmp |= (hblankstart >> 4) & 0x10;
12388c2ecf20Sopenharmony_ci	write3X4(par, HorizOverflow, tmp);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	tmp = 0x40;
12418c2ecf20Sopenharmony_ci	if (vblankstart & 0x200) tmp |= 0x20;
12428c2ecf20Sopenharmony_ci//FIXME	if (info->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80;  /* double scan for 200 line modes */
12438c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_MAX_SCAN, tmp);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_LINE_COMPARE, 0xFF);
12468c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_PRESET_ROW, 0);
12478c2ecf20Sopenharmony_ci	write3X4(par, VGA_CRTC_MODE, 0xC3);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci	write3X4(par, LinearAddReg, 0x20);	/* enable linear addressing */
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci	tmp = (info->var.vmode & FB_VMODE_INTERLACED) ? 0x84 : 0x80;
12528c2ecf20Sopenharmony_ci	/* enable access extended memory */
12538c2ecf20Sopenharmony_ci	write3X4(par, CRTCModuleTest, tmp);
12548c2ecf20Sopenharmony_ci	tmp = read3CE(par, MiscIntContReg) & ~0x4;
12558c2ecf20Sopenharmony_ci	if (info->var.vmode & FB_VMODE_INTERLACED)
12568c2ecf20Sopenharmony_ci		tmp |= 0x4;
12578c2ecf20Sopenharmony_ci	write3CE(par, MiscIntContReg, tmp);
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	/* enable GE for text acceleration */
12608c2ecf20Sopenharmony_ci	write3X4(par, GraphEngReg, 0x80);
12618c2ecf20Sopenharmony_ci
12628c2ecf20Sopenharmony_ci	switch (bpp) {
12638c2ecf20Sopenharmony_ci	case 8:
12648c2ecf20Sopenharmony_ci		tmp = 0x00;
12658c2ecf20Sopenharmony_ci		break;
12668c2ecf20Sopenharmony_ci	case 16:
12678c2ecf20Sopenharmony_ci		tmp = 0x05;
12688c2ecf20Sopenharmony_ci		break;
12698c2ecf20Sopenharmony_ci	case 24:
12708c2ecf20Sopenharmony_ci		tmp = 0x29;
12718c2ecf20Sopenharmony_ci		break;
12728c2ecf20Sopenharmony_ci	case 32:
12738c2ecf20Sopenharmony_ci		tmp = 0x09;
12748c2ecf20Sopenharmony_ci		break;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	write3X4(par, PixelBusReg, tmp);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	tmp = read3X4(par, DRAMControl);
12808c2ecf20Sopenharmony_ci	if (!is_oldprotect(par->chip_id))
12818c2ecf20Sopenharmony_ci		tmp |= 0x10;
12828c2ecf20Sopenharmony_ci	if (iscyber(par->chip_id))
12838c2ecf20Sopenharmony_ci		tmp |= 0x20;
12848c2ecf20Sopenharmony_ci	write3X4(par, DRAMControl, tmp);	/* both IO, linear enable */
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	write3X4(par, InterfaceSel, read3X4(par, InterfaceSel) | 0x40);
12878c2ecf20Sopenharmony_ci	if (!is_xp(par->chip_id))
12888c2ecf20Sopenharmony_ci		write3X4(par, Performance, read3X4(par, Performance) | 0x10);
12898c2ecf20Sopenharmony_ci	/* MMIO & PCI read and write burst enable */
12908c2ecf20Sopenharmony_ci	if (par->chip_id != TGUI9440 && par->chip_id != IMAGE975)
12918c2ecf20Sopenharmony_ci		write3X4(par, PCIReg, read3X4(par, PCIReg) | 0x06);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, 0, 3);
12948c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, 1, 1); /* set char clock 8 dots wide */
12958c2ecf20Sopenharmony_ci	/* enable 4 maps because needed in chain4 mode */
12968c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, 2, 0x0F);
12978c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, 3, 0);
12988c2ecf20Sopenharmony_ci	vga_mm_wseq(par->io_virt, 4, 0x0E); /* memory mode enable bitmaps ?? */
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	/* convert from picoseconds to kHz */
13018c2ecf20Sopenharmony_ci	vclk = PICOS2KHZ(info->var.pixclock);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	/* divide clock by 2 if 32bpp chain4 mode display and CPU path */
13048c2ecf20Sopenharmony_ci	tmp = read3CE(par, MiscExtFunc) & 0xF0;
13058c2ecf20Sopenharmony_ci	if (bpp == 32 || (par->chip_id == TGUI9440 && bpp == 16)) {
13068c2ecf20Sopenharmony_ci		tmp |= 8;
13078c2ecf20Sopenharmony_ci		vclk *= 2;
13088c2ecf20Sopenharmony_ci	}
13098c2ecf20Sopenharmony_ci	set_vclk(par, vclk);
13108c2ecf20Sopenharmony_ci	write3CE(par, MiscExtFunc, tmp | 0x12);
13118c2ecf20Sopenharmony_ci	write3CE(par, 0x5, 0x40);	/* no CGA compat, allow 256 col */
13128c2ecf20Sopenharmony_ci	write3CE(par, 0x6, 0x05);	/* graphics mode */
13138c2ecf20Sopenharmony_ci	write3CE(par, 0x7, 0x0F);	/* planes? */
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci	/* graphics mode and support 256 color modes */
13168c2ecf20Sopenharmony_ci	writeAttr(par, 0x10, 0x41);
13178c2ecf20Sopenharmony_ci	writeAttr(par, 0x12, 0x0F);	/* planes */
13188c2ecf20Sopenharmony_ci	writeAttr(par, 0x13, 0);	/* horizontal pel panning */
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	/* colors */
13218c2ecf20Sopenharmony_ci	for (tmp = 0; tmp < 0x10; tmp++)
13228c2ecf20Sopenharmony_ci		writeAttr(par, tmp, tmp);
13238c2ecf20Sopenharmony_ci	fb_readb(par->io_virt + VGA_IS1_RC);	/* flip-flop to index */
13248c2ecf20Sopenharmony_ci	t_outb(par, 0x20, VGA_ATT_W);		/* enable attr */
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	switch (bpp) {
13278c2ecf20Sopenharmony_ci	case 8:
13288c2ecf20Sopenharmony_ci		tmp = 0;
13298c2ecf20Sopenharmony_ci		break;
13308c2ecf20Sopenharmony_ci	case 16:
13318c2ecf20Sopenharmony_ci		tmp = 0x30;
13328c2ecf20Sopenharmony_ci		break;
13338c2ecf20Sopenharmony_ci	case 24:
13348c2ecf20Sopenharmony_ci	case 32:
13358c2ecf20Sopenharmony_ci		tmp = 0xD0;
13368c2ecf20Sopenharmony_ci		break;
13378c2ecf20Sopenharmony_ci	}
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_IW);
13408c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_MSK);
13418c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_MSK);
13428c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_MSK);
13438c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_MSK);
13448c2ecf20Sopenharmony_ci	t_outb(par, tmp, VGA_PEL_MSK);
13458c2ecf20Sopenharmony_ci	t_inb(par, VGA_PEL_IW);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	if (par->flatpanel)
13488c2ecf20Sopenharmony_ci		set_number_of_lines(par, info->var.yres);
13498c2ecf20Sopenharmony_ci	info->fix.line_length = info->var.xres_virtual * bpp / 8;
13508c2ecf20Sopenharmony_ci	set_lwidth(par, info->fix.line_length / 8);
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	if (!(info->flags & FBINFO_HWACCEL_DISABLED))
13538c2ecf20Sopenharmony_ci		par->init_accel(par, info->var.xres_virtual, bpp);
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	info->fix.visual = (bpp == 8) ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR;
13568c2ecf20Sopenharmony_ci	info->cmap.len = (bpp == 8) ? 256 : 16;
13578c2ecf20Sopenharmony_ci	debug("exit\n");
13588c2ecf20Sopenharmony_ci	return 0;
13598c2ecf20Sopenharmony_ci}
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci/* Set one color register */
13628c2ecf20Sopenharmony_cistatic int tridentfb_setcolreg(unsigned regno, unsigned red, unsigned green,
13638c2ecf20Sopenharmony_ci			       unsigned blue, unsigned transp,
13648c2ecf20Sopenharmony_ci			       struct fb_info *info)
13658c2ecf20Sopenharmony_ci{
13668c2ecf20Sopenharmony_ci	int bpp = info->var.bits_per_pixel;
13678c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	if (regno >= info->cmap.len)
13708c2ecf20Sopenharmony_ci		return 1;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	if (bpp == 8) {
13738c2ecf20Sopenharmony_ci		t_outb(par, 0xFF, VGA_PEL_MSK);
13748c2ecf20Sopenharmony_ci		t_outb(par, regno, VGA_PEL_IW);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci		t_outb(par, red >> 10, VGA_PEL_D);
13778c2ecf20Sopenharmony_ci		t_outb(par, green >> 10, VGA_PEL_D);
13788c2ecf20Sopenharmony_ci		t_outb(par, blue >> 10, VGA_PEL_D);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	} else if (regno < 16) {
13818c2ecf20Sopenharmony_ci		if (bpp == 16) {	/* RGB 565 */
13828c2ecf20Sopenharmony_ci			u32 col;
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci			col = (red & 0xF800) | ((green & 0xFC00) >> 5) |
13858c2ecf20Sopenharmony_ci				((blue & 0xF800) >> 11);
13868c2ecf20Sopenharmony_ci			col |= col << 16;
13878c2ecf20Sopenharmony_ci			((u32 *)(info->pseudo_palette))[regno] = col;
13888c2ecf20Sopenharmony_ci		} else if (bpp == 32)		/* ARGB 8888 */
13898c2ecf20Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] =
13908c2ecf20Sopenharmony_ci				((transp & 0xFF00) << 16)	|
13918c2ecf20Sopenharmony_ci				((red & 0xFF00) << 8)		|
13928c2ecf20Sopenharmony_ci				((green & 0xFF00))		|
13938c2ecf20Sopenharmony_ci				((blue & 0xFF00) >> 8);
13948c2ecf20Sopenharmony_ci	}
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci	return 0;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci/* Try blanking the screen. For flat panels it does nothing */
14008c2ecf20Sopenharmony_cistatic int tridentfb_blank(int blank_mode, struct fb_info *info)
14018c2ecf20Sopenharmony_ci{
14028c2ecf20Sopenharmony_ci	unsigned char PMCont, DPMSCont;
14038c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci	debug("enter\n");
14068c2ecf20Sopenharmony_ci	if (par->flatpanel)
14078c2ecf20Sopenharmony_ci		return 0;
14088c2ecf20Sopenharmony_ci	t_outb(par, 0x04, 0x83C8); /* Read DPMS Control */
14098c2ecf20Sopenharmony_ci	PMCont = t_inb(par, 0x83C6) & 0xFC;
14108c2ecf20Sopenharmony_ci	DPMSCont = read3CE(par, PowerStatus) & 0xFC;
14118c2ecf20Sopenharmony_ci	switch (blank_mode) {
14128c2ecf20Sopenharmony_ci	case FB_BLANK_UNBLANK:
14138c2ecf20Sopenharmony_ci		/* Screen: On, HSync: On, VSync: On */
14148c2ecf20Sopenharmony_ci	case FB_BLANK_NORMAL:
14158c2ecf20Sopenharmony_ci		/* Screen: Off, HSync: On, VSync: On */
14168c2ecf20Sopenharmony_ci		PMCont |= 0x03;
14178c2ecf20Sopenharmony_ci		DPMSCont |= 0x00;
14188c2ecf20Sopenharmony_ci		break;
14198c2ecf20Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND:
14208c2ecf20Sopenharmony_ci		/* Screen: Off, HSync: Off, VSync: On */
14218c2ecf20Sopenharmony_ci		PMCont |= 0x02;
14228c2ecf20Sopenharmony_ci		DPMSCont |= 0x01;
14238c2ecf20Sopenharmony_ci		break;
14248c2ecf20Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND:
14258c2ecf20Sopenharmony_ci		/* Screen: Off, HSync: On, VSync: Off */
14268c2ecf20Sopenharmony_ci		PMCont |= 0x02;
14278c2ecf20Sopenharmony_ci		DPMSCont |= 0x02;
14288c2ecf20Sopenharmony_ci		break;
14298c2ecf20Sopenharmony_ci	case FB_BLANK_POWERDOWN:
14308c2ecf20Sopenharmony_ci		/* Screen: Off, HSync: Off, VSync: Off */
14318c2ecf20Sopenharmony_ci		PMCont |= 0x00;
14328c2ecf20Sopenharmony_ci		DPMSCont |= 0x03;
14338c2ecf20Sopenharmony_ci		break;
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	write3CE(par, PowerStatus, DPMSCont);
14378c2ecf20Sopenharmony_ci	t_outb(par, 4, 0x83C8);
14388c2ecf20Sopenharmony_ci	t_outb(par, PMCont, 0x83C6);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	debug("exit\n");
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* let fbcon do a softblank for us */
14438c2ecf20Sopenharmony_ci	return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic const struct fb_ops tridentfb_ops = {
14478c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
14488c2ecf20Sopenharmony_ci	.fb_setcolreg = tridentfb_setcolreg,
14498c2ecf20Sopenharmony_ci	.fb_pan_display = tridentfb_pan_display,
14508c2ecf20Sopenharmony_ci	.fb_blank = tridentfb_blank,
14518c2ecf20Sopenharmony_ci	.fb_check_var = tridentfb_check_var,
14528c2ecf20Sopenharmony_ci	.fb_set_par = tridentfb_set_par,
14538c2ecf20Sopenharmony_ci	.fb_fillrect = tridentfb_fillrect,
14548c2ecf20Sopenharmony_ci	.fb_copyarea = tridentfb_copyarea,
14558c2ecf20Sopenharmony_ci	.fb_imageblit = tridentfb_imageblit,
14568c2ecf20Sopenharmony_ci	.fb_sync = tridentfb_sync,
14578c2ecf20Sopenharmony_ci};
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic int trident_pci_probe(struct pci_dev *dev,
14608c2ecf20Sopenharmony_ci			     const struct pci_device_id *id)
14618c2ecf20Sopenharmony_ci{
14628c2ecf20Sopenharmony_ci	int err;
14638c2ecf20Sopenharmony_ci	unsigned char revision;
14648c2ecf20Sopenharmony_ci	struct fb_info *info;
14658c2ecf20Sopenharmony_ci	struct tridentfb_par *default_par;
14668c2ecf20Sopenharmony_ci	int chip3D;
14678c2ecf20Sopenharmony_ci	int chip_id;
14688c2ecf20Sopenharmony_ci	bool found = false;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	err = pci_enable_device(dev);
14718c2ecf20Sopenharmony_ci	if (err)
14728c2ecf20Sopenharmony_ci		return err;
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct tridentfb_par), &dev->dev);
14758c2ecf20Sopenharmony_ci	if (!info)
14768c2ecf20Sopenharmony_ci		return -ENOMEM;
14778c2ecf20Sopenharmony_ci	default_par = info->par;
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	chip_id = id->device;
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	/* If PCI id is 0x9660 then further detect chip type */
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (chip_id == TGUI9660) {
14848c2ecf20Sopenharmony_ci		revision = vga_io_rseq(RevisionID);
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci		switch (revision) {
14878c2ecf20Sopenharmony_ci		case 0x21:
14888c2ecf20Sopenharmony_ci			chip_id = PROVIDIA9685;
14898c2ecf20Sopenharmony_ci			break;
14908c2ecf20Sopenharmony_ci		case 0x22:
14918c2ecf20Sopenharmony_ci		case 0x23:
14928c2ecf20Sopenharmony_ci			chip_id = CYBER9397;
14938c2ecf20Sopenharmony_ci			break;
14948c2ecf20Sopenharmony_ci		case 0x2A:
14958c2ecf20Sopenharmony_ci			chip_id = CYBER9397DVD;
14968c2ecf20Sopenharmony_ci			break;
14978c2ecf20Sopenharmony_ci		case 0x30:
14988c2ecf20Sopenharmony_ci		case 0x33:
14998c2ecf20Sopenharmony_ci		case 0x34:
15008c2ecf20Sopenharmony_ci		case 0x35:
15018c2ecf20Sopenharmony_ci		case 0x38:
15028c2ecf20Sopenharmony_ci		case 0x3A:
15038c2ecf20Sopenharmony_ci		case 0xB3:
15048c2ecf20Sopenharmony_ci			chip_id = CYBER9385;
15058c2ecf20Sopenharmony_ci			break;
15068c2ecf20Sopenharmony_ci		case 0x40 ... 0x43:
15078c2ecf20Sopenharmony_ci			chip_id = CYBER9382;
15088c2ecf20Sopenharmony_ci			break;
15098c2ecf20Sopenharmony_ci		case 0x4A:
15108c2ecf20Sopenharmony_ci			chip_id = CYBER9388;
15118c2ecf20Sopenharmony_ci			break;
15128c2ecf20Sopenharmony_ci		default:
15138c2ecf20Sopenharmony_ci			break;
15148c2ecf20Sopenharmony_ci		}
15158c2ecf20Sopenharmony_ci	}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_ci	chip3D = is3Dchip(chip_id);
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (is_xp(chip_id)) {
15208c2ecf20Sopenharmony_ci		default_par->init_accel = xp_init_accel;
15218c2ecf20Sopenharmony_ci		default_par->wait_engine = xp_wait_engine;
15228c2ecf20Sopenharmony_ci		default_par->fill_rect = xp_fill_rect;
15238c2ecf20Sopenharmony_ci		default_par->copy_rect = xp_copy_rect;
15248c2ecf20Sopenharmony_ci		tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADEXP;
15258c2ecf20Sopenharmony_ci	} else if (is_blade(chip_id)) {
15268c2ecf20Sopenharmony_ci		default_par->init_accel = blade_init_accel;
15278c2ecf20Sopenharmony_ci		default_par->wait_engine = blade_wait_engine;
15288c2ecf20Sopenharmony_ci		default_par->fill_rect = blade_fill_rect;
15298c2ecf20Sopenharmony_ci		default_par->copy_rect = blade_copy_rect;
15308c2ecf20Sopenharmony_ci		default_par->image_blit = blade_image_blit;
15318c2ecf20Sopenharmony_ci		tridentfb_fix.accel = FB_ACCEL_TRIDENT_BLADE3D;
15328c2ecf20Sopenharmony_ci	} else if (chip3D) {			/* 3DImage family left */
15338c2ecf20Sopenharmony_ci		default_par->init_accel = image_init_accel;
15348c2ecf20Sopenharmony_ci		default_par->wait_engine = image_wait_engine;
15358c2ecf20Sopenharmony_ci		default_par->fill_rect = image_fill_rect;
15368c2ecf20Sopenharmony_ci		default_par->copy_rect = image_copy_rect;
15378c2ecf20Sopenharmony_ci		tridentfb_fix.accel = FB_ACCEL_TRIDENT_3DIMAGE;
15388c2ecf20Sopenharmony_ci	} else { 				/* TGUI 9440/96XX family */
15398c2ecf20Sopenharmony_ci		default_par->init_accel = tgui_init_accel;
15408c2ecf20Sopenharmony_ci		default_par->wait_engine = xp_wait_engine;
15418c2ecf20Sopenharmony_ci		default_par->fill_rect = tgui_fill_rect;
15428c2ecf20Sopenharmony_ci		default_par->copy_rect = tgui_copy_rect;
15438c2ecf20Sopenharmony_ci		tridentfb_fix.accel = FB_ACCEL_TRIDENT_TGUI;
15448c2ecf20Sopenharmony_ci	}
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	default_par->chip_id = chip_id;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	/* setup MMIO region */
15498c2ecf20Sopenharmony_ci	tridentfb_fix.mmio_start = pci_resource_start(dev, 1);
15508c2ecf20Sopenharmony_ci	tridentfb_fix.mmio_len = pci_resource_len(dev, 1);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (!request_mem_region(tridentfb_fix.mmio_start,
15538c2ecf20Sopenharmony_ci				tridentfb_fix.mmio_len, "tridentfb")) {
15548c2ecf20Sopenharmony_ci		debug("request_region failed!\n");
15558c2ecf20Sopenharmony_ci		framebuffer_release(info);
15568c2ecf20Sopenharmony_ci		return -1;
15578c2ecf20Sopenharmony_ci	}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_ci	default_par->io_virt = ioremap(tridentfb_fix.mmio_start,
15608c2ecf20Sopenharmony_ci					       tridentfb_fix.mmio_len);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	if (!default_par->io_virt) {
15638c2ecf20Sopenharmony_ci		debug("ioremap failed\n");
15648c2ecf20Sopenharmony_ci		err = -1;
15658c2ecf20Sopenharmony_ci		goto out_unmap1;
15668c2ecf20Sopenharmony_ci	}
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	enable_mmio(default_par);
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	/* setup framebuffer memory */
15718c2ecf20Sopenharmony_ci	tridentfb_fix.smem_start = pci_resource_start(dev, 0);
15728c2ecf20Sopenharmony_ci	tridentfb_fix.smem_len = get_memsize(default_par);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	if (!request_mem_region(tridentfb_fix.smem_start,
15758c2ecf20Sopenharmony_ci				tridentfb_fix.smem_len, "tridentfb")) {
15768c2ecf20Sopenharmony_ci		debug("request_mem_region failed!\n");
15778c2ecf20Sopenharmony_ci		disable_mmio(info->par);
15788c2ecf20Sopenharmony_ci		err = -1;
15798c2ecf20Sopenharmony_ci		goto out_unmap1;
15808c2ecf20Sopenharmony_ci	}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	info->screen_base = ioremap(tridentfb_fix.smem_start,
15838c2ecf20Sopenharmony_ci					    tridentfb_fix.smem_len);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	if (!info->screen_base) {
15868c2ecf20Sopenharmony_ci		debug("ioremap failed\n");
15878c2ecf20Sopenharmony_ci		err = -1;
15888c2ecf20Sopenharmony_ci		goto out_unmap2;
15898c2ecf20Sopenharmony_ci	}
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	default_par->flatpanel = is_flatpanel(default_par);
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if (default_par->flatpanel)
15948c2ecf20Sopenharmony_ci		nativex = get_nativex(default_par);
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci	info->fix = tridentfb_fix;
15978c2ecf20Sopenharmony_ci	info->fbops = &tridentfb_ops;
15988c2ecf20Sopenharmony_ci	info->pseudo_palette = default_par->pseudo_pal;
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
16018c2ecf20Sopenharmony_ci	if (!noaccel && default_par->init_accel) {
16028c2ecf20Sopenharmony_ci		info->flags &= ~FBINFO_HWACCEL_DISABLED;
16038c2ecf20Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_COPYAREA;
16048c2ecf20Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_FILLRECT;
16058c2ecf20Sopenharmony_ci	} else
16068c2ecf20Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_DISABLED;
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci	if (is_blade(chip_id) && chip_id != BLADE3D)
16098c2ecf20Sopenharmony_ci		info->flags |= FBINFO_READS_FAST;
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_ci	info->pixmap.addr = kmalloc(4096, GFP_KERNEL);
16128c2ecf20Sopenharmony_ci	if (!info->pixmap.addr) {
16138c2ecf20Sopenharmony_ci		err = -ENOMEM;
16148c2ecf20Sopenharmony_ci		goto out_unmap2;
16158c2ecf20Sopenharmony_ci	}
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	info->pixmap.size = 4096;
16188c2ecf20Sopenharmony_ci	info->pixmap.buf_align = 4;
16198c2ecf20Sopenharmony_ci	info->pixmap.scan_align = 1;
16208c2ecf20Sopenharmony_ci	info->pixmap.access_align = 32;
16218c2ecf20Sopenharmony_ci	info->pixmap.flags = FB_PIXMAP_SYSTEM;
16228c2ecf20Sopenharmony_ci	info->var.bits_per_pixel = 8;
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci	if (default_par->image_blit) {
16258c2ecf20Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_IMAGEBLIT;
16268c2ecf20Sopenharmony_ci		info->pixmap.scan_align = 4;
16278c2ecf20Sopenharmony_ci	}
16288c2ecf20Sopenharmony_ci
16298c2ecf20Sopenharmony_ci	if (noaccel) {
16308c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "disabling acceleration\n");
16318c2ecf20Sopenharmony_ci		info->flags |= FBINFO_HWACCEL_DISABLED;
16328c2ecf20Sopenharmony_ci		info->pixmap.scan_align = 1;
16338c2ecf20Sopenharmony_ci	}
16348c2ecf20Sopenharmony_ci
16358c2ecf20Sopenharmony_ci	if (tridentfb_setup_ddc_bus(info) == 0) {
16368c2ecf20Sopenharmony_ci		u8 *edid = fb_ddc_read(&default_par->ddc_adapter);
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci		default_par->ddc_registered = true;
16398c2ecf20Sopenharmony_ci		if (edid) {
16408c2ecf20Sopenharmony_ci			fb_edid_to_monspecs(edid, &info->monspecs);
16418c2ecf20Sopenharmony_ci			kfree(edid);
16428c2ecf20Sopenharmony_ci			if (!info->monspecs.modedb)
16438c2ecf20Sopenharmony_ci				dev_err(info->device, "error getting mode database\n");
16448c2ecf20Sopenharmony_ci			else {
16458c2ecf20Sopenharmony_ci				const struct fb_videomode *m;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci				fb_videomode_to_modelist(info->monspecs.modedb,
16488c2ecf20Sopenharmony_ci						 info->monspecs.modedb_len,
16498c2ecf20Sopenharmony_ci						 &info->modelist);
16508c2ecf20Sopenharmony_ci				m = fb_find_best_display(&info->monspecs,
16518c2ecf20Sopenharmony_ci							 &info->modelist);
16528c2ecf20Sopenharmony_ci				if (m) {
16538c2ecf20Sopenharmony_ci					fb_videomode_to_var(&info->var, m);
16548c2ecf20Sopenharmony_ci					/* fill all other info->var's fields */
16558c2ecf20Sopenharmony_ci					if (tridentfb_check_var(&info->var,
16568c2ecf20Sopenharmony_ci								info) == 0)
16578c2ecf20Sopenharmony_ci						found = true;
16588c2ecf20Sopenharmony_ci				}
16598c2ecf20Sopenharmony_ci			}
16608c2ecf20Sopenharmony_ci		}
16618c2ecf20Sopenharmony_ci	}
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	if (!mode_option && !found)
16648c2ecf20Sopenharmony_ci		mode_option = "640x480-8@60";
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	/* Prepare startup mode */
16678c2ecf20Sopenharmony_ci	if (mode_option) {
16688c2ecf20Sopenharmony_ci		err = fb_find_mode(&info->var, info, mode_option,
16698c2ecf20Sopenharmony_ci				   info->monspecs.modedb,
16708c2ecf20Sopenharmony_ci				   info->monspecs.modedb_len,
16718c2ecf20Sopenharmony_ci				   NULL, info->var.bits_per_pixel);
16728c2ecf20Sopenharmony_ci		if (!err || err == 4) {
16738c2ecf20Sopenharmony_ci			err = -EINVAL;
16748c2ecf20Sopenharmony_ci			dev_err(info->device, "mode %s not found\n",
16758c2ecf20Sopenharmony_ci								mode_option);
16768c2ecf20Sopenharmony_ci			fb_destroy_modedb(info->monspecs.modedb);
16778c2ecf20Sopenharmony_ci			info->monspecs.modedb = NULL;
16788c2ecf20Sopenharmony_ci			goto out_unmap2;
16798c2ecf20Sopenharmony_ci		}
16808c2ecf20Sopenharmony_ci	}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci	fb_destroy_modedb(info->monspecs.modedb);
16838c2ecf20Sopenharmony_ci	info->monspecs.modedb = NULL;
16848c2ecf20Sopenharmony_ci
16858c2ecf20Sopenharmony_ci	err = fb_alloc_cmap(&info->cmap, 256, 0);
16868c2ecf20Sopenharmony_ci	if (err < 0)
16878c2ecf20Sopenharmony_ci		goto out_unmap2;
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_ci	info->var.activate |= FB_ACTIVATE_NOW;
16908c2ecf20Sopenharmony_ci	info->device = &dev->dev;
16918c2ecf20Sopenharmony_ci	if (register_framebuffer(info) < 0) {
16928c2ecf20Sopenharmony_ci		printk(KERN_ERR "tridentfb: could not register framebuffer\n");
16938c2ecf20Sopenharmony_ci		fb_dealloc_cmap(&info->cmap);
16948c2ecf20Sopenharmony_ci		err = -EINVAL;
16958c2ecf20Sopenharmony_ci		goto out_unmap2;
16968c2ecf20Sopenharmony_ci	}
16978c2ecf20Sopenharmony_ci	output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
16988c2ecf20Sopenharmony_ci	   info->node, info->fix.id, info->var.xres,
16998c2ecf20Sopenharmony_ci	   info->var.yres, info->var.bits_per_pixel);
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, info);
17028c2ecf20Sopenharmony_ci	return 0;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ciout_unmap2:
17058c2ecf20Sopenharmony_ci	if (default_par->ddc_registered)
17068c2ecf20Sopenharmony_ci		i2c_del_adapter(&default_par->ddc_adapter);
17078c2ecf20Sopenharmony_ci	kfree(info->pixmap.addr);
17088c2ecf20Sopenharmony_ci	if (info->screen_base)
17098c2ecf20Sopenharmony_ci		iounmap(info->screen_base);
17108c2ecf20Sopenharmony_ci	release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
17118c2ecf20Sopenharmony_ci	disable_mmio(info->par);
17128c2ecf20Sopenharmony_ciout_unmap1:
17138c2ecf20Sopenharmony_ci	if (default_par->io_virt)
17148c2ecf20Sopenharmony_ci		iounmap(default_par->io_virt);
17158c2ecf20Sopenharmony_ci	release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
17168c2ecf20Sopenharmony_ci	framebuffer_release(info);
17178c2ecf20Sopenharmony_ci	return err;
17188c2ecf20Sopenharmony_ci}
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_cistatic void trident_pci_remove(struct pci_dev *dev)
17218c2ecf20Sopenharmony_ci{
17228c2ecf20Sopenharmony_ci	struct fb_info *info = pci_get_drvdata(dev);
17238c2ecf20Sopenharmony_ci	struct tridentfb_par *par = info->par;
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
17268c2ecf20Sopenharmony_ci	if (par->ddc_registered)
17278c2ecf20Sopenharmony_ci		i2c_del_adapter(&par->ddc_adapter);
17288c2ecf20Sopenharmony_ci	iounmap(par->io_virt);
17298c2ecf20Sopenharmony_ci	iounmap(info->screen_base);
17308c2ecf20Sopenharmony_ci	release_mem_region(tridentfb_fix.smem_start, tridentfb_fix.smem_len);
17318c2ecf20Sopenharmony_ci	release_mem_region(tridentfb_fix.mmio_start, tridentfb_fix.mmio_len);
17328c2ecf20Sopenharmony_ci	kfree(info->pixmap.addr);
17338c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
17348c2ecf20Sopenharmony_ci	framebuffer_release(info);
17358c2ecf20Sopenharmony_ci}
17368c2ecf20Sopenharmony_ci
17378c2ecf20Sopenharmony_ci/* List of boards that we are trying to support */
17388c2ecf20Sopenharmony_cistatic const struct pci_device_id trident_devices[] = {
17398c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	BLADE3D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17408c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEi7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17418c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEi7D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17428c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17438c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17448c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17458c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEAi1D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17468c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEE4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17478c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	TGUI9440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17488c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	TGUI9660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17498c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	IMAGE975, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17508c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	IMAGE985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17518c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17528c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9388, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17538c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17548c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9525DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17558c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9397, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17568c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBER9397DVD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17578c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEXPAi1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17588c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEXPm8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17598c2ecf20Sopenharmony_ci	{PCI_VENDOR_ID_TRIDENT,	CYBERBLADEXPm16, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
17608c2ecf20Sopenharmony_ci	{0,}
17618c2ecf20Sopenharmony_ci};
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, trident_devices);
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_cistatic struct pci_driver tridentfb_pci_driver = {
17668c2ecf20Sopenharmony_ci	.name = "tridentfb",
17678c2ecf20Sopenharmony_ci	.id_table = trident_devices,
17688c2ecf20Sopenharmony_ci	.probe = trident_pci_probe,
17698c2ecf20Sopenharmony_ci	.remove = trident_pci_remove,
17708c2ecf20Sopenharmony_ci};
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci/*
17738c2ecf20Sopenharmony_ci * Parse user specified options (`video=trident:')
17748c2ecf20Sopenharmony_ci * example:
17758c2ecf20Sopenharmony_ci *	video=trident:800x600,bpp=16,noaccel
17768c2ecf20Sopenharmony_ci */
17778c2ecf20Sopenharmony_ci#ifndef MODULE
17788c2ecf20Sopenharmony_cistatic int __init tridentfb_setup(char *options)
17798c2ecf20Sopenharmony_ci{
17808c2ecf20Sopenharmony_ci	char *opt;
17818c2ecf20Sopenharmony_ci	if (!options || !*options)
17828c2ecf20Sopenharmony_ci		return 0;
17838c2ecf20Sopenharmony_ci	while ((opt = strsep(&options, ",")) != NULL) {
17848c2ecf20Sopenharmony_ci		if (!*opt)
17858c2ecf20Sopenharmony_ci			continue;
17868c2ecf20Sopenharmony_ci		if (!strncmp(opt, "noaccel", 7))
17878c2ecf20Sopenharmony_ci			noaccel = 1;
17888c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "fp", 2))
17898c2ecf20Sopenharmony_ci			fp = 1;
17908c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "crt", 3))
17918c2ecf20Sopenharmony_ci			fp = 0;
17928c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "bpp=", 4))
17938c2ecf20Sopenharmony_ci			bpp = simple_strtoul(opt + 4, NULL, 0);
17948c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "center", 6))
17958c2ecf20Sopenharmony_ci			center = 1;
17968c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "stretch", 7))
17978c2ecf20Sopenharmony_ci			stretch = 1;
17988c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "memsize=", 8))
17998c2ecf20Sopenharmony_ci			memsize = simple_strtoul(opt + 8, NULL, 0);
18008c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "memdiff=", 8))
18018c2ecf20Sopenharmony_ci			memdiff = simple_strtoul(opt + 8, NULL, 0);
18028c2ecf20Sopenharmony_ci		else if (!strncmp(opt, "nativex=", 8))
18038c2ecf20Sopenharmony_ci			nativex = simple_strtoul(opt + 8, NULL, 0);
18048c2ecf20Sopenharmony_ci		else
18058c2ecf20Sopenharmony_ci			mode_option = opt;
18068c2ecf20Sopenharmony_ci	}
18078c2ecf20Sopenharmony_ci	return 0;
18088c2ecf20Sopenharmony_ci}
18098c2ecf20Sopenharmony_ci#endif
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic int __init tridentfb_init(void)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci#ifndef MODULE
18148c2ecf20Sopenharmony_ci	char *option = NULL;
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	if (fb_get_options("tridentfb", &option))
18178c2ecf20Sopenharmony_ci		return -ENODEV;
18188c2ecf20Sopenharmony_ci	tridentfb_setup(option);
18198c2ecf20Sopenharmony_ci#endif
18208c2ecf20Sopenharmony_ci	return pci_register_driver(&tridentfb_pci_driver);
18218c2ecf20Sopenharmony_ci}
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_cistatic void __exit tridentfb_exit(void)
18248c2ecf20Sopenharmony_ci{
18258c2ecf20Sopenharmony_ci	pci_unregister_driver(&tridentfb_pci_driver);
18268c2ecf20Sopenharmony_ci}
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_cimodule_init(tridentfb_init);
18298c2ecf20Sopenharmony_cimodule_exit(tridentfb_exit);
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jani Monoses <jani@iv.ro>");
18328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Trident cards");
18338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
18348c2ecf20Sopenharmony_ciMODULE_ALIAS("cyblafb");
18358c2ecf20Sopenharmony_ci
1836