18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *  linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *	Copyright (C) 1995 Jay Estabrook
58c2ecf20Sopenharmony_ci *	Copyright (C) 1997 Geert Uytterhoeven
68c2ecf20Sopenharmony_ci *	Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha
78c2ecf20Sopenharmony_ci *	Copyright (C) 2002 Richard Henderson
88c2ecf20Sopenharmony_ci *	Copyright (C) 2006, 2007  Maciej W. Rozycki
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *  This file is subject to the terms and conditions of the GNU General Public
118c2ecf20Sopenharmony_ci *  License. See the file COPYING in the main directory of this archive for
128c2ecf20Sopenharmony_ci *  more details.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/bitrev.h>
168c2ecf20Sopenharmony_ci#include <linux/compiler.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/device.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/fb.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/ioport.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/mm.h>
258c2ecf20Sopenharmony_ci#include <linux/module.h>
268c2ecf20Sopenharmony_ci#include <linux/pci.h>
278c2ecf20Sopenharmony_ci#include <linux/selection.h>
288c2ecf20Sopenharmony_ci#include <linux/string.h>
298c2ecf20Sopenharmony_ci#include <linux/tc.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <asm/io.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <video/tgafb.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#ifdef CONFIG_TC
368c2ecf20Sopenharmony_ci#define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type)
378c2ecf20Sopenharmony_ci#else
388c2ecf20Sopenharmony_ci#define TGA_BUS_TC(dev) 0
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Local functions.
438c2ecf20Sopenharmony_ci */
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic int tgafb_check_var(struct fb_var_screeninfo *, struct fb_info *);
468c2ecf20Sopenharmony_cistatic int tgafb_set_par(struct fb_info *);
478c2ecf20Sopenharmony_cistatic void tgafb_set_pll(struct tga_par *, int);
488c2ecf20Sopenharmony_cistatic int tgafb_setcolreg(unsigned, unsigned, unsigned, unsigned,
498c2ecf20Sopenharmony_ci			   unsigned, struct fb_info *);
508c2ecf20Sopenharmony_cistatic int tgafb_blank(int, struct fb_info *);
518c2ecf20Sopenharmony_cistatic void tgafb_init_fix(struct fb_info *);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void tgafb_imageblit(struct fb_info *, const struct fb_image *);
548c2ecf20Sopenharmony_cistatic void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *);
558c2ecf20Sopenharmony_cistatic void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *);
568c2ecf20Sopenharmony_cistatic int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int tgafb_register(struct device *dev);
598c2ecf20Sopenharmony_cistatic void tgafb_unregister(struct device *dev);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const char *mode_option;
628c2ecf20Sopenharmony_cistatic const char *mode_option_pci = "640x480@60";
638c2ecf20Sopenharmony_cistatic const char *mode_option_tc = "1280x1024@72";
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct pci_driver tgafb_pci_driver;
678c2ecf20Sopenharmony_cistatic struct tc_driver tgafb_tc_driver;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci *  Frame buffer operations
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const struct fb_ops tgafb_ops = {
748c2ecf20Sopenharmony_ci	.owner			= THIS_MODULE,
758c2ecf20Sopenharmony_ci	.fb_check_var		= tgafb_check_var,
768c2ecf20Sopenharmony_ci	.fb_set_par		= tgafb_set_par,
778c2ecf20Sopenharmony_ci	.fb_setcolreg		= tgafb_setcolreg,
788c2ecf20Sopenharmony_ci	.fb_blank		= tgafb_blank,
798c2ecf20Sopenharmony_ci	.fb_pan_display		= tgafb_pan_display,
808c2ecf20Sopenharmony_ci	.fb_fillrect		= tgafb_fillrect,
818c2ecf20Sopenharmony_ci	.fb_copyarea		= tgafb_copyarea,
828c2ecf20Sopenharmony_ci	.fb_imageblit		= tgafb_imageblit,
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci *  PCI registration operations
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_cistatic int tgafb_pci_register(struct pci_dev *, const struct pci_device_id *);
918c2ecf20Sopenharmony_cistatic void tgafb_pci_unregister(struct pci_dev *);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic struct pci_device_id const tgafb_pci_table[] = {
948c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
958c2ecf20Sopenharmony_ci	{ }
968c2ecf20Sopenharmony_ci};
978c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, tgafb_pci_table);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic struct pci_driver tgafb_pci_driver = {
1008c2ecf20Sopenharmony_ci	.name			= "tgafb",
1018c2ecf20Sopenharmony_ci	.id_table		= tgafb_pci_table,
1028c2ecf20Sopenharmony_ci	.probe			= tgafb_pci_register,
1038c2ecf20Sopenharmony_ci	.remove			= tgafb_pci_unregister,
1048c2ecf20Sopenharmony_ci};
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int tgafb_pci_register(struct pci_dev *pdev,
1078c2ecf20Sopenharmony_ci			      const struct pci_device_id *ent)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	return tgafb_register(&pdev->dev);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic void tgafb_pci_unregister(struct pci_dev *pdev)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	tgafb_unregister(&pdev->dev);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci#ifdef CONFIG_TC
1198c2ecf20Sopenharmony_ci/*
1208c2ecf20Sopenharmony_ci *  TC registration operations
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_cistatic int tgafb_tc_register(struct device *);
1238c2ecf20Sopenharmony_cistatic int tgafb_tc_unregister(struct device *);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic struct tc_device_id const tgafb_tc_table[] = {
1268c2ecf20Sopenharmony_ci	{ "DEC     ", "PMAGD-AA" },
1278c2ecf20Sopenharmony_ci	{ "DEC     ", "PMAGD   " },
1288c2ecf20Sopenharmony_ci	{ }
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(tc, tgafb_tc_table);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic struct tc_driver tgafb_tc_driver = {
1338c2ecf20Sopenharmony_ci	.id_table		= tgafb_tc_table,
1348c2ecf20Sopenharmony_ci	.driver			= {
1358c2ecf20Sopenharmony_ci		.name		= "tgafb",
1368c2ecf20Sopenharmony_ci		.bus		= &tc_bus_type,
1378c2ecf20Sopenharmony_ci		.probe		= tgafb_tc_register,
1388c2ecf20Sopenharmony_ci		.remove		= tgafb_tc_unregister,
1398c2ecf20Sopenharmony_ci	},
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int tgafb_tc_register(struct device *dev)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int status = tgafb_register(dev);
1458c2ecf20Sopenharmony_ci	if (!status)
1468c2ecf20Sopenharmony_ci		get_device(dev);
1478c2ecf20Sopenharmony_ci	return status;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int tgafb_tc_unregister(struct device *dev)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	put_device(dev);
1538c2ecf20Sopenharmony_ci	tgafb_unregister(dev);
1548c2ecf20Sopenharmony_ci	return 0;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci#endif /* CONFIG_TC */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/**
1608c2ecf20Sopenharmony_ci *      tgafb_check_var - Optional function.  Validates a var passed in.
1618c2ecf20Sopenharmony_ci *      @var: frame buffer variable screen structure
1628c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
1638c2ecf20Sopenharmony_ci */
1648c2ecf20Sopenharmony_cistatic int
1658c2ecf20Sopenharmony_citgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *)info->par;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (!var->pixclock)
1708c2ecf20Sopenharmony_ci		return -EINVAL;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (par->tga_type == TGA_TYPE_8PLANE) {
1738c2ecf20Sopenharmony_ci		if (var->bits_per_pixel != 8)
1748c2ecf20Sopenharmony_ci			return -EINVAL;
1758c2ecf20Sopenharmony_ci	} else {
1768c2ecf20Sopenharmony_ci		if (var->bits_per_pixel != 32)
1778c2ecf20Sopenharmony_ci			return -EINVAL;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	var->red.length = var->green.length = var->blue.length = 8;
1808c2ecf20Sopenharmony_ci	if (var->bits_per_pixel == 32) {
1818c2ecf20Sopenharmony_ci		var->red.offset = 16;
1828c2ecf20Sopenharmony_ci		var->green.offset = 8;
1838c2ecf20Sopenharmony_ci		var->blue.offset = 0;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
1878c2ecf20Sopenharmony_ci		return -EINVAL;
1888c2ecf20Sopenharmony_ci	if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len)
1898c2ecf20Sopenharmony_ci		return -EINVAL;
1908c2ecf20Sopenharmony_ci	if (var->nonstd)
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci	if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci	if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Some of the acceleration routines assume the line width is
1988c2ecf20Sopenharmony_ci	   a multiple of 8 bytes.  */
1998c2ecf20Sopenharmony_ci	if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 8)
2008c2ecf20Sopenharmony_ci		return -EINVAL;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return 0;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci/**
2068c2ecf20Sopenharmony_ci *      tgafb_set_par - Optional function.  Alters the hardware state.
2078c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_cistatic int
2108c2ecf20Sopenharmony_citgafb_set_par(struct fb_info *info)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	static unsigned int const deep_presets[4] = {
2138c2ecf20Sopenharmony_ci		0x00004000,
2148c2ecf20Sopenharmony_ci		0x0000440d,
2158c2ecf20Sopenharmony_ci		0xffffffff,
2168c2ecf20Sopenharmony_ci		0x0000441d
2178c2ecf20Sopenharmony_ci	};
2188c2ecf20Sopenharmony_ci	static unsigned int const rasterop_presets[4] = {
2198c2ecf20Sopenharmony_ci		0x00000003,
2208c2ecf20Sopenharmony_ci		0x00000303,
2218c2ecf20Sopenharmony_ci		0xffffffff,
2228c2ecf20Sopenharmony_ci		0x00000303
2238c2ecf20Sopenharmony_ci	};
2248c2ecf20Sopenharmony_ci	static unsigned int const mode_presets[4] = {
2258c2ecf20Sopenharmony_ci		0x00000000,
2268c2ecf20Sopenharmony_ci		0x00000300,
2278c2ecf20Sopenharmony_ci		0xffffffff,
2288c2ecf20Sopenharmony_ci		0x00000300
2298c2ecf20Sopenharmony_ci	};
2308c2ecf20Sopenharmony_ci	static unsigned int const base_addr_presets[4] = {
2318c2ecf20Sopenharmony_ci		0x00000000,
2328c2ecf20Sopenharmony_ci		0x00000001,
2338c2ecf20Sopenharmony_ci		0xffffffff,
2348c2ecf20Sopenharmony_ci		0x00000001
2358c2ecf20Sopenharmony_ci	};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
2388c2ecf20Sopenharmony_ci	int tga_bus_pci = dev_is_pci(par->dev);
2398c2ecf20Sopenharmony_ci	int tga_bus_tc = TGA_BUS_TC(par->dev);
2408c2ecf20Sopenharmony_ci	u32 htimings, vtimings, pll_freq;
2418c2ecf20Sopenharmony_ci	u8 tga_type;
2428c2ecf20Sopenharmony_ci	int i;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	/* Encode video timings.  */
2458c2ecf20Sopenharmony_ci	htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB)
2468c2ecf20Sopenharmony_ci		    | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB));
2478c2ecf20Sopenharmony_ci	vtimings = (info->var.yres & TGA_VERT_ACTIVE);
2488c2ecf20Sopenharmony_ci	htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP;
2498c2ecf20Sopenharmony_ci	vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP;
2508c2ecf20Sopenharmony_ci	htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC;
2518c2ecf20Sopenharmony_ci	vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC;
2528c2ecf20Sopenharmony_ci	htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP;
2538c2ecf20Sopenharmony_ci	vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
2568c2ecf20Sopenharmony_ci		htimings |= TGA_HORIZ_POLARITY;
2578c2ecf20Sopenharmony_ci	if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
2588c2ecf20Sopenharmony_ci		vtimings |= TGA_VERT_POLARITY;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	par->htimings = htimings;
2618c2ecf20Sopenharmony_ci	par->vtimings = vtimings;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* Store other useful values in par.  */
2668c2ecf20Sopenharmony_ci	par->xres = info->var.xres;
2678c2ecf20Sopenharmony_ci	par->yres = info->var.yres;
2688c2ecf20Sopenharmony_ci	par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
2698c2ecf20Sopenharmony_ci	par->bits_per_pixel = info->var.bits_per_pixel;
2708c2ecf20Sopenharmony_ci	info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	tga_type = par->tga_type;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* First, disable video.  */
2758c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	/* Write the DEEP register.  */
2788c2ecf20Sopenharmony_ci	while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
2798c2ecf20Sopenharmony_ci		continue;
2808c2ecf20Sopenharmony_ci	mb();
2818c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, deep_presets[tga_type] |
2828c2ecf20Sopenharmony_ci			   (par->sync_on_green ? 0x0 : 0x00010000),
2838c2ecf20Sopenharmony_ci		      TGA_DEEP_REG);
2848c2ecf20Sopenharmony_ci	while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
2858c2ecf20Sopenharmony_ci		continue;
2868c2ecf20Sopenharmony_ci	mb();
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	/* Write some more registers.  */
2898c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG);
2908c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG);
2918c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	/* Calculate & write the PLL.  */
2948c2ecf20Sopenharmony_ci	tgafb_set_pll(par, pll_freq);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Write some more registers.  */
2978c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG);
2988c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	/* Init video timing regs.  */
3018c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG);
3028c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	/* Initialise RAMDAC. */
3058c2ecf20Sopenharmony_ci	if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci		/* Init BT485 RAMDAC registers.  */
3088c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
3098c2ecf20Sopenharmony_ci			    BT485_CMD_0);
3108c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE);
3118c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */
3128c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0x40, BT485_CMD_1);
3138c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */
3148c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0xff, BT485_PIXEL_MASK);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci		/* Fill palette registers.  */
3178c2ecf20Sopenharmony_ci		BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE);
3188c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		for (i = 0; i < 256 * 3; i += 4) {
3218c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8),
3228c2ecf20Sopenharmony_ci				      TGA_RAMDAC_REG);
3238c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
3248c2ecf20Sopenharmony_ci				      TGA_RAMDAC_REG);
3258c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
3268c2ecf20Sopenharmony_ci				      TGA_RAMDAC_REG);
3278c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
3288c2ecf20Sopenharmony_ci				      TGA_RAMDAC_REG);
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	} else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		/* Init BT459 RAMDAC registers.  */
3348c2ecf20Sopenharmony_ci		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
3358c2ecf20Sopenharmony_ci		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
3368c2ecf20Sopenharmony_ci		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
3378c2ecf20Sopenharmony_ci			    (par->sync_on_green ? 0xc0 : 0x40));
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci		/* Fill the palette.  */
3428c2ecf20Sopenharmony_ci		BT459_LOAD_ADDR(par, 0x0000);
3438c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		for (i = 0; i < 256 * 3; i += 4) {
3468c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
3478c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3488c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3498c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3508c2ecf20Sopenharmony_ci		}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	} else { /* 24-plane or 24plusZ */
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		/* Init BT463 RAMDAC registers.  */
3558c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
3568c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
3578c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2,
3588c2ecf20Sopenharmony_ci			    (par->sync_on_green ? 0xc0 : 0x40));
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
3618c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
3628c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
3638c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
3668c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
3678c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
3688c2ecf20Sopenharmony_ci		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		/* Fill the palette.  */
3718c2ecf20Sopenharmony_ci		BT463_LOAD_ADDR(par, 0x0000);
3728c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci#ifdef CONFIG_HW_CONSOLE
3758c2ecf20Sopenharmony_ci		for (i = 0; i < 16; i++) {
3768c2ecf20Sopenharmony_ci			int j = color_table[i];
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
3798c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
3808c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
3818c2ecf20Sopenharmony_ci		}
3828c2ecf20Sopenharmony_ci		for (i = 0; i < 512 * 3; i += 4) {
3838c2ecf20Sopenharmony_ci#else
3848c2ecf20Sopenharmony_ci		for (i = 0; i < 528 * 3; i += 4) {
3858c2ecf20Sopenharmony_ci#endif
3868c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
3878c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3888c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3898c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
3908c2ecf20Sopenharmony_ci		}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		/* Fill window type table after start of vertical retrace.  */
3938c2ecf20Sopenharmony_ci		while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
3948c2ecf20Sopenharmony_ci			continue;
3958c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
3968c2ecf20Sopenharmony_ci		mb();
3978c2ecf20Sopenharmony_ci		while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
3988c2ecf20Sopenharmony_ci			continue;
3998c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE);
4028c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci		for (i = 0; i < 16; i++) {
4058c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
4068c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG);
4078c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	/* Finally, enable video scan (and pray for the monitor... :-) */
4138c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	return 0;
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci#define DIFFCHECK(X)							  \
4198c2ecf20Sopenharmony_cido {									  \
4208c2ecf20Sopenharmony_ci	if (m <= 0x3f) {						  \
4218c2ecf20Sopenharmony_ci		int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \
4228c2ecf20Sopenharmony_ci		if (delta < 0)						  \
4238c2ecf20Sopenharmony_ci			delta = -delta;					  \
4248c2ecf20Sopenharmony_ci		if (delta < min_diff)					  \
4258c2ecf20Sopenharmony_ci			min_diff = delta, vm = m, va = a, vr = r;	  \
4268c2ecf20Sopenharmony_ci	}								  \
4278c2ecf20Sopenharmony_ci} while (0)
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic void
4308c2ecf20Sopenharmony_citgafb_set_pll(struct tga_par *par, int f)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	int n, shift, base, min_diff, target;
4338c2ecf20Sopenharmony_ci	int r,a,m,vm = 34, va = 1, vr = 30;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	for (r = 0 ; r < 12 ; r++)
4368c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, !r, TGA_CLOCK_REG);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (f > TGA_PLL_MAX_FREQ)
4398c2ecf20Sopenharmony_ci		f = TGA_PLL_MAX_FREQ;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (f >= TGA_PLL_MAX_FREQ / 2)
4428c2ecf20Sopenharmony_ci		shift = 0;
4438c2ecf20Sopenharmony_ci	else if (f >= TGA_PLL_MAX_FREQ / 4)
4448c2ecf20Sopenharmony_ci		shift = 1;
4458c2ecf20Sopenharmony_ci	else
4468c2ecf20Sopenharmony_ci		shift = 2;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG);
4498c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	for (r = 0 ; r < 10 ; r++)
4528c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (f <= 120000) {
4558c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4568c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci	else if (f <= 200000) {
4598c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
4608c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci	else {
4638c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4648c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
4688c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4698c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4708c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
4718c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
4728c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	target = (f << shift) / TGA_PLL_BASE_FREQ;
4758c2ecf20Sopenharmony_ci	min_diff = TGA_PLL_MAX_FREQ;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	r = 7 / target;
4788c2ecf20Sopenharmony_ci	if (!r) r = 1;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	base = target * r;
4818c2ecf20Sopenharmony_ci	while (base < 449) {
4828c2ecf20Sopenharmony_ci		for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) {
4838c2ecf20Sopenharmony_ci			m = ((n + 3) / 7) - 1;
4848c2ecf20Sopenharmony_ci			a = 0;
4858c2ecf20Sopenharmony_ci			DIFFCHECK((m + 1) * 7);
4868c2ecf20Sopenharmony_ci			m++;
4878c2ecf20Sopenharmony_ci			DIFFCHECK((m + 1) * 7);
4888c2ecf20Sopenharmony_ci			m = (n / 6) - 1;
4898c2ecf20Sopenharmony_ci			if ((a = n % 6))
4908c2ecf20Sopenharmony_ci				DIFFCHECK(n);
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci		r++;
4938c2ecf20Sopenharmony_ci		base += target;
4948c2ecf20Sopenharmony_ci	}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	vr--;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	for (r = 0; r < 8; r++)
4998c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG);
5008c2ecf20Sopenharmony_ci	for (r = 0; r < 8 ; r++)
5018c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG);
5028c2ecf20Sopenharmony_ci	for (r = 0; r < 7 ; r++)
5038c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG);
5048c2ecf20Sopenharmony_ci	TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/**
5098c2ecf20Sopenharmony_ci *      tgafb_setcolreg - Optional function. Sets a color register.
5108c2ecf20Sopenharmony_ci *      @regno: boolean, 0 copy local, 1 get_user() function
5118c2ecf20Sopenharmony_ci *      @red: frame buffer colormap structure
5128c2ecf20Sopenharmony_ci *      @green: The green value which can be up to 16 bits wide
5138c2ecf20Sopenharmony_ci *      @blue:  The blue value which can be up to 16 bits wide.
5148c2ecf20Sopenharmony_ci *      @transp: If supported the alpha value which can be up to 16 bits wide.
5158c2ecf20Sopenharmony_ci *      @info: frame buffer info structure
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_cistatic int
5188c2ecf20Sopenharmony_citgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
5198c2ecf20Sopenharmony_ci		unsigned transp, struct fb_info *info)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
5228c2ecf20Sopenharmony_ci	int tga_bus_pci = dev_is_pci(par->dev);
5238c2ecf20Sopenharmony_ci	int tga_bus_tc = TGA_BUS_TC(par->dev);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (regno > 255)
5268c2ecf20Sopenharmony_ci		return 1;
5278c2ecf20Sopenharmony_ci	red >>= 8;
5288c2ecf20Sopenharmony_ci	green >>= 8;
5298c2ecf20Sopenharmony_ci	blue >>= 8;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
5328c2ecf20Sopenharmony_ci		BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
5338c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
5348c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
5358c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
5368c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
5378c2ecf20Sopenharmony_ci	} else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
5388c2ecf20Sopenharmony_ci		BT459_LOAD_ADDR(par, regno);
5398c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
5408c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
5418c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
5428c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
5438c2ecf20Sopenharmony_ci	} else {
5448c2ecf20Sopenharmony_ci		if (regno < 16) {
5458c2ecf20Sopenharmony_ci			u32 value = (regno << 16) | (regno << 8) | regno;
5468c2ecf20Sopenharmony_ci			((u32 *)info->pseudo_palette)[regno] = value;
5478c2ecf20Sopenharmony_ci		}
5488c2ecf20Sopenharmony_ci		BT463_LOAD_ADDR(par, regno);
5498c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
5508c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
5518c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
5528c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/**
5608c2ecf20Sopenharmony_ci *      tgafb_blank - Optional function.  Blanks the display.
5618c2ecf20Sopenharmony_ci *      @blank_mode: the blank mode we want.
5628c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
5638c2ecf20Sopenharmony_ci */
5648c2ecf20Sopenharmony_cistatic int
5658c2ecf20Sopenharmony_citgafb_blank(int blank, struct fb_info *info)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
5688c2ecf20Sopenharmony_ci	u32 vhcr, vvcr, vvvr;
5698c2ecf20Sopenharmony_ci	unsigned long flags;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	local_irq_save(flags);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	vhcr = TGA_READ_REG(par, TGA_HORIZ_REG);
5748c2ecf20Sopenharmony_ci	vvcr = TGA_READ_REG(par, TGA_VERT_REG);
5758c2ecf20Sopenharmony_ci	vvvr = TGA_READ_REG(par, TGA_VALID_REG);
5768c2ecf20Sopenharmony_ci	vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	switch (blank) {
5798c2ecf20Sopenharmony_ci	case FB_BLANK_UNBLANK: /* Unblanking */
5808c2ecf20Sopenharmony_ci		if (par->vesa_blanked) {
5818c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG);
5828c2ecf20Sopenharmony_ci			TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG);
5838c2ecf20Sopenharmony_ci			par->vesa_blanked = 0;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG);
5868c2ecf20Sopenharmony_ci		break;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	case FB_BLANK_NORMAL: /* Normal blanking */
5898c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK,
5908c2ecf20Sopenharmony_ci			      TGA_VALID_REG);
5918c2ecf20Sopenharmony_ci		break;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
5948c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
5958c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
5968c2ecf20Sopenharmony_ci		par->vesa_blanked = 1;
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
6008c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
6018c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
6028c2ecf20Sopenharmony_ci		par->vesa_blanked = 1;
6038c2ecf20Sopenharmony_ci		break;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	case FB_BLANK_POWERDOWN: /* Poweroff */
6068c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
6078c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
6088c2ecf20Sopenharmony_ci		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
6098c2ecf20Sopenharmony_ci		par->vesa_blanked = 1;
6108c2ecf20Sopenharmony_ci		break;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	local_irq_restore(flags);
6148c2ecf20Sopenharmony_ci	return 0;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/*
6198c2ecf20Sopenharmony_ci *  Acceleration.
6208c2ecf20Sopenharmony_ci */
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic void
6238c2ecf20Sopenharmony_citgafb_mono_imageblit(struct fb_info *info, const struct fb_image *image)
6248c2ecf20Sopenharmony_ci{
6258c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
6268c2ecf20Sopenharmony_ci	u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask;
6278c2ecf20Sopenharmony_ci	unsigned long rincr, line_length, shift, pos, is8bpp;
6288c2ecf20Sopenharmony_ci	unsigned long i, j;
6298c2ecf20Sopenharmony_ci	const unsigned char *data;
6308c2ecf20Sopenharmony_ci	void __iomem *regs_base;
6318c2ecf20Sopenharmony_ci	void __iomem *fb_base;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	is8bpp = info->var.bits_per_pixel == 8;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	dx = image->dx;
6368c2ecf20Sopenharmony_ci	dy = image->dy;
6378c2ecf20Sopenharmony_ci	width = image->width;
6388c2ecf20Sopenharmony_ci	height = image->height;
6398c2ecf20Sopenharmony_ci	vxres = info->var.xres_virtual;
6408c2ecf20Sopenharmony_ci	vyres = info->var.yres_virtual;
6418c2ecf20Sopenharmony_ci	line_length = info->fix.line_length;
6428c2ecf20Sopenharmony_ci	rincr = (width + 7) / 8;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	/* A shift below cannot cope with.  */
6458c2ecf20Sopenharmony_ci	if (unlikely(width == 0))
6468c2ecf20Sopenharmony_ci		return;
6478c2ecf20Sopenharmony_ci	/* Crop the image to the screen.  */
6488c2ecf20Sopenharmony_ci	if (dx > vxres || dy > vyres)
6498c2ecf20Sopenharmony_ci		return;
6508c2ecf20Sopenharmony_ci	if (dx + width > vxres)
6518c2ecf20Sopenharmony_ci		width = vxres - dx;
6528c2ecf20Sopenharmony_ci	if (dy + height > vyres)
6538c2ecf20Sopenharmony_ci		height = vyres - dy;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	regs_base = par->tga_regs_base;
6568c2ecf20Sopenharmony_ci	fb_base = par->tga_fb_base;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	/* Expand the color values to fill 32-bits.  */
6598c2ecf20Sopenharmony_ci	/* ??? Would be nice to notice colour changes elsewhere, so
6608c2ecf20Sopenharmony_ci	   that we can do this only when necessary.  */
6618c2ecf20Sopenharmony_ci	fgcolor = image->fg_color;
6628c2ecf20Sopenharmony_ci	bgcolor = image->bg_color;
6638c2ecf20Sopenharmony_ci	if (is8bpp) {
6648c2ecf20Sopenharmony_ci		fgcolor |= fgcolor << 8;
6658c2ecf20Sopenharmony_ci		fgcolor |= fgcolor << 16;
6668c2ecf20Sopenharmony_ci		bgcolor |= bgcolor << 8;
6678c2ecf20Sopenharmony_ci		bgcolor |= bgcolor << 16;
6688c2ecf20Sopenharmony_ci	} else {
6698c2ecf20Sopenharmony_ci		if (fgcolor < 16)
6708c2ecf20Sopenharmony_ci			fgcolor = ((u32 *)info->pseudo_palette)[fgcolor];
6718c2ecf20Sopenharmony_ci		if (bgcolor < 16)
6728c2ecf20Sopenharmony_ci			bgcolor = ((u32 *)info->pseudo_palette)[bgcolor];
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci	__raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG);
6758c2ecf20Sopenharmony_ci	__raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	/* Acquire proper alignment; set up the PIXELMASK register
6788c2ecf20Sopenharmony_ci	   so that we only write the proper character cell.  */
6798c2ecf20Sopenharmony_ci	pos = dy * line_length;
6808c2ecf20Sopenharmony_ci	if (is8bpp) {
6818c2ecf20Sopenharmony_ci		pos += dx;
6828c2ecf20Sopenharmony_ci		shift = pos & 3;
6838c2ecf20Sopenharmony_ci		pos &= -4;
6848c2ecf20Sopenharmony_ci	} else {
6858c2ecf20Sopenharmony_ci		pos += dx * 4;
6868c2ecf20Sopenharmony_ci		shift = (pos & 7) >> 2;
6878c2ecf20Sopenharmony_ci		pos &= -8;
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	data = (const unsigned char *) image->data;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	/* Enable opaque stipple mode.  */
6938c2ecf20Sopenharmony_ci	__raw_writel((is8bpp
6948c2ecf20Sopenharmony_ci		      ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE
6958c2ecf20Sopenharmony_ci		      : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE),
6968c2ecf20Sopenharmony_ci		     regs_base + TGA_MODE_REG);
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (width + shift <= 32) {
6998c2ecf20Sopenharmony_ci		unsigned long bwidth;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		/* Handle common case of imaging a single character, in
7028c2ecf20Sopenharmony_ci		   a font less than or 32 pixels wide.  */
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci		/* Avoid a shift by 32; width > 0 implied.  */
7058c2ecf20Sopenharmony_ci		pixelmask = (2ul << (width - 1)) - 1;
7068c2ecf20Sopenharmony_ci		pixelmask <<= shift;
7078c2ecf20Sopenharmony_ci		__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
7088c2ecf20Sopenharmony_ci		wmb();
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci		bwidth = (width + 7) / 8;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		for (i = 0; i < height; ++i) {
7138c2ecf20Sopenharmony_ci			u32 mask = 0;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci			/* The image data is bit big endian; we need
7168c2ecf20Sopenharmony_ci			   little endian.  */
7178c2ecf20Sopenharmony_ci			for (j = 0; j < bwidth; ++j)
7188c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j]) << (j * 8);
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci			__raw_writel(mask << shift, fb_base + pos);
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci			pos += line_length;
7238c2ecf20Sopenharmony_ci			data += rincr;
7248c2ecf20Sopenharmony_ci		}
7258c2ecf20Sopenharmony_ci		wmb();
7268c2ecf20Sopenharmony_ci		__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
7278c2ecf20Sopenharmony_ci	} else if (shift == 0) {
7288c2ecf20Sopenharmony_ci		unsigned long pos0 = pos;
7298c2ecf20Sopenharmony_ci		const unsigned char *data0 = data;
7308c2ecf20Sopenharmony_ci		unsigned long bincr = (is8bpp ? 8 : 8*4);
7318c2ecf20Sopenharmony_ci		unsigned long bwidth;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		/* Handle another common case in which accel_putcs
7348c2ecf20Sopenharmony_ci		   generates a large bitmap, which happens to be aligned.
7358c2ecf20Sopenharmony_ci		   Allow the tail to be misaligned.  This case is
7368c2ecf20Sopenharmony_ci		   interesting because we've not got to hold partial
7378c2ecf20Sopenharmony_ci		   bytes across the words being written.  */
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		wmb();
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci		bwidth = (width / 8) & -4;
7428c2ecf20Sopenharmony_ci		for (i = 0; i < height; ++i) {
7438c2ecf20Sopenharmony_ci			for (j = 0; j < bwidth; j += 4) {
7448c2ecf20Sopenharmony_ci				u32 mask = 0;
7458c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+0]) << (0 * 8);
7468c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+1]) << (1 * 8);
7478c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+2]) << (2 * 8);
7488c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+3]) << (3 * 8);
7498c2ecf20Sopenharmony_ci				__raw_writel(mask, fb_base + pos + j*bincr);
7508c2ecf20Sopenharmony_ci			}
7518c2ecf20Sopenharmony_ci			pos += line_length;
7528c2ecf20Sopenharmony_ci			data += rincr;
7538c2ecf20Sopenharmony_ci		}
7548c2ecf20Sopenharmony_ci		wmb();
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci		pixelmask = (1ul << (width & 31)) - 1;
7578c2ecf20Sopenharmony_ci		if (pixelmask) {
7588c2ecf20Sopenharmony_ci			__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
7598c2ecf20Sopenharmony_ci			wmb();
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci			pos = pos0 + bwidth*bincr;
7628c2ecf20Sopenharmony_ci			data = data0 + bwidth;
7638c2ecf20Sopenharmony_ci			bwidth = ((width & 31) + 7) / 8;
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci			for (i = 0; i < height; ++i) {
7668c2ecf20Sopenharmony_ci				u32 mask = 0;
7678c2ecf20Sopenharmony_ci				for (j = 0; j < bwidth; ++j)
7688c2ecf20Sopenharmony_ci					mask |= bitrev8(data[j]) << (j * 8);
7698c2ecf20Sopenharmony_ci				__raw_writel(mask, fb_base + pos);
7708c2ecf20Sopenharmony_ci				pos += line_length;
7718c2ecf20Sopenharmony_ci				data += rincr;
7728c2ecf20Sopenharmony_ci			}
7738c2ecf20Sopenharmony_ci			wmb();
7748c2ecf20Sopenharmony_ci			__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
7758c2ecf20Sopenharmony_ci		}
7768c2ecf20Sopenharmony_ci	} else {
7778c2ecf20Sopenharmony_ci		unsigned long pos0 = pos;
7788c2ecf20Sopenharmony_ci		const unsigned char *data0 = data;
7798c2ecf20Sopenharmony_ci		unsigned long bincr = (is8bpp ? 8 : 8*4);
7808c2ecf20Sopenharmony_ci		unsigned long bwidth;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci		/* Finally, handle the generic case of misaligned start.
7838c2ecf20Sopenharmony_ci		   Here we split the write into 16-bit spans.  This allows
7848c2ecf20Sopenharmony_ci		   us to use only one pixel mask, instead of four as would
7858c2ecf20Sopenharmony_ci		   be required by writing 24-bit spans.  */
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		pixelmask = 0xffff << shift;
7888c2ecf20Sopenharmony_ci		__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
7898c2ecf20Sopenharmony_ci		wmb();
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci		bwidth = (width / 8) & -2;
7928c2ecf20Sopenharmony_ci		for (i = 0; i < height; ++i) {
7938c2ecf20Sopenharmony_ci			for (j = 0; j < bwidth; j += 2) {
7948c2ecf20Sopenharmony_ci				u32 mask = 0;
7958c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+0]) << (0 * 8);
7968c2ecf20Sopenharmony_ci				mask |= bitrev8(data[j+1]) << (1 * 8);
7978c2ecf20Sopenharmony_ci				mask <<= shift;
7988c2ecf20Sopenharmony_ci				__raw_writel(mask, fb_base + pos + j*bincr);
7998c2ecf20Sopenharmony_ci			}
8008c2ecf20Sopenharmony_ci			pos += line_length;
8018c2ecf20Sopenharmony_ci			data += rincr;
8028c2ecf20Sopenharmony_ci		}
8038c2ecf20Sopenharmony_ci		wmb();
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		pixelmask = ((1ul << (width & 15)) - 1) << shift;
8068c2ecf20Sopenharmony_ci		if (pixelmask) {
8078c2ecf20Sopenharmony_ci			__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
8088c2ecf20Sopenharmony_ci			wmb();
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci			pos = pos0 + bwidth*bincr;
8118c2ecf20Sopenharmony_ci			data = data0 + bwidth;
8128c2ecf20Sopenharmony_ci			bwidth = (width & 15) > 8;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci			for (i = 0; i < height; ++i) {
8158c2ecf20Sopenharmony_ci				u32 mask = bitrev8(data[0]);
8168c2ecf20Sopenharmony_ci				if (bwidth)
8178c2ecf20Sopenharmony_ci					mask |= bitrev8(data[1]) << 8;
8188c2ecf20Sopenharmony_ci				mask <<= shift;
8198c2ecf20Sopenharmony_ci				__raw_writel(mask, fb_base + pos);
8208c2ecf20Sopenharmony_ci				pos += line_length;
8218c2ecf20Sopenharmony_ci				data += rincr;
8228c2ecf20Sopenharmony_ci			}
8238c2ecf20Sopenharmony_ci			wmb();
8248c2ecf20Sopenharmony_ci		}
8258c2ecf20Sopenharmony_ci		__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
8268c2ecf20Sopenharmony_ci	}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	/* Disable opaque stipple mode.  */
8298c2ecf20Sopenharmony_ci	__raw_writel((is8bpp
8308c2ecf20Sopenharmony_ci		      ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
8318c2ecf20Sopenharmony_ci		      : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
8328c2ecf20Sopenharmony_ci		     regs_base + TGA_MODE_REG);
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic void
8368c2ecf20Sopenharmony_citgafb_clut_imageblit(struct fb_info *info, const struct fb_image *image)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
8398c2ecf20Sopenharmony_ci	u32 color, dx, dy, width, height, vxres, vyres;
8408c2ecf20Sopenharmony_ci	u32 *palette = ((u32 *)info->pseudo_palette);
8418c2ecf20Sopenharmony_ci	unsigned long pos, line_length, i, j;
8428c2ecf20Sopenharmony_ci	const unsigned char *data;
8438c2ecf20Sopenharmony_ci	void __iomem *regs_base, *fb_base;
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	dx = image->dx;
8468c2ecf20Sopenharmony_ci	dy = image->dy;
8478c2ecf20Sopenharmony_ci	width = image->width;
8488c2ecf20Sopenharmony_ci	height = image->height;
8498c2ecf20Sopenharmony_ci	vxres = info->var.xres_virtual;
8508c2ecf20Sopenharmony_ci	vyres = info->var.yres_virtual;
8518c2ecf20Sopenharmony_ci	line_length = info->fix.line_length;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	/* Crop the image to the screen.  */
8548c2ecf20Sopenharmony_ci	if (dx > vxres || dy > vyres)
8558c2ecf20Sopenharmony_ci		return;
8568c2ecf20Sopenharmony_ci	if (dx + width > vxres)
8578c2ecf20Sopenharmony_ci		width = vxres - dx;
8588c2ecf20Sopenharmony_ci	if (dy + height > vyres)
8598c2ecf20Sopenharmony_ci		height = vyres - dy;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	regs_base = par->tga_regs_base;
8628c2ecf20Sopenharmony_ci	fb_base = par->tga_fb_base;
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	pos = dy * line_length + (dx * 4);
8658c2ecf20Sopenharmony_ci	data = image->data;
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* Now copy the image, color_expanding via the palette. */
8688c2ecf20Sopenharmony_ci	for (i = 0; i < height; i++) {
8698c2ecf20Sopenharmony_ci		for (j = 0; j < width; j++) {
8708c2ecf20Sopenharmony_ci			color = palette[*data++];
8718c2ecf20Sopenharmony_ci			__raw_writel(color, fb_base + pos + j*4);
8728c2ecf20Sopenharmony_ci		}
8738c2ecf20Sopenharmony_ci		pos += line_length;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci/**
8788c2ecf20Sopenharmony_ci *      tgafb_imageblit - REQUIRED function. Can use generic routines if
8798c2ecf20Sopenharmony_ci *                        non acclerated hardware and packed pixel based.
8808c2ecf20Sopenharmony_ci *                        Copies a image from system memory to the screen.
8818c2ecf20Sopenharmony_ci *
8828c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
8838c2ecf20Sopenharmony_ci *      @image: structure defining the image.
8848c2ecf20Sopenharmony_ci */
8858c2ecf20Sopenharmony_cistatic void
8868c2ecf20Sopenharmony_citgafb_imageblit(struct fb_info *info, const struct fb_image *image)
8878c2ecf20Sopenharmony_ci{
8888c2ecf20Sopenharmony_ci	unsigned int is8bpp = info->var.bits_per_pixel == 8;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/* If a mono image, regardless of FB depth, go do it. */
8918c2ecf20Sopenharmony_ci	if (image->depth == 1) {
8928c2ecf20Sopenharmony_ci		tgafb_mono_imageblit(info, image);
8938c2ecf20Sopenharmony_ci		return;
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	/* For copies that aren't pixel expansion, there's little we
8978c2ecf20Sopenharmony_ci	   can do better than the generic code.  */
8988c2ecf20Sopenharmony_ci	/* ??? There is a DMA write mode; I wonder if that could be
8998c2ecf20Sopenharmony_ci	   made to pull the data from the image buffer...  */
9008c2ecf20Sopenharmony_ci	if (image->depth == info->var.bits_per_pixel) {
9018c2ecf20Sopenharmony_ci		cfb_imageblit(info, image);
9028c2ecf20Sopenharmony_ci		return;
9038c2ecf20Sopenharmony_ci	}
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	/* If 24-plane FB and the image is 8-plane with CLUT, we can do it. */
9068c2ecf20Sopenharmony_ci	if (!is8bpp && image->depth == 8) {
9078c2ecf20Sopenharmony_ci		tgafb_clut_imageblit(info, image);
9088c2ecf20Sopenharmony_ci		return;
9098c2ecf20Sopenharmony_ci	}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/* Silently return... */
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci/**
9158c2ecf20Sopenharmony_ci *      tgafb_fillrect - REQUIRED function. Can use generic routines if
9168c2ecf20Sopenharmony_ci *                       non acclerated hardware and packed pixel based.
9178c2ecf20Sopenharmony_ci *                       Draws a rectangle on the screen.
9188c2ecf20Sopenharmony_ci *
9198c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
9208c2ecf20Sopenharmony_ci *      @rect: structure defining the rectagle and operation.
9218c2ecf20Sopenharmony_ci */
9228c2ecf20Sopenharmony_cistatic void
9238c2ecf20Sopenharmony_citgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
9248c2ecf20Sopenharmony_ci{
9258c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
9268c2ecf20Sopenharmony_ci	int is8bpp = info->var.bits_per_pixel == 8;
9278c2ecf20Sopenharmony_ci	u32 dx, dy, width, height, vxres, vyres, color;
9288c2ecf20Sopenharmony_ci	unsigned long pos, align, line_length, i, j;
9298c2ecf20Sopenharmony_ci	void __iomem *regs_base;
9308c2ecf20Sopenharmony_ci	void __iomem *fb_base;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	dx = rect->dx;
9338c2ecf20Sopenharmony_ci	dy = rect->dy;
9348c2ecf20Sopenharmony_ci	width = rect->width;
9358c2ecf20Sopenharmony_ci	height = rect->height;
9368c2ecf20Sopenharmony_ci	vxres = info->var.xres_virtual;
9378c2ecf20Sopenharmony_ci	vyres = info->var.yres_virtual;
9388c2ecf20Sopenharmony_ci	line_length = info->fix.line_length;
9398c2ecf20Sopenharmony_ci	regs_base = par->tga_regs_base;
9408c2ecf20Sopenharmony_ci	fb_base = par->tga_fb_base;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	/* Crop the rectangle to the screen.  */
9438c2ecf20Sopenharmony_ci	if (dx > vxres || dy > vyres || !width || !height)
9448c2ecf20Sopenharmony_ci		return;
9458c2ecf20Sopenharmony_ci	if (dx + width > vxres)
9468c2ecf20Sopenharmony_ci		width = vxres - dx;
9478c2ecf20Sopenharmony_ci	if (dy + height > vyres)
9488c2ecf20Sopenharmony_ci		height = vyres - dy;
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	pos = dy * line_length + dx * (is8bpp ? 1 : 4);
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci	/* ??? We could implement ROP_XOR with opaque fill mode
9538c2ecf20Sopenharmony_ci	   and a RasterOp setting of GXxor, but as far as I can
9548c2ecf20Sopenharmony_ci	   tell, this mode is not actually used in the kernel.
9558c2ecf20Sopenharmony_ci	   Thus I am ignoring it for now.  */
9568c2ecf20Sopenharmony_ci	if (rect->rop != ROP_COPY) {
9578c2ecf20Sopenharmony_ci		cfb_fillrect(info, rect);
9588c2ecf20Sopenharmony_ci		return;
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	/* Expand the color value to fill 8 pixels.  */
9628c2ecf20Sopenharmony_ci	color = rect->color;
9638c2ecf20Sopenharmony_ci	if (is8bpp) {
9648c2ecf20Sopenharmony_ci		color |= color << 8;
9658c2ecf20Sopenharmony_ci		color |= color << 16;
9668c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
9678c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
9688c2ecf20Sopenharmony_ci	} else {
9698c2ecf20Sopenharmony_ci		if (color < 16)
9708c2ecf20Sopenharmony_ci			color = ((u32 *)info->pseudo_palette)[color];
9718c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
9728c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
9738c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG);
9748c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG);
9758c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG);
9768c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG);
9778c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG);
9788c2ecf20Sopenharmony_ci		__raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG);
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	/* The DATA register holds the fill mask for block fill mode.
9828c2ecf20Sopenharmony_ci	   Since we're not stippling, this is all ones.  */
9838c2ecf20Sopenharmony_ci	__raw_writel(0xffffffff, regs_base + TGA_DATA_REG);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* Enable block fill mode.  */
9868c2ecf20Sopenharmony_ci	__raw_writel((is8bpp
9878c2ecf20Sopenharmony_ci		      ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL
9888c2ecf20Sopenharmony_ci		      : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL),
9898c2ecf20Sopenharmony_ci		     regs_base + TGA_MODE_REG);
9908c2ecf20Sopenharmony_ci	wmb();
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	/* We can fill 2k pixels per operation.  Notice blocks that fit
9938c2ecf20Sopenharmony_ci	   the width of the screen so that we can take advantage of this
9948c2ecf20Sopenharmony_ci	   and fill more than one line per write.  */
9958c2ecf20Sopenharmony_ci	if (width == line_length) {
9968c2ecf20Sopenharmony_ci		width *= height;
9978c2ecf20Sopenharmony_ci		height = 1;
9988c2ecf20Sopenharmony_ci	}
9998c2ecf20Sopenharmony_ci
10008c2ecf20Sopenharmony_ci	/* The write into the frame buffer must be aligned to 4 bytes,
10018c2ecf20Sopenharmony_ci	   but we are allowed to encode the offset within the word in
10028c2ecf20Sopenharmony_ci	   the data word written.  */
10038c2ecf20Sopenharmony_ci	align = (pos & 3) << 16;
10048c2ecf20Sopenharmony_ci	pos &= -4;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	if (width <= 2048) {
10078c2ecf20Sopenharmony_ci		u32 data;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci		data = (width - 1) | align;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci		for (i = 0; i < height; ++i) {
10128c2ecf20Sopenharmony_ci			__raw_writel(data, fb_base + pos);
10138c2ecf20Sopenharmony_ci			pos += line_length;
10148c2ecf20Sopenharmony_ci		}
10158c2ecf20Sopenharmony_ci	} else {
10168c2ecf20Sopenharmony_ci		unsigned long Bpp = (is8bpp ? 1 : 4);
10178c2ecf20Sopenharmony_ci		unsigned long nwidth = width & -2048;
10188c2ecf20Sopenharmony_ci		u32 fdata, ldata;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci		fdata = (2048 - 1) | align;
10218c2ecf20Sopenharmony_ci		ldata = ((width & 2047) - 1) | align;
10228c2ecf20Sopenharmony_ci
10238c2ecf20Sopenharmony_ci		for (i = 0; i < height; ++i) {
10248c2ecf20Sopenharmony_ci			for (j = 0; j < nwidth; j += 2048)
10258c2ecf20Sopenharmony_ci				__raw_writel(fdata, fb_base + pos + j*Bpp);
10268c2ecf20Sopenharmony_ci			if (j < width)
10278c2ecf20Sopenharmony_ci				__raw_writel(ldata, fb_base + pos + j*Bpp);
10288c2ecf20Sopenharmony_ci			pos += line_length;
10298c2ecf20Sopenharmony_ci		}
10308c2ecf20Sopenharmony_ci	}
10318c2ecf20Sopenharmony_ci	wmb();
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	/* Disable block fill mode.  */
10348c2ecf20Sopenharmony_ci	__raw_writel((is8bpp
10358c2ecf20Sopenharmony_ci		      ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
10368c2ecf20Sopenharmony_ci		      : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
10378c2ecf20Sopenharmony_ci		     regs_base + TGA_MODE_REG);
10388c2ecf20Sopenharmony_ci}
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci/**
10418c2ecf20Sopenharmony_ci *      tgafb_copyarea - REQUIRED function. Can use generic routines if
10428c2ecf20Sopenharmony_ci *                       non acclerated hardware and packed pixel based.
10438c2ecf20Sopenharmony_ci *                       Copies on area of the screen to another area.
10448c2ecf20Sopenharmony_ci *
10458c2ecf20Sopenharmony_ci *      @info: frame buffer structure that represents a single frame buffer
10468c2ecf20Sopenharmony_ci *      @area: structure defining the source and destination.
10478c2ecf20Sopenharmony_ci */
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci/* Handle the special case of copying entire lines, e.g. during scrolling.
10508c2ecf20Sopenharmony_ci   We can avoid a lot of needless computation in this case.  In the 8bpp
10518c2ecf20Sopenharmony_ci   case we need to use the COPY64 registers instead of mask writes into
10528c2ecf20Sopenharmony_ci   the frame buffer to achieve maximum performance.  */
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic inline void
10558c2ecf20Sopenharmony_cicopyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy,
10568c2ecf20Sopenharmony_ci		   u32 height, u32 width)
10578c2ecf20Sopenharmony_ci{
10588c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
10598c2ecf20Sopenharmony_ci	void __iomem *tga_regs = par->tga_regs_base;
10608c2ecf20Sopenharmony_ci	unsigned long dpos, spos, i, n64;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* Set up the MODE and PIXELSHIFT registers.  */
10638c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
10648c2ecf20Sopenharmony_ci	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
10658c2ecf20Sopenharmony_ci	wmb();
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	n64 = (height * width) / 64;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	if (sy < dy) {
10708c2ecf20Sopenharmony_ci		spos = (sy + height) * width;
10718c2ecf20Sopenharmony_ci		dpos = (dy + height) * width;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci		for (i = 0; i < n64; ++i) {
10748c2ecf20Sopenharmony_ci			spos -= 64;
10758c2ecf20Sopenharmony_ci			dpos -= 64;
10768c2ecf20Sopenharmony_ci			__raw_writel(spos, tga_regs+TGA_COPY64_SRC);
10778c2ecf20Sopenharmony_ci			wmb();
10788c2ecf20Sopenharmony_ci			__raw_writel(dpos, tga_regs+TGA_COPY64_DST);
10798c2ecf20Sopenharmony_ci			wmb();
10808c2ecf20Sopenharmony_ci		}
10818c2ecf20Sopenharmony_ci	} else {
10828c2ecf20Sopenharmony_ci		spos = sy * width;
10838c2ecf20Sopenharmony_ci		dpos = dy * width;
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci		for (i = 0; i < n64; ++i) {
10868c2ecf20Sopenharmony_ci			__raw_writel(spos, tga_regs+TGA_COPY64_SRC);
10878c2ecf20Sopenharmony_ci			wmb();
10888c2ecf20Sopenharmony_ci			__raw_writel(dpos, tga_regs+TGA_COPY64_DST);
10898c2ecf20Sopenharmony_ci			wmb();
10908c2ecf20Sopenharmony_ci			spos += 64;
10918c2ecf20Sopenharmony_ci			dpos += 64;
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/* Reset the MODE register to normal.  */
10968c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
10978c2ecf20Sopenharmony_ci}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_cistatic inline void
11008c2ecf20Sopenharmony_cicopyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
11018c2ecf20Sopenharmony_ci		    u32 height, u32 width)
11028c2ecf20Sopenharmony_ci{
11038c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
11048c2ecf20Sopenharmony_ci	void __iomem *tga_regs = par->tga_regs_base;
11058c2ecf20Sopenharmony_ci	void __iomem *tga_fb = par->tga_fb_base;
11068c2ecf20Sopenharmony_ci	void __iomem *src;
11078c2ecf20Sopenharmony_ci	void __iomem *dst;
11088c2ecf20Sopenharmony_ci	unsigned long i, n16;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	/* Set up the MODE and PIXELSHIFT registers.  */
11118c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
11128c2ecf20Sopenharmony_ci	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
11138c2ecf20Sopenharmony_ci	wmb();
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci	n16 = (height * width) / 16;
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	if (sy < dy) {
11188c2ecf20Sopenharmony_ci		src = tga_fb + (sy + height) * width * 4;
11198c2ecf20Sopenharmony_ci		dst = tga_fb + (dy + height) * width * 4;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci		for (i = 0; i < n16; ++i) {
11228c2ecf20Sopenharmony_ci			src -= 64;
11238c2ecf20Sopenharmony_ci			dst -= 64;
11248c2ecf20Sopenharmony_ci			__raw_writel(0xffff, src);
11258c2ecf20Sopenharmony_ci			wmb();
11268c2ecf20Sopenharmony_ci			__raw_writel(0xffff, dst);
11278c2ecf20Sopenharmony_ci			wmb();
11288c2ecf20Sopenharmony_ci		}
11298c2ecf20Sopenharmony_ci	} else {
11308c2ecf20Sopenharmony_ci		src = tga_fb + sy * width * 4;
11318c2ecf20Sopenharmony_ci		dst = tga_fb + dy * width * 4;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci		for (i = 0; i < n16; ++i) {
11348c2ecf20Sopenharmony_ci			__raw_writel(0xffff, src);
11358c2ecf20Sopenharmony_ci			wmb();
11368c2ecf20Sopenharmony_ci			__raw_writel(0xffff, dst);
11378c2ecf20Sopenharmony_ci			wmb();
11388c2ecf20Sopenharmony_ci			src += 64;
11398c2ecf20Sopenharmony_ci			dst += 64;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	/* Reset the MODE register to normal.  */
11448c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
11458c2ecf20Sopenharmony_ci}
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci/* The (almost) general case of backward copy in 8bpp mode.  */
11488c2ecf20Sopenharmony_cistatic inline void
11498c2ecf20Sopenharmony_cicopyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
11508c2ecf20Sopenharmony_ci	      u32 height, u32 width, u32 line_length,
11518c2ecf20Sopenharmony_ci	      const struct fb_copyarea *area)
11528c2ecf20Sopenharmony_ci{
11538c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *) info->par;
11548c2ecf20Sopenharmony_ci	unsigned i, yincr;
11558c2ecf20Sopenharmony_ci	int depos, sepos, backward, last_step, step;
11568c2ecf20Sopenharmony_ci	u32 mask_last;
11578c2ecf20Sopenharmony_ci	unsigned n32;
11588c2ecf20Sopenharmony_ci	void __iomem *tga_regs;
11598c2ecf20Sopenharmony_ci	void __iomem *tga_fb;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	/* Do acceleration only if we are aligned on 8 pixels */
11628c2ecf20Sopenharmony_ci	if ((dx | sx | width) & 7) {
11638c2ecf20Sopenharmony_ci		cfb_copyarea(info, area);
11648c2ecf20Sopenharmony_ci		return;
11658c2ecf20Sopenharmony_ci	}
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	yincr = line_length;
11688c2ecf20Sopenharmony_ci	if (dy > sy) {
11698c2ecf20Sopenharmony_ci		dy += height - 1;
11708c2ecf20Sopenharmony_ci		sy += height - 1;
11718c2ecf20Sopenharmony_ci		yincr = -yincr;
11728c2ecf20Sopenharmony_ci	}
11738c2ecf20Sopenharmony_ci	backward = dy == sy && dx > sx && dx < sx + width;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	/* Compute the offsets and alignments in the frame buffer.
11768c2ecf20Sopenharmony_ci	   More than anything else, these control how we do copies.  */
11778c2ecf20Sopenharmony_ci	depos = dy * line_length + dx;
11788c2ecf20Sopenharmony_ci	sepos = sy * line_length + sx;
11798c2ecf20Sopenharmony_ci	if (backward) {
11808c2ecf20Sopenharmony_ci		depos += width;
11818c2ecf20Sopenharmony_ci		sepos += width;
11828c2ecf20Sopenharmony_ci	}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	/* Next copy full words at a time.  */
11858c2ecf20Sopenharmony_ci	n32 = width / 32;
11868c2ecf20Sopenharmony_ci	last_step = width % 32;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	/* Finally copy the unaligned head of the span.  */
11898c2ecf20Sopenharmony_ci	mask_last = (1ul << last_step) - 1;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci	if (!backward) {
11928c2ecf20Sopenharmony_ci		step = 32;
11938c2ecf20Sopenharmony_ci		last_step = 32;
11948c2ecf20Sopenharmony_ci	} else {
11958c2ecf20Sopenharmony_ci		step = -32;
11968c2ecf20Sopenharmony_ci		last_step = -last_step;
11978c2ecf20Sopenharmony_ci		sepos -= 32;
11988c2ecf20Sopenharmony_ci		depos -= 32;
11998c2ecf20Sopenharmony_ci	}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_ci	tga_regs = par->tga_regs_base;
12028c2ecf20Sopenharmony_ci	tga_fb = par->tga_fb_base;
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	/* Set up the MODE and PIXELSHIFT registers.  */
12058c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
12068c2ecf20Sopenharmony_ci	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
12078c2ecf20Sopenharmony_ci	wmb();
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	for (i = 0; i < height; ++i) {
12108c2ecf20Sopenharmony_ci		unsigned long j;
12118c2ecf20Sopenharmony_ci		void __iomem *sfb;
12128c2ecf20Sopenharmony_ci		void __iomem *dfb;
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci		sfb = tga_fb + sepos;
12158c2ecf20Sopenharmony_ci		dfb = tga_fb + depos;
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ci		for (j = 0; j < n32; j++) {
12188c2ecf20Sopenharmony_ci			if (j < 2 && j + 1 < n32 && !backward &&
12198c2ecf20Sopenharmony_ci			    !(((unsigned long)sfb | (unsigned long)dfb) & 63)) {
12208c2ecf20Sopenharmony_ci				do {
12218c2ecf20Sopenharmony_ci					__raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
12228c2ecf20Sopenharmony_ci					wmb();
12238c2ecf20Sopenharmony_ci					__raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
12248c2ecf20Sopenharmony_ci					wmb();
12258c2ecf20Sopenharmony_ci					sfb += 64;
12268c2ecf20Sopenharmony_ci					dfb += 64;
12278c2ecf20Sopenharmony_ci					j += 2;
12288c2ecf20Sopenharmony_ci				} while (j + 1 < n32);
12298c2ecf20Sopenharmony_ci				j--;
12308c2ecf20Sopenharmony_ci				continue;
12318c2ecf20Sopenharmony_ci			}
12328c2ecf20Sopenharmony_ci			__raw_writel(0xffffffff, sfb);
12338c2ecf20Sopenharmony_ci			wmb();
12348c2ecf20Sopenharmony_ci			__raw_writel(0xffffffff, dfb);
12358c2ecf20Sopenharmony_ci			wmb();
12368c2ecf20Sopenharmony_ci			sfb += step;
12378c2ecf20Sopenharmony_ci			dfb += step;
12388c2ecf20Sopenharmony_ci		}
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci		if (mask_last) {
12418c2ecf20Sopenharmony_ci			sfb += last_step - step;
12428c2ecf20Sopenharmony_ci			dfb += last_step - step;
12438c2ecf20Sopenharmony_ci			__raw_writel(mask_last, sfb);
12448c2ecf20Sopenharmony_ci			wmb();
12458c2ecf20Sopenharmony_ci			__raw_writel(mask_last, dfb);
12468c2ecf20Sopenharmony_ci			wmb();
12478c2ecf20Sopenharmony_ci		}
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci		sepos += yincr;
12508c2ecf20Sopenharmony_ci		depos += yincr;
12518c2ecf20Sopenharmony_ci	}
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	/* Reset the MODE register to normal.  */
12548c2ecf20Sopenharmony_ci	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic void
12588c2ecf20Sopenharmony_citgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
12598c2ecf20Sopenharmony_ci{
12608c2ecf20Sopenharmony_ci	unsigned long dx, dy, width, height, sx, sy, vxres, vyres;
12618c2ecf20Sopenharmony_ci	unsigned long line_length, bpp;
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	dx = area->dx;
12648c2ecf20Sopenharmony_ci	dy = area->dy;
12658c2ecf20Sopenharmony_ci	width = area->width;
12668c2ecf20Sopenharmony_ci	height = area->height;
12678c2ecf20Sopenharmony_ci	sx = area->sx;
12688c2ecf20Sopenharmony_ci	sy = area->sy;
12698c2ecf20Sopenharmony_ci	vxres = info->var.xres_virtual;
12708c2ecf20Sopenharmony_ci	vyres = info->var.yres_virtual;
12718c2ecf20Sopenharmony_ci	line_length = info->fix.line_length;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	/* The top left corners must be in the virtual screen.  */
12748c2ecf20Sopenharmony_ci	if (dx > vxres || sx > vxres || dy > vyres || sy > vyres)
12758c2ecf20Sopenharmony_ci		return;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	/* Clip the destination.  */
12788c2ecf20Sopenharmony_ci	if (dx + width > vxres)
12798c2ecf20Sopenharmony_ci		width = vxres - dx;
12808c2ecf20Sopenharmony_ci	if (dy + height > vyres)
12818c2ecf20Sopenharmony_ci		height = vyres - dy;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/* The source must be completely inside the virtual screen.  */
12848c2ecf20Sopenharmony_ci	if (sx + width > vxres || sy + height > vyres)
12858c2ecf20Sopenharmony_ci		return;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	bpp = info->var.bits_per_pixel;
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	/* Detect copies of the entire line.  */
12908c2ecf20Sopenharmony_ci	if (!(line_length & 63) && width * (bpp >> 3) == line_length) {
12918c2ecf20Sopenharmony_ci		if (bpp == 8)
12928c2ecf20Sopenharmony_ci			copyarea_line_8bpp(info, dy, sy, height, width);
12938c2ecf20Sopenharmony_ci		else
12948c2ecf20Sopenharmony_ci			copyarea_line_32bpp(info, dy, sy, height, width);
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	/* ??? The documentation is unclear to me exactly how the pixelshift
12988c2ecf20Sopenharmony_ci	   register works in 32bpp mode.  Since I don't have hardware to test,
12998c2ecf20Sopenharmony_ci	   give up for now and fall back on the generic routines.  */
13008c2ecf20Sopenharmony_ci	else if (bpp == 32)
13018c2ecf20Sopenharmony_ci		cfb_copyarea(info, area);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	else
13048c2ecf20Sopenharmony_ci		copyarea_8bpp(info, dx, dy, sx, sy, height,
13058c2ecf20Sopenharmony_ci			      width, line_length, area);
13068c2ecf20Sopenharmony_ci}
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci/*
13108c2ecf20Sopenharmony_ci *  Initialisation
13118c2ecf20Sopenharmony_ci */
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cistatic void
13148c2ecf20Sopenharmony_citgafb_init_fix(struct fb_info *info)
13158c2ecf20Sopenharmony_ci{
13168c2ecf20Sopenharmony_ci	struct tga_par *par = (struct tga_par *)info->par;
13178c2ecf20Sopenharmony_ci	int tga_bus_pci = dev_is_pci(par->dev);
13188c2ecf20Sopenharmony_ci	int tga_bus_tc = TGA_BUS_TC(par->dev);
13198c2ecf20Sopenharmony_ci	u8 tga_type = par->tga_type;
13208c2ecf20Sopenharmony_ci	const char *tga_type_name = NULL;
13218c2ecf20Sopenharmony_ci	unsigned memory_size;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	switch (tga_type) {
13248c2ecf20Sopenharmony_ci	case TGA_TYPE_8PLANE:
13258c2ecf20Sopenharmony_ci		if (tga_bus_pci)
13268c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLXp-E1";
13278c2ecf20Sopenharmony_ci		if (tga_bus_tc)
13288c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLX-E1";
13298c2ecf20Sopenharmony_ci		memory_size = 2097152;
13308c2ecf20Sopenharmony_ci		break;
13318c2ecf20Sopenharmony_ci	case TGA_TYPE_24PLANE:
13328c2ecf20Sopenharmony_ci		if (tga_bus_pci)
13338c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLXp-E2";
13348c2ecf20Sopenharmony_ci		if (tga_bus_tc)
13358c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLX-E2";
13368c2ecf20Sopenharmony_ci		memory_size = 8388608;
13378c2ecf20Sopenharmony_ci		break;
13388c2ecf20Sopenharmony_ci	case TGA_TYPE_24PLUSZ:
13398c2ecf20Sopenharmony_ci		if (tga_bus_pci)
13408c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLXp-E3";
13418c2ecf20Sopenharmony_ci		if (tga_bus_tc)
13428c2ecf20Sopenharmony_ci			tga_type_name = "Digital ZLX-E3";
13438c2ecf20Sopenharmony_ci		memory_size = 16777216;
13448c2ecf20Sopenharmony_ci		break;
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci	if (!tga_type_name) {
13478c2ecf20Sopenharmony_ci		tga_type_name = "Unknown";
13488c2ecf20Sopenharmony_ci		memory_size = 16777216;
13498c2ecf20Sopenharmony_ci	}
13508c2ecf20Sopenharmony_ci
13518c2ecf20Sopenharmony_ci	strlcpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	info->fix.type = FB_TYPE_PACKED_PIXELS;
13548c2ecf20Sopenharmony_ci	info->fix.type_aux = 0;
13558c2ecf20Sopenharmony_ci	info->fix.visual = (tga_type == TGA_TYPE_8PLANE
13568c2ecf20Sopenharmony_ci			    ? FB_VISUAL_PSEUDOCOLOR
13578c2ecf20Sopenharmony_ci			    : FB_VISUAL_DIRECTCOLOR);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	info->fix.smem_start = (size_t) par->tga_fb_base;
13608c2ecf20Sopenharmony_ci	info->fix.smem_len = memory_size;
13618c2ecf20Sopenharmony_ci	info->fix.mmio_start = (size_t) par->tga_regs_base;
13628c2ecf20Sopenharmony_ci	info->fix.mmio_len = 512;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	info->fix.xpanstep = 0;
13658c2ecf20Sopenharmony_ci	info->fix.ypanstep = 0;
13668c2ecf20Sopenharmony_ci	info->fix.ywrapstep = 0;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	info->fix.accel = FB_ACCEL_DEC_TGA;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	/*
13718c2ecf20Sopenharmony_ci	 * These are needed by fb_set_logo_truepalette(), so we
13728c2ecf20Sopenharmony_ci	 * set them here for 24-plane cards.
13738c2ecf20Sopenharmony_ci	 */
13748c2ecf20Sopenharmony_ci	if (tga_type != TGA_TYPE_8PLANE) {
13758c2ecf20Sopenharmony_ci		info->var.red.length = 8;
13768c2ecf20Sopenharmony_ci		info->var.green.length = 8;
13778c2ecf20Sopenharmony_ci		info->var.blue.length = 8;
13788c2ecf20Sopenharmony_ci		info->var.red.offset = 16;
13798c2ecf20Sopenharmony_ci		info->var.green.offset = 8;
13808c2ecf20Sopenharmony_ci		info->var.blue.offset = 0;
13818c2ecf20Sopenharmony_ci	}
13828c2ecf20Sopenharmony_ci}
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_cistatic int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	/* We just use this to catch switches out of graphics mode. */
13878c2ecf20Sopenharmony_ci	tgafb_set_par(info); /* A bit of overkill for BASE_ADDR reset. */
13888c2ecf20Sopenharmony_ci	return 0;
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_cistatic int tgafb_register(struct device *dev)
13928c2ecf20Sopenharmony_ci{
13938c2ecf20Sopenharmony_ci	static const struct fb_videomode modedb_tc = {
13948c2ecf20Sopenharmony_ci		/* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
13958c2ecf20Sopenharmony_ci		"1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
13968c2ecf20Sopenharmony_ci		FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
13978c2ecf20Sopenharmony_ci	};
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_ci	static unsigned int const fb_offset_presets[4] = {
14008c2ecf20Sopenharmony_ci		TGA_8PLANE_FB_OFFSET,
14018c2ecf20Sopenharmony_ci		TGA_24PLANE_FB_OFFSET,
14028c2ecf20Sopenharmony_ci		0xffffffff,
14038c2ecf20Sopenharmony_ci		TGA_24PLUSZ_FB_OFFSET
14048c2ecf20Sopenharmony_ci	};
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	const struct fb_videomode *modedb_tga = NULL;
14078c2ecf20Sopenharmony_ci	resource_size_t bar0_start = 0, bar0_len = 0;
14088c2ecf20Sopenharmony_ci	const char *mode_option_tga = NULL;
14098c2ecf20Sopenharmony_ci	int tga_bus_pci = dev_is_pci(dev);
14108c2ecf20Sopenharmony_ci	int tga_bus_tc = TGA_BUS_TC(dev);
14118c2ecf20Sopenharmony_ci	unsigned int modedbsize_tga = 0;
14128c2ecf20Sopenharmony_ci	void __iomem *mem_base;
14138c2ecf20Sopenharmony_ci	struct fb_info *info;
14148c2ecf20Sopenharmony_ci	struct tga_par *par;
14158c2ecf20Sopenharmony_ci	u8 tga_type;
14168c2ecf20Sopenharmony_ci	int ret = 0;
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	/* Enable device in PCI config.  */
14198c2ecf20Sopenharmony_ci	if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
14208c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
14218c2ecf20Sopenharmony_ci		return -ENODEV;
14228c2ecf20Sopenharmony_ci	}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	/* Allocate the fb and par structures.  */
14258c2ecf20Sopenharmony_ci	info = framebuffer_alloc(sizeof(struct tga_par), dev);
14268c2ecf20Sopenharmony_ci	if (!info)
14278c2ecf20Sopenharmony_ci		return -ENOMEM;
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_ci	par = info->par;
14308c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, info);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	/* Request the mem regions.  */
14338c2ecf20Sopenharmony_ci	ret = -ENODEV;
14348c2ecf20Sopenharmony_ci	if (tga_bus_pci) {
14358c2ecf20Sopenharmony_ci		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
14368c2ecf20Sopenharmony_ci		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
14378c2ecf20Sopenharmony_ci	}
14388c2ecf20Sopenharmony_ci	if (tga_bus_tc) {
14398c2ecf20Sopenharmony_ci		bar0_start = to_tc_dev(dev)->resource.start;
14408c2ecf20Sopenharmony_ci		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci	if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
14438c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: cannot reserve FB region\n");
14448c2ecf20Sopenharmony_ci		goto err0;
14458c2ecf20Sopenharmony_ci	}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/* Map the framebuffer.  */
14488c2ecf20Sopenharmony_ci	mem_base = ioremap(bar0_start, bar0_len);
14498c2ecf20Sopenharmony_ci	if (!mem_base) {
14508c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: Cannot map MMIO\n");
14518c2ecf20Sopenharmony_ci		goto err1;
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	/* Grab info about the card.  */
14558c2ecf20Sopenharmony_ci	tga_type = (readl(mem_base) >> 12) & 0x0f;
14568c2ecf20Sopenharmony_ci	par->dev = dev;
14578c2ecf20Sopenharmony_ci	par->tga_mem_base = mem_base;
14588c2ecf20Sopenharmony_ci	par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
14598c2ecf20Sopenharmony_ci	par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
14608c2ecf20Sopenharmony_ci	par->tga_type = tga_type;
14618c2ecf20Sopenharmony_ci	if (tga_bus_pci)
14628c2ecf20Sopenharmony_ci		par->tga_chip_rev = (to_pci_dev(dev))->revision;
14638c2ecf20Sopenharmony_ci	if (tga_bus_tc)
14648c2ecf20Sopenharmony_ci		par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	/* Setup framebuffer.  */
14678c2ecf20Sopenharmony_ci	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
14688c2ecf20Sopenharmony_ci		      FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT;
14698c2ecf20Sopenharmony_ci	info->fbops = &tgafb_ops;
14708c2ecf20Sopenharmony_ci	info->screen_base = par->tga_fb_base;
14718c2ecf20Sopenharmony_ci	info->pseudo_palette = par->palette;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	/* This should give a reasonable default video mode.  */
14748c2ecf20Sopenharmony_ci	if (tga_bus_pci) {
14758c2ecf20Sopenharmony_ci		mode_option_tga = mode_option_pci;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci	if (tga_bus_tc) {
14788c2ecf20Sopenharmony_ci		mode_option_tga = mode_option_tc;
14798c2ecf20Sopenharmony_ci		modedb_tga = &modedb_tc;
14808c2ecf20Sopenharmony_ci		modedbsize_tga = 1;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	tgafb_init_fix(info);
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	ret = fb_find_mode(&info->var, info,
14868c2ecf20Sopenharmony_ci			   mode_option ? mode_option : mode_option_tga,
14878c2ecf20Sopenharmony_ci			   modedb_tga, modedbsize_tga, NULL,
14888c2ecf20Sopenharmony_ci			   tga_type == TGA_TYPE_8PLANE ? 8 : 32);
14898c2ecf20Sopenharmony_ci	if (ret == 0 || ret == 4) {
14908c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: Could not find valid video mode\n");
14918c2ecf20Sopenharmony_ci		ret = -EINVAL;
14928c2ecf20Sopenharmony_ci		goto err1;
14938c2ecf20Sopenharmony_ci	}
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	if (fb_alloc_cmap(&info->cmap, 256, 0)) {
14968c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: Could not allocate color map\n");
14978c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14988c2ecf20Sopenharmony_ci		goto err1;
14998c2ecf20Sopenharmony_ci	}
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	tgafb_set_par(info);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	if (register_framebuffer(info) < 0) {
15048c2ecf20Sopenharmony_ci		printk(KERN_ERR "tgafb: Could not register framebuffer\n");
15058c2ecf20Sopenharmony_ci		ret = -EINVAL;
15068c2ecf20Sopenharmony_ci		goto err2;
15078c2ecf20Sopenharmony_ci	}
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci	if (tga_bus_pci) {
15108c2ecf20Sopenharmony_ci		pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
15118c2ecf20Sopenharmony_ci			par->tga_chip_rev);
15128c2ecf20Sopenharmony_ci		pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
15138c2ecf20Sopenharmony_ci			to_pci_dev(dev)->bus->number,
15148c2ecf20Sopenharmony_ci			PCI_SLOT(to_pci_dev(dev)->devfn),
15158c2ecf20Sopenharmony_ci			PCI_FUNC(to_pci_dev(dev)->devfn));
15168c2ecf20Sopenharmony_ci	}
15178c2ecf20Sopenharmony_ci	if (tga_bus_tc)
15188c2ecf20Sopenharmony_ci		pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
15198c2ecf20Sopenharmony_ci			par->tga_chip_rev);
15208c2ecf20Sopenharmony_ci	fb_info(info, "%s frame buffer device at 0x%lx\n",
15218c2ecf20Sopenharmony_ci		info->fix.id, (long)bar0_start);
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci	return 0;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci err2:
15268c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
15278c2ecf20Sopenharmony_ci err1:
15288c2ecf20Sopenharmony_ci	if (mem_base)
15298c2ecf20Sopenharmony_ci		iounmap(mem_base);
15308c2ecf20Sopenharmony_ci	release_mem_region(bar0_start, bar0_len);
15318c2ecf20Sopenharmony_ci err0:
15328c2ecf20Sopenharmony_ci	framebuffer_release(info);
15338c2ecf20Sopenharmony_ci	return ret;
15348c2ecf20Sopenharmony_ci}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_cistatic void tgafb_unregister(struct device *dev)
15378c2ecf20Sopenharmony_ci{
15388c2ecf20Sopenharmony_ci	resource_size_t bar0_start = 0, bar0_len = 0;
15398c2ecf20Sopenharmony_ci	int tga_bus_pci = dev_is_pci(dev);
15408c2ecf20Sopenharmony_ci	int tga_bus_tc = TGA_BUS_TC(dev);
15418c2ecf20Sopenharmony_ci	struct fb_info *info = NULL;
15428c2ecf20Sopenharmony_ci	struct tga_par *par;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	info = dev_get_drvdata(dev);
15458c2ecf20Sopenharmony_ci	if (!info)
15468c2ecf20Sopenharmony_ci		return;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	par = info->par;
15498c2ecf20Sopenharmony_ci	unregister_framebuffer(info);
15508c2ecf20Sopenharmony_ci	fb_dealloc_cmap(&info->cmap);
15518c2ecf20Sopenharmony_ci	iounmap(par->tga_mem_base);
15528c2ecf20Sopenharmony_ci	if (tga_bus_pci) {
15538c2ecf20Sopenharmony_ci		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
15548c2ecf20Sopenharmony_ci		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci	if (tga_bus_tc) {
15578c2ecf20Sopenharmony_ci		bar0_start = to_tc_dev(dev)->resource.start;
15588c2ecf20Sopenharmony_ci		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
15598c2ecf20Sopenharmony_ci	}
15608c2ecf20Sopenharmony_ci	release_mem_region(bar0_start, bar0_len);
15618c2ecf20Sopenharmony_ci	framebuffer_release(info);
15628c2ecf20Sopenharmony_ci}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_cistatic void tgafb_exit(void)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	tc_unregister_driver(&tgafb_tc_driver);
15678c2ecf20Sopenharmony_ci	pci_unregister_driver(&tgafb_pci_driver);
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci#ifndef MODULE
15718c2ecf20Sopenharmony_cistatic int tgafb_setup(char *arg)
15728c2ecf20Sopenharmony_ci{
15738c2ecf20Sopenharmony_ci	char *this_opt;
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	if (arg && *arg) {
15768c2ecf20Sopenharmony_ci		while ((this_opt = strsep(&arg, ","))) {
15778c2ecf20Sopenharmony_ci			if (!*this_opt)
15788c2ecf20Sopenharmony_ci				continue;
15798c2ecf20Sopenharmony_ci			if (!strncmp(this_opt, "mode:", 5))
15808c2ecf20Sopenharmony_ci				mode_option = this_opt+5;
15818c2ecf20Sopenharmony_ci			else
15828c2ecf20Sopenharmony_ci				printk(KERN_ERR
15838c2ecf20Sopenharmony_ci				       "tgafb: unknown parameter %s\n",
15848c2ecf20Sopenharmony_ci				       this_opt);
15858c2ecf20Sopenharmony_ci		}
15868c2ecf20Sopenharmony_ci	}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	return 0;
15898c2ecf20Sopenharmony_ci}
15908c2ecf20Sopenharmony_ci#endif /* !MODULE */
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_cistatic int tgafb_init(void)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	int status;
15958c2ecf20Sopenharmony_ci#ifndef MODULE
15968c2ecf20Sopenharmony_ci	char *option = NULL;
15978c2ecf20Sopenharmony_ci
15988c2ecf20Sopenharmony_ci	if (fb_get_options("tgafb", &option))
15998c2ecf20Sopenharmony_ci		return -ENODEV;
16008c2ecf20Sopenharmony_ci	tgafb_setup(option);
16018c2ecf20Sopenharmony_ci#endif
16028c2ecf20Sopenharmony_ci	status = pci_register_driver(&tgafb_pci_driver);
16038c2ecf20Sopenharmony_ci	if (!status)
16048c2ecf20Sopenharmony_ci		status = tc_register_driver(&tgafb_tc_driver);
16058c2ecf20Sopenharmony_ci	return status;
16068c2ecf20Sopenharmony_ci}
16078c2ecf20Sopenharmony_ci
16088c2ecf20Sopenharmony_ci/*
16098c2ecf20Sopenharmony_ci *  Modularisation
16108c2ecf20Sopenharmony_ci */
16118c2ecf20Sopenharmony_ci
16128c2ecf20Sopenharmony_cimodule_init(tgafb_init);
16138c2ecf20Sopenharmony_cimodule_exit(tgafb_exit);
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
16168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1617