xref: /openbmc/linux/drivers/video/fbdev/tgafb.c (revision b3e148d7)
1f7018c21STomi Valkeinen /*
2f7018c21STomi Valkeinen  *  linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device
3f7018c21STomi Valkeinen  *
4f7018c21STomi Valkeinen  *	Copyright (C) 1995 Jay Estabrook
5f7018c21STomi Valkeinen  *	Copyright (C) 1997 Geert Uytterhoeven
6f7018c21STomi Valkeinen  *	Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha
7f7018c21STomi Valkeinen  *	Copyright (C) 2002 Richard Henderson
8f7018c21STomi Valkeinen  *	Copyright (C) 2006, 2007  Maciej W. Rozycki
9f7018c21STomi Valkeinen  *
10f7018c21STomi Valkeinen  *  This file is subject to the terms and conditions of the GNU General Public
11f7018c21STomi Valkeinen  *  License. See the file COPYING in the main directory of this archive for
12f7018c21STomi Valkeinen  *  more details.
13f7018c21STomi Valkeinen  */
14f7018c21STomi Valkeinen 
15145eed48SThomas Zimmermann #include <linux/aperture.h>
16f7018c21STomi Valkeinen #include <linux/bitrev.h>
17f7018c21STomi Valkeinen #include <linux/compiler.h>
18f7018c21STomi Valkeinen #include <linux/delay.h>
19f7018c21STomi Valkeinen #include <linux/device.h>
20f7018c21STomi Valkeinen #include <linux/errno.h>
21f7018c21STomi Valkeinen #include <linux/fb.h>
22f7018c21STomi Valkeinen #include <linux/init.h>
23f7018c21STomi Valkeinen #include <linux/ioport.h>
24f7018c21STomi Valkeinen #include <linux/kernel.h>
25f7018c21STomi Valkeinen #include <linux/mm.h>
26f7018c21STomi Valkeinen #include <linux/module.h>
27f7018c21STomi Valkeinen #include <linux/pci.h>
28f7018c21STomi Valkeinen #include <linux/selection.h>
29f7018c21STomi Valkeinen #include <linux/string.h>
30f7018c21STomi Valkeinen #include <linux/tc.h>
31f7018c21STomi Valkeinen 
32f7018c21STomi Valkeinen #include <asm/io.h>
33f7018c21STomi Valkeinen 
34f7018c21STomi Valkeinen #include <video/tgafb.h>
35f7018c21STomi Valkeinen 
36f7018c21STomi Valkeinen #ifdef CONFIG_TC
37f7018c21STomi Valkeinen #define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type)
38f7018c21STomi Valkeinen #else
39f7018c21STomi Valkeinen #define TGA_BUS_TC(dev) 0
40f7018c21STomi Valkeinen #endif
41f7018c21STomi Valkeinen 
42f7018c21STomi Valkeinen /*
43f7018c21STomi Valkeinen  * Local functions.
44f7018c21STomi Valkeinen  */
45f7018c21STomi Valkeinen 
46f7018c21STomi Valkeinen static int tgafb_check_var(struct fb_var_screeninfo *, struct fb_info *);
47f7018c21STomi Valkeinen static int tgafb_set_par(struct fb_info *);
48f7018c21STomi Valkeinen static void tgafb_set_pll(struct tga_par *, int);
49f7018c21STomi Valkeinen static int tgafb_setcolreg(unsigned, unsigned, unsigned, unsigned,
50f7018c21STomi Valkeinen 			   unsigned, struct fb_info *);
51f7018c21STomi Valkeinen static int tgafb_blank(int, struct fb_info *);
52f7018c21STomi Valkeinen static void tgafb_init_fix(struct fb_info *);
53f7018c21STomi Valkeinen 
54f7018c21STomi Valkeinen static void tgafb_imageblit(struct fb_info *, const struct fb_image *);
55f7018c21STomi Valkeinen static void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *);
56f7018c21STomi Valkeinen static void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *);
57f7018c21STomi Valkeinen static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
58f7018c21STomi Valkeinen 
59f7018c21STomi Valkeinen static int tgafb_register(struct device *dev);
60f7018c21STomi Valkeinen static void tgafb_unregister(struct device *dev);
61f7018c21STomi Valkeinen 
62f7018c21STomi Valkeinen static const char *mode_option;
63f7018c21STomi Valkeinen static const char *mode_option_pci = "640x480@60";
64f7018c21STomi Valkeinen static const char *mode_option_tc = "1280x1024@72";
65f7018c21STomi Valkeinen 
66f7018c21STomi Valkeinen 
67f7018c21STomi Valkeinen static struct pci_driver tgafb_pci_driver;
68f7018c21STomi Valkeinen static struct tc_driver tgafb_tc_driver;
69f7018c21STomi Valkeinen 
70f7018c21STomi Valkeinen /*
71f7018c21STomi Valkeinen  *  Frame buffer operations
72f7018c21STomi Valkeinen  */
73f7018c21STomi Valkeinen 
748a48ac33SJani Nikula static const struct fb_ops tgafb_ops = {
75f7018c21STomi Valkeinen 	.owner			= THIS_MODULE,
76f7018c21STomi Valkeinen 	.fb_check_var		= tgafb_check_var,
77f7018c21STomi Valkeinen 	.fb_set_par		= tgafb_set_par,
78f7018c21STomi Valkeinen 	.fb_setcolreg		= tgafb_setcolreg,
79f7018c21STomi Valkeinen 	.fb_blank		= tgafb_blank,
80f7018c21STomi Valkeinen 	.fb_pan_display		= tgafb_pan_display,
81f7018c21STomi Valkeinen 	.fb_fillrect		= tgafb_fillrect,
82f7018c21STomi Valkeinen 	.fb_copyarea		= tgafb_copyarea,
83f7018c21STomi Valkeinen 	.fb_imageblit		= tgafb_imageblit,
84f7018c21STomi Valkeinen };
85f7018c21STomi Valkeinen 
86f7018c21STomi Valkeinen 
87f7018c21STomi Valkeinen #ifdef CONFIG_PCI
88f7018c21STomi Valkeinen /*
89f7018c21STomi Valkeinen  *  PCI registration operations
90f7018c21STomi Valkeinen  */
91f7018c21STomi Valkeinen static int tgafb_pci_register(struct pci_dev *, const struct pci_device_id *);
92f7018c21STomi Valkeinen static void tgafb_pci_unregister(struct pci_dev *);
93f7018c21STomi Valkeinen 
94f7018c21STomi Valkeinen static struct pci_device_id const tgafb_pci_table[] = {
95f7018c21STomi Valkeinen 	{ PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
96f7018c21STomi Valkeinen 	{ }
97f7018c21STomi Valkeinen };
98f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(pci, tgafb_pci_table);
99f7018c21STomi Valkeinen 
100f7018c21STomi Valkeinen static struct pci_driver tgafb_pci_driver = {
101f7018c21STomi Valkeinen 	.name			= "tgafb",
102f7018c21STomi Valkeinen 	.id_table		= tgafb_pci_table,
103f7018c21STomi Valkeinen 	.probe			= tgafb_pci_register,
104f7018c21STomi Valkeinen 	.remove			= tgafb_pci_unregister,
105f7018c21STomi Valkeinen };
106f7018c21STomi Valkeinen 
tgafb_pci_register(struct pci_dev * pdev,const struct pci_device_id * ent)107f7018c21STomi Valkeinen static int tgafb_pci_register(struct pci_dev *pdev,
108f7018c21STomi Valkeinen 			      const struct pci_device_id *ent)
109f7018c21STomi Valkeinen {
110145eed48SThomas Zimmermann 	int ret;
111145eed48SThomas Zimmermann 
112145eed48SThomas Zimmermann 	ret = aperture_remove_conflicting_pci_devices(pdev, "tgafb");
113145eed48SThomas Zimmermann 	if (ret)
114145eed48SThomas Zimmermann 		return ret;
115145eed48SThomas Zimmermann 
116f7018c21STomi Valkeinen 	return tgafb_register(&pdev->dev);
117f7018c21STomi Valkeinen }
118f7018c21STomi Valkeinen 
tgafb_pci_unregister(struct pci_dev * pdev)119f7018c21STomi Valkeinen static void tgafb_pci_unregister(struct pci_dev *pdev)
120f7018c21STomi Valkeinen {
121f7018c21STomi Valkeinen 	tgafb_unregister(&pdev->dev);
122f7018c21STomi Valkeinen }
123f7018c21STomi Valkeinen #endif /* CONFIG_PCI */
124f7018c21STomi Valkeinen 
125f7018c21STomi Valkeinen #ifdef CONFIG_TC
126f7018c21STomi Valkeinen /*
127f7018c21STomi Valkeinen  *  TC registration operations
128f7018c21STomi Valkeinen  */
129f7018c21STomi Valkeinen static int tgafb_tc_register(struct device *);
130f7018c21STomi Valkeinen static int tgafb_tc_unregister(struct device *);
131f7018c21STomi Valkeinen 
132f7018c21STomi Valkeinen static struct tc_device_id const tgafb_tc_table[] = {
133f7018c21STomi Valkeinen 	{ "DEC     ", "PMAGD-AA" },
134f7018c21STomi Valkeinen 	{ "DEC     ", "PMAGD   " },
135f7018c21STomi Valkeinen 	{ }
136f7018c21STomi Valkeinen };
137f7018c21STomi Valkeinen MODULE_DEVICE_TABLE(tc, tgafb_tc_table);
138f7018c21STomi Valkeinen 
139f7018c21STomi Valkeinen static struct tc_driver tgafb_tc_driver = {
140f7018c21STomi Valkeinen 	.id_table		= tgafb_tc_table,
141f7018c21STomi Valkeinen 	.driver			= {
142f7018c21STomi Valkeinen 		.name		= "tgafb",
143f7018c21STomi Valkeinen 		.bus		= &tc_bus_type,
144f7018c21STomi Valkeinen 		.probe		= tgafb_tc_register,
145f7018c21STomi Valkeinen 		.remove		= tgafb_tc_unregister,
146f7018c21STomi Valkeinen 	},
147f7018c21STomi Valkeinen };
148f7018c21STomi Valkeinen 
tgafb_tc_register(struct device * dev)149f7018c21STomi Valkeinen static int tgafb_tc_register(struct device *dev)
150f7018c21STomi Valkeinen {
151f7018c21STomi Valkeinen 	int status = tgafb_register(dev);
152f7018c21STomi Valkeinen 	if (!status)
153f7018c21STomi Valkeinen 		get_device(dev);
154f7018c21STomi Valkeinen 	return status;
155f7018c21STomi Valkeinen }
156f7018c21STomi Valkeinen 
tgafb_tc_unregister(struct device * dev)157f7018c21STomi Valkeinen static int tgafb_tc_unregister(struct device *dev)
158f7018c21STomi Valkeinen {
159f7018c21STomi Valkeinen 	put_device(dev);
160f7018c21STomi Valkeinen 	tgafb_unregister(dev);
161f7018c21STomi Valkeinen 	return 0;
162f7018c21STomi Valkeinen }
163f7018c21STomi Valkeinen #endif /* CONFIG_TC */
164f7018c21STomi Valkeinen 
165f7018c21STomi Valkeinen 
166f7018c21STomi Valkeinen /**
167f7018c21STomi Valkeinen  *      tgafb_check_var - Optional function.  Validates a var passed in.
168f7018c21STomi Valkeinen  *      @var: frame buffer variable screen structure
169f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
170f7018c21STomi Valkeinen  */
171f7018c21STomi Valkeinen static int
tgafb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)172f7018c21STomi Valkeinen tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
173f7018c21STomi Valkeinen {
174f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *)info->par;
175f7018c21STomi Valkeinen 
176f90bd245SWei Chen 	if (!var->pixclock)
177f90bd245SWei Chen 		return -EINVAL;
178f90bd245SWei Chen 
179f7018c21STomi Valkeinen 	if (par->tga_type == TGA_TYPE_8PLANE) {
180f7018c21STomi Valkeinen 		if (var->bits_per_pixel != 8)
181f7018c21STomi Valkeinen 			return -EINVAL;
182f7018c21STomi Valkeinen 	} else {
183f7018c21STomi Valkeinen 		if (var->bits_per_pixel != 32)
184f7018c21STomi Valkeinen 			return -EINVAL;
185f7018c21STomi Valkeinen 	}
186f7018c21STomi Valkeinen 	var->red.length = var->green.length = var->blue.length = 8;
187f7018c21STomi Valkeinen 	if (var->bits_per_pixel == 32) {
188f7018c21STomi Valkeinen 		var->red.offset = 16;
189f7018c21STomi Valkeinen 		var->green.offset = 8;
190f7018c21STomi Valkeinen 		var->blue.offset = 0;
191f7018c21STomi Valkeinen 	}
192f7018c21STomi Valkeinen 
193f7018c21STomi Valkeinen 	if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
194f7018c21STomi Valkeinen 		return -EINVAL;
195f7018c21STomi Valkeinen 	if (var->xres * var->yres * (var->bits_per_pixel >> 3) > info->fix.smem_len)
196f7018c21STomi Valkeinen 		return -EINVAL;
197f7018c21STomi Valkeinen 	if (var->nonstd)
198f7018c21STomi Valkeinen 		return -EINVAL;
199f7018c21STomi Valkeinen 	if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
200f7018c21STomi Valkeinen 		return -EINVAL;
201f7018c21STomi Valkeinen 	if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
202f7018c21STomi Valkeinen 		return -EINVAL;
203f7018c21STomi Valkeinen 
204f7018c21STomi Valkeinen 	/* Some of the acceleration routines assume the line width is
205f7018c21STomi Valkeinen 	   a multiple of 8 bytes.  */
206f7018c21STomi Valkeinen 	if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 8)
207f7018c21STomi Valkeinen 		return -EINVAL;
208f7018c21STomi Valkeinen 
209f7018c21STomi Valkeinen 	return 0;
210f7018c21STomi Valkeinen }
211f7018c21STomi Valkeinen 
212f7018c21STomi Valkeinen /**
213f7018c21STomi Valkeinen  *      tgafb_set_par - Optional function.  Alters the hardware state.
214f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
215f7018c21STomi Valkeinen  */
216f7018c21STomi Valkeinen static int
tgafb_set_par(struct fb_info * info)217f7018c21STomi Valkeinen tgafb_set_par(struct fb_info *info)
218f7018c21STomi Valkeinen {
219f7018c21STomi Valkeinen 	static unsigned int const deep_presets[4] = {
220f7018c21STomi Valkeinen 		0x00004000,
221f7018c21STomi Valkeinen 		0x0000440d,
222f7018c21STomi Valkeinen 		0xffffffff,
223f7018c21STomi Valkeinen 		0x0000441d
224f7018c21STomi Valkeinen 	};
225f7018c21STomi Valkeinen 	static unsigned int const rasterop_presets[4] = {
226f7018c21STomi Valkeinen 		0x00000003,
227f7018c21STomi Valkeinen 		0x00000303,
228f7018c21STomi Valkeinen 		0xffffffff,
229f7018c21STomi Valkeinen 		0x00000303
230f7018c21STomi Valkeinen 	};
231f7018c21STomi Valkeinen 	static unsigned int const mode_presets[4] = {
232f7018c21STomi Valkeinen 		0x00000000,
233f7018c21STomi Valkeinen 		0x00000300,
234f7018c21STomi Valkeinen 		0xffffffff,
235f7018c21STomi Valkeinen 		0x00000300
236f7018c21STomi Valkeinen 	};
237f7018c21STomi Valkeinen 	static unsigned int const base_addr_presets[4] = {
238f7018c21STomi Valkeinen 		0x00000000,
239f7018c21STomi Valkeinen 		0x00000001,
240f7018c21STomi Valkeinen 		0xffffffff,
241f7018c21STomi Valkeinen 		0x00000001
242f7018c21STomi Valkeinen 	};
243f7018c21STomi Valkeinen 
244f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
245f7018c21STomi Valkeinen 	int tga_bus_pci = dev_is_pci(par->dev);
246f7018c21STomi Valkeinen 	int tga_bus_tc = TGA_BUS_TC(par->dev);
247f7018c21STomi Valkeinen 	u32 htimings, vtimings, pll_freq;
248f7018c21STomi Valkeinen 	u8 tga_type;
249f7018c21STomi Valkeinen 	int i;
250f7018c21STomi Valkeinen 
251f7018c21STomi Valkeinen 	/* Encode video timings.  */
252f7018c21STomi Valkeinen 	htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB)
253f7018c21STomi Valkeinen 		    | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB));
254f7018c21STomi Valkeinen 	vtimings = (info->var.yres & TGA_VERT_ACTIVE);
255f7018c21STomi Valkeinen 	htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP;
256f7018c21STomi Valkeinen 	vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP;
257f7018c21STomi Valkeinen 	htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC;
258f7018c21STomi Valkeinen 	vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC;
259f7018c21STomi Valkeinen 	htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP;
260f7018c21STomi Valkeinen 	vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP;
261f7018c21STomi Valkeinen 
262f7018c21STomi Valkeinen 	if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
263f7018c21STomi Valkeinen 		htimings |= TGA_HORIZ_POLARITY;
264f7018c21STomi Valkeinen 	if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
265f7018c21STomi Valkeinen 		vtimings |= TGA_VERT_POLARITY;
266f7018c21STomi Valkeinen 
267f7018c21STomi Valkeinen 	par->htimings = htimings;
268f7018c21STomi Valkeinen 	par->vtimings = vtimings;
269f7018c21STomi Valkeinen 
270f7018c21STomi Valkeinen 	par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN);
271f7018c21STomi Valkeinen 
272f7018c21STomi Valkeinen 	/* Store other useful values in par.  */
273f7018c21STomi Valkeinen 	par->xres = info->var.xres;
274f7018c21STomi Valkeinen 	par->yres = info->var.yres;
275f7018c21STomi Valkeinen 	par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
276f7018c21STomi Valkeinen 	par->bits_per_pixel = info->var.bits_per_pixel;
277f7018c21STomi Valkeinen 	info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
278f7018c21STomi Valkeinen 
279f7018c21STomi Valkeinen 	tga_type = par->tga_type;
280f7018c21STomi Valkeinen 
281f7018c21STomi Valkeinen 	/* First, disable video.  */
282f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG);
283f7018c21STomi Valkeinen 
284f7018c21STomi Valkeinen 	/* Write the DEEP register.  */
285f7018c21STomi Valkeinen 	while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
286f7018c21STomi Valkeinen 		continue;
287f7018c21STomi Valkeinen 	mb();
288f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, deep_presets[tga_type] |
289f7018c21STomi Valkeinen 			   (par->sync_on_green ? 0x0 : 0x00010000),
290f7018c21STomi Valkeinen 		      TGA_DEEP_REG);
291f7018c21STomi Valkeinen 	while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
292f7018c21STomi Valkeinen 		continue;
293f7018c21STomi Valkeinen 	mb();
294f7018c21STomi Valkeinen 
295f7018c21STomi Valkeinen 	/* Write some more registers.  */
296f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG);
297f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG);
298f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
299f7018c21STomi Valkeinen 
300f7018c21STomi Valkeinen 	/* Calculate & write the PLL.  */
301f7018c21STomi Valkeinen 	tgafb_set_pll(par, pll_freq);
302f7018c21STomi Valkeinen 
303f7018c21STomi Valkeinen 	/* Write some more registers.  */
304f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG);
305f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG);
306f7018c21STomi Valkeinen 
307f7018c21STomi Valkeinen 	/* Init video timing regs.  */
308f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG);
309f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
310f7018c21STomi Valkeinen 
311f7018c21STomi Valkeinen 	/* Initialise RAMDAC. */
312f7018c21STomi Valkeinen 	if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
313f7018c21STomi Valkeinen 
314f7018c21STomi Valkeinen 		/* Init BT485 RAMDAC registers.  */
315f7018c21STomi Valkeinen 		BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
316f7018c21STomi Valkeinen 			    BT485_CMD_0);
317f7018c21STomi Valkeinen 		BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE);
318f7018c21STomi Valkeinen 		BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */
319f7018c21STomi Valkeinen 		BT485_WRITE(par, 0x40, BT485_CMD_1);
320f7018c21STomi Valkeinen 		BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */
321f7018c21STomi Valkeinen 		BT485_WRITE(par, 0xff, BT485_PIXEL_MASK);
322f7018c21STomi Valkeinen 
323f7018c21STomi Valkeinen 		/* Fill palette registers.  */
324f7018c21STomi Valkeinen 		BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE);
325f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
326f7018c21STomi Valkeinen 
327f7018c21STomi Valkeinen 		for (i = 0; i < 256 * 3; i += 4) {
328f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8),
329f7018c21STomi Valkeinen 				      TGA_RAMDAC_REG);
330f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
331f7018c21STomi Valkeinen 				      TGA_RAMDAC_REG);
332f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
333f7018c21STomi Valkeinen 				      TGA_RAMDAC_REG);
334f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
335f7018c21STomi Valkeinen 				      TGA_RAMDAC_REG);
336f7018c21STomi Valkeinen 		}
337f7018c21STomi Valkeinen 
338f7018c21STomi Valkeinen 	} else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
339f7018c21STomi Valkeinen 
340f7018c21STomi Valkeinen 		/* Init BT459 RAMDAC registers.  */
341f7018c21STomi Valkeinen 		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
342f7018c21STomi Valkeinen 		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
343f7018c21STomi Valkeinen 		BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
344f7018c21STomi Valkeinen 			    (par->sync_on_green ? 0xc0 : 0x40));
345f7018c21STomi Valkeinen 
346f7018c21STomi Valkeinen 		BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
347f7018c21STomi Valkeinen 
348f7018c21STomi Valkeinen 		/* Fill the palette.  */
349f7018c21STomi Valkeinen 		BT459_LOAD_ADDR(par, 0x0000);
350f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
351f7018c21STomi Valkeinen 
352f7018c21STomi Valkeinen 		for (i = 0; i < 256 * 3; i += 4) {
353f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
354f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
355f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
356f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
357f7018c21STomi Valkeinen 		}
358f7018c21STomi Valkeinen 
359f7018c21STomi Valkeinen 	} else { /* 24-plane or 24plusZ */
360f7018c21STomi Valkeinen 
361f7018c21STomi Valkeinen 		/* Init BT463 RAMDAC registers.  */
362f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
363f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
364f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2,
365f7018c21STomi Valkeinen 			    (par->sync_on_green ? 0xc0 : 0x40));
366f7018c21STomi Valkeinen 
367f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
368f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
369f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
370f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
371f7018c21STomi Valkeinen 
372f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
373f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
374f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
375f7018c21STomi Valkeinen 		BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
376f7018c21STomi Valkeinen 
377f7018c21STomi Valkeinen 		/* Fill the palette.  */
378f7018c21STomi Valkeinen 		BT463_LOAD_ADDR(par, 0x0000);
379f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
380f7018c21STomi Valkeinen 
381f7018c21STomi Valkeinen #ifdef CONFIG_HW_CONSOLE
382f7018c21STomi Valkeinen 		for (i = 0; i < 16; i++) {
383f7018c21STomi Valkeinen 			int j = color_table[i];
384f7018c21STomi Valkeinen 
385f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
386f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
387f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
388f7018c21STomi Valkeinen 		}
389f7018c21STomi Valkeinen 		for (i = 0; i < 512 * 3; i += 4) {
390f7018c21STomi Valkeinen #else
391f7018c21STomi Valkeinen 		for (i = 0; i < 528 * 3; i += 4) {
392f7018c21STomi Valkeinen #endif
393f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
394f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
395f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
396f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
397f7018c21STomi Valkeinen 		}
398f7018c21STomi Valkeinen 
399f7018c21STomi Valkeinen 		/* Fill window type table after start of vertical retrace.  */
400f7018c21STomi Valkeinen 		while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
401f7018c21STomi Valkeinen 			continue;
402f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
403f7018c21STomi Valkeinen 		mb();
404f7018c21STomi Valkeinen 		while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
405f7018c21STomi Valkeinen 			continue;
406f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
407f7018c21STomi Valkeinen 
408f7018c21STomi Valkeinen 		BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE);
409f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG);
410f7018c21STomi Valkeinen 
411f7018c21STomi Valkeinen 		for (i = 0; i < 16; i++) {
412f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
413f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG);
414f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
415f7018c21STomi Valkeinen 		}
416f7018c21STomi Valkeinen 
417f7018c21STomi Valkeinen 	}
418f7018c21STomi Valkeinen 
419f7018c21STomi Valkeinen 	/* Finally, enable video scan (and pray for the monitor... :-) */
420f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG);
421f7018c21STomi Valkeinen 
422f7018c21STomi Valkeinen 	return 0;
423f7018c21STomi Valkeinen }
424f7018c21STomi Valkeinen 
425f7018c21STomi Valkeinen #define DIFFCHECK(X)							  \
426f7018c21STomi Valkeinen do {									  \
427f7018c21STomi Valkeinen 	if (m <= 0x3f) {						  \
428f7018c21STomi Valkeinen 		int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \
429f7018c21STomi Valkeinen 		if (delta < 0)						  \
430f7018c21STomi Valkeinen 			delta = -delta;					  \
431f7018c21STomi Valkeinen 		if (delta < min_diff)					  \
432f7018c21STomi Valkeinen 			min_diff = delta, vm = m, va = a, vr = r;	  \
433f7018c21STomi Valkeinen 	}								  \
434f7018c21STomi Valkeinen } while (0)
435f7018c21STomi Valkeinen 
436f7018c21STomi Valkeinen static void
437f7018c21STomi Valkeinen tgafb_set_pll(struct tga_par *par, int f)
438f7018c21STomi Valkeinen {
439f7018c21STomi Valkeinen 	int n, shift, base, min_diff, target;
440f7018c21STomi Valkeinen 	int r,a,m,vm = 34, va = 1, vr = 30;
441f7018c21STomi Valkeinen 
442f7018c21STomi Valkeinen 	for (r = 0 ; r < 12 ; r++)
443f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, !r, TGA_CLOCK_REG);
444f7018c21STomi Valkeinen 
445f7018c21STomi Valkeinen 	if (f > TGA_PLL_MAX_FREQ)
446f7018c21STomi Valkeinen 		f = TGA_PLL_MAX_FREQ;
447f7018c21STomi Valkeinen 
448f7018c21STomi Valkeinen 	if (f >= TGA_PLL_MAX_FREQ / 2)
449f7018c21STomi Valkeinen 		shift = 0;
450f7018c21STomi Valkeinen 	else if (f >= TGA_PLL_MAX_FREQ / 4)
451f7018c21STomi Valkeinen 		shift = 1;
452f7018c21STomi Valkeinen 	else
453f7018c21STomi Valkeinen 		shift = 2;
454f7018c21STomi Valkeinen 
455f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG);
456f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG);
457f7018c21STomi Valkeinen 
458f7018c21STomi Valkeinen 	for (r = 0 ; r < 10 ; r++)
459f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
460f7018c21STomi Valkeinen 
461f7018c21STomi Valkeinen 	if (f <= 120000) {
462f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
463f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
464f7018c21STomi Valkeinen 	}
465f7018c21STomi Valkeinen 	else if (f <= 200000) {
466f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
467f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
468f7018c21STomi Valkeinen 	}
469f7018c21STomi Valkeinen 	else {
470f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
471f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
472f7018c21STomi Valkeinen 	}
473f7018c21STomi Valkeinen 
474f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
475f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
476f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
477f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
478f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
479f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
480f7018c21STomi Valkeinen 
481f7018c21STomi Valkeinen 	target = (f << shift) / TGA_PLL_BASE_FREQ;
482f7018c21STomi Valkeinen 	min_diff = TGA_PLL_MAX_FREQ;
483f7018c21STomi Valkeinen 
484f7018c21STomi Valkeinen 	r = 7 / target;
485f7018c21STomi Valkeinen 	if (!r) r = 1;
486f7018c21STomi Valkeinen 
487f7018c21STomi Valkeinen 	base = target * r;
488f7018c21STomi Valkeinen 	while (base < 449) {
489f7018c21STomi Valkeinen 		for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) {
490f7018c21STomi Valkeinen 			m = ((n + 3) / 7) - 1;
491f7018c21STomi Valkeinen 			a = 0;
492f7018c21STomi Valkeinen 			DIFFCHECK((m + 1) * 7);
493f7018c21STomi Valkeinen 			m++;
494f7018c21STomi Valkeinen 			DIFFCHECK((m + 1) * 7);
495f7018c21STomi Valkeinen 			m = (n / 6) - 1;
496f7018c21STomi Valkeinen 			if ((a = n % 6))
497f7018c21STomi Valkeinen 				DIFFCHECK(n);
498f7018c21STomi Valkeinen 		}
499f7018c21STomi Valkeinen 		r++;
500f7018c21STomi Valkeinen 		base += target;
501f7018c21STomi Valkeinen 	}
502f7018c21STomi Valkeinen 
503f7018c21STomi Valkeinen 	vr--;
504f7018c21STomi Valkeinen 
505f7018c21STomi Valkeinen 	for (r = 0; r < 8; r++)
506f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG);
507f7018c21STomi Valkeinen 	for (r = 0; r < 8 ; r++)
508f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG);
509f7018c21STomi Valkeinen 	for (r = 0; r < 7 ; r++)
510f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG);
511f7018c21STomi Valkeinen 	TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG);
512f7018c21STomi Valkeinen }
513f7018c21STomi Valkeinen 
514f7018c21STomi Valkeinen 
515f7018c21STomi Valkeinen /**
516f7018c21STomi Valkeinen  *      tgafb_setcolreg - Optional function. Sets a color register.
517f7018c21STomi Valkeinen  *      @regno: boolean, 0 copy local, 1 get_user() function
518f7018c21STomi Valkeinen  *      @red: frame buffer colormap structure
519f7018c21STomi Valkeinen  *      @green: The green value which can be up to 16 bits wide
520f7018c21STomi Valkeinen  *      @blue:  The blue value which can be up to 16 bits wide.
521f7018c21STomi Valkeinen  *      @transp: If supported the alpha value which can be up to 16 bits wide.
522f7018c21STomi Valkeinen  *      @info: frame buffer info structure
523f7018c21STomi Valkeinen  */
524f7018c21STomi Valkeinen static int
525f7018c21STomi Valkeinen tgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
526f7018c21STomi Valkeinen 		unsigned transp, struct fb_info *info)
527f7018c21STomi Valkeinen {
528f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
529f7018c21STomi Valkeinen 	int tga_bus_pci = dev_is_pci(par->dev);
530f7018c21STomi Valkeinen 	int tga_bus_tc = TGA_BUS_TC(par->dev);
531f7018c21STomi Valkeinen 
532f7018c21STomi Valkeinen 	if (regno > 255)
533f7018c21STomi Valkeinen 		return 1;
534f7018c21STomi Valkeinen 	red >>= 8;
535f7018c21STomi Valkeinen 	green >>= 8;
536f7018c21STomi Valkeinen 	blue >>= 8;
537f7018c21STomi Valkeinen 
538f7018c21STomi Valkeinen 	if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
539f7018c21STomi Valkeinen 		BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
540f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
541f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
542f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
543f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
544f7018c21STomi Valkeinen 	} else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
545f7018c21STomi Valkeinen 		BT459_LOAD_ADDR(par, regno);
546f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
547f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
548f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
549f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
550f7018c21STomi Valkeinen 	} else {
551f7018c21STomi Valkeinen 		if (regno < 16) {
552f7018c21STomi Valkeinen 			u32 value = (regno << 16) | (regno << 8) | regno;
553f7018c21STomi Valkeinen 			((u32 *)info->pseudo_palette)[regno] = value;
554f7018c21STomi Valkeinen 		}
555f7018c21STomi Valkeinen 		BT463_LOAD_ADDR(par, regno);
556f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
557f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
558f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
559f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
560f7018c21STomi Valkeinen 	}
561f7018c21STomi Valkeinen 
562f7018c21STomi Valkeinen 	return 0;
563f7018c21STomi Valkeinen }
564f7018c21STomi Valkeinen 
565f7018c21STomi Valkeinen 
566f7018c21STomi Valkeinen /**
567f7018c21STomi Valkeinen  *      tgafb_blank - Optional function.  Blanks the display.
5682f71315eSSam Ravnborg  *      @blank: the blank mode we want.
569f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
570f7018c21STomi Valkeinen  */
571f7018c21STomi Valkeinen static int
572f7018c21STomi Valkeinen tgafb_blank(int blank, struct fb_info *info)
573f7018c21STomi Valkeinen {
574f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
575f7018c21STomi Valkeinen 	u32 vhcr, vvcr, vvvr;
576f7018c21STomi Valkeinen 	unsigned long flags;
577f7018c21STomi Valkeinen 
578f7018c21STomi Valkeinen 	local_irq_save(flags);
579f7018c21STomi Valkeinen 
580f7018c21STomi Valkeinen 	vhcr = TGA_READ_REG(par, TGA_HORIZ_REG);
581f7018c21STomi Valkeinen 	vvcr = TGA_READ_REG(par, TGA_VERT_REG);
582f7018c21STomi Valkeinen 	vvvr = TGA_READ_REG(par, TGA_VALID_REG);
583f7018c21STomi Valkeinen 	vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK);
584f7018c21STomi Valkeinen 
585f7018c21STomi Valkeinen 	switch (blank) {
586f7018c21STomi Valkeinen 	case FB_BLANK_UNBLANK: /* Unblanking */
587f7018c21STomi Valkeinen 		if (par->vesa_blanked) {
588f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG);
589f7018c21STomi Valkeinen 			TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG);
590f7018c21STomi Valkeinen 			par->vesa_blanked = 0;
591f7018c21STomi Valkeinen 		}
592f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG);
593f7018c21STomi Valkeinen 		break;
594f7018c21STomi Valkeinen 
595f7018c21STomi Valkeinen 	case FB_BLANK_NORMAL: /* Normal blanking */
596f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK,
597f7018c21STomi Valkeinen 			      TGA_VALID_REG);
598f7018c21STomi Valkeinen 		break;
599f7018c21STomi Valkeinen 
600f7018c21STomi Valkeinen 	case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
601f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
602f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
603f7018c21STomi Valkeinen 		par->vesa_blanked = 1;
604f7018c21STomi Valkeinen 		break;
605f7018c21STomi Valkeinen 
606f7018c21STomi Valkeinen 	case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
607f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
608f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
609f7018c21STomi Valkeinen 		par->vesa_blanked = 1;
610f7018c21STomi Valkeinen 		break;
611f7018c21STomi Valkeinen 
612f7018c21STomi Valkeinen 	case FB_BLANK_POWERDOWN: /* Poweroff */
613f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
614f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
615f7018c21STomi Valkeinen 		TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
616f7018c21STomi Valkeinen 		par->vesa_blanked = 1;
617f7018c21STomi Valkeinen 		break;
618f7018c21STomi Valkeinen 	}
619f7018c21STomi Valkeinen 
620f7018c21STomi Valkeinen 	local_irq_restore(flags);
621f7018c21STomi Valkeinen 	return 0;
622f7018c21STomi Valkeinen }
623f7018c21STomi Valkeinen 
624f7018c21STomi Valkeinen 
625f7018c21STomi Valkeinen /*
626f7018c21STomi Valkeinen  *  Acceleration.
627f7018c21STomi Valkeinen  */
628f7018c21STomi Valkeinen 
629f7018c21STomi Valkeinen static void
630f7018c21STomi Valkeinen tgafb_mono_imageblit(struct fb_info *info, const struct fb_image *image)
631f7018c21STomi Valkeinen {
632f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
633f7018c21STomi Valkeinen 	u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask;
634f7018c21STomi Valkeinen 	unsigned long rincr, line_length, shift, pos, is8bpp;
635f7018c21STomi Valkeinen 	unsigned long i, j;
636f7018c21STomi Valkeinen 	const unsigned char *data;
637f7018c21STomi Valkeinen 	void __iomem *regs_base;
638f7018c21STomi Valkeinen 	void __iomem *fb_base;
639f7018c21STomi Valkeinen 
640f7018c21STomi Valkeinen 	is8bpp = info->var.bits_per_pixel == 8;
641f7018c21STomi Valkeinen 
642f7018c21STomi Valkeinen 	dx = image->dx;
643f7018c21STomi Valkeinen 	dy = image->dy;
644f7018c21STomi Valkeinen 	width = image->width;
645f7018c21STomi Valkeinen 	height = image->height;
646f7018c21STomi Valkeinen 	vxres = info->var.xres_virtual;
647f7018c21STomi Valkeinen 	vyres = info->var.yres_virtual;
648f7018c21STomi Valkeinen 	line_length = info->fix.line_length;
649f7018c21STomi Valkeinen 	rincr = (width + 7) / 8;
650f7018c21STomi Valkeinen 
651f7018c21STomi Valkeinen 	/* A shift below cannot cope with.  */
652f7018c21STomi Valkeinen 	if (unlikely(width == 0))
653f7018c21STomi Valkeinen 		return;
654f7018c21STomi Valkeinen 	/* Crop the image to the screen.  */
655f7018c21STomi Valkeinen 	if (dx > vxres || dy > vyres)
656f7018c21STomi Valkeinen 		return;
657f7018c21STomi Valkeinen 	if (dx + width > vxres)
658f7018c21STomi Valkeinen 		width = vxres - dx;
659f7018c21STomi Valkeinen 	if (dy + height > vyres)
660f7018c21STomi Valkeinen 		height = vyres - dy;
661f7018c21STomi Valkeinen 
662f7018c21STomi Valkeinen 	regs_base = par->tga_regs_base;
663f7018c21STomi Valkeinen 	fb_base = par->tga_fb_base;
664f7018c21STomi Valkeinen 
665f7018c21STomi Valkeinen 	/* Expand the color values to fill 32-bits.  */
666f7018c21STomi Valkeinen 	/* ??? Would be nice to notice colour changes elsewhere, so
667f7018c21STomi Valkeinen 	   that we can do this only when necessary.  */
668f7018c21STomi Valkeinen 	fgcolor = image->fg_color;
669f7018c21STomi Valkeinen 	bgcolor = image->bg_color;
670f7018c21STomi Valkeinen 	if (is8bpp) {
671f7018c21STomi Valkeinen 		fgcolor |= fgcolor << 8;
672f7018c21STomi Valkeinen 		fgcolor |= fgcolor << 16;
673f7018c21STomi Valkeinen 		bgcolor |= bgcolor << 8;
674f7018c21STomi Valkeinen 		bgcolor |= bgcolor << 16;
675f7018c21STomi Valkeinen 	} else {
676f7018c21STomi Valkeinen 		if (fgcolor < 16)
677f7018c21STomi Valkeinen 			fgcolor = ((u32 *)info->pseudo_palette)[fgcolor];
678f7018c21STomi Valkeinen 		if (bgcolor < 16)
679f7018c21STomi Valkeinen 			bgcolor = ((u32 *)info->pseudo_palette)[bgcolor];
680f7018c21STomi Valkeinen 	}
681f7018c21STomi Valkeinen 	__raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG);
682f7018c21STomi Valkeinen 	__raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG);
683f7018c21STomi Valkeinen 
684f7018c21STomi Valkeinen 	/* Acquire proper alignment; set up the PIXELMASK register
685f7018c21STomi Valkeinen 	   so that we only write the proper character cell.  */
686f7018c21STomi Valkeinen 	pos = dy * line_length;
687f7018c21STomi Valkeinen 	if (is8bpp) {
688f7018c21STomi Valkeinen 		pos += dx;
689f7018c21STomi Valkeinen 		shift = pos & 3;
690f7018c21STomi Valkeinen 		pos &= -4;
691f7018c21STomi Valkeinen 	} else {
692f7018c21STomi Valkeinen 		pos += dx * 4;
693f7018c21STomi Valkeinen 		shift = (pos & 7) >> 2;
694f7018c21STomi Valkeinen 		pos &= -8;
695f7018c21STomi Valkeinen 	}
696f7018c21STomi Valkeinen 
697f7018c21STomi Valkeinen 	data = (const unsigned char *) image->data;
698f7018c21STomi Valkeinen 
699f7018c21STomi Valkeinen 	/* Enable opaque stipple mode.  */
700f7018c21STomi Valkeinen 	__raw_writel((is8bpp
701f7018c21STomi Valkeinen 		      ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE
702f7018c21STomi Valkeinen 		      : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE),
703f7018c21STomi Valkeinen 		     regs_base + TGA_MODE_REG);
704f7018c21STomi Valkeinen 
705f7018c21STomi Valkeinen 	if (width + shift <= 32) {
706f7018c21STomi Valkeinen 		unsigned long bwidth;
707f7018c21STomi Valkeinen 
708f7018c21STomi Valkeinen 		/* Handle common case of imaging a single character, in
709f7018c21STomi Valkeinen 		   a font less than or 32 pixels wide.  */
710f7018c21STomi Valkeinen 
711f7018c21STomi Valkeinen 		/* Avoid a shift by 32; width > 0 implied.  */
712f7018c21STomi Valkeinen 		pixelmask = (2ul << (width - 1)) - 1;
713f7018c21STomi Valkeinen 		pixelmask <<= shift;
714f7018c21STomi Valkeinen 		__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
715f7018c21STomi Valkeinen 		wmb();
716f7018c21STomi Valkeinen 
717f7018c21STomi Valkeinen 		bwidth = (width + 7) / 8;
718f7018c21STomi Valkeinen 
719f7018c21STomi Valkeinen 		for (i = 0; i < height; ++i) {
720f7018c21STomi Valkeinen 			u32 mask = 0;
721f7018c21STomi Valkeinen 
722f7018c21STomi Valkeinen 			/* The image data is bit big endian; we need
723f7018c21STomi Valkeinen 			   little endian.  */
724f7018c21STomi Valkeinen 			for (j = 0; j < bwidth; ++j)
725f7018c21STomi Valkeinen 				mask |= bitrev8(data[j]) << (j * 8);
726f7018c21STomi Valkeinen 
727f7018c21STomi Valkeinen 			__raw_writel(mask << shift, fb_base + pos);
728f7018c21STomi Valkeinen 
729f7018c21STomi Valkeinen 			pos += line_length;
730f7018c21STomi Valkeinen 			data += rincr;
731f7018c21STomi Valkeinen 		}
732f7018c21STomi Valkeinen 		wmb();
733f7018c21STomi Valkeinen 		__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
734f7018c21STomi Valkeinen 	} else if (shift == 0) {
735f7018c21STomi Valkeinen 		unsigned long pos0 = pos;
736f7018c21STomi Valkeinen 		const unsigned char *data0 = data;
737f7018c21STomi Valkeinen 		unsigned long bincr = (is8bpp ? 8 : 8*4);
738f7018c21STomi Valkeinen 		unsigned long bwidth;
739f7018c21STomi Valkeinen 
740f7018c21STomi Valkeinen 		/* Handle another common case in which accel_putcs
741f7018c21STomi Valkeinen 		   generates a large bitmap, which happens to be aligned.
742f7018c21STomi Valkeinen 		   Allow the tail to be misaligned.  This case is
743f7018c21STomi Valkeinen 		   interesting because we've not got to hold partial
744f7018c21STomi Valkeinen 		   bytes across the words being written.  */
745f7018c21STomi Valkeinen 
746f7018c21STomi Valkeinen 		wmb();
747f7018c21STomi Valkeinen 
748f7018c21STomi Valkeinen 		bwidth = (width / 8) & -4;
749f7018c21STomi Valkeinen 		for (i = 0; i < height; ++i) {
750f7018c21STomi Valkeinen 			for (j = 0; j < bwidth; j += 4) {
751f7018c21STomi Valkeinen 				u32 mask = 0;
752f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+0]) << (0 * 8);
753f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+1]) << (1 * 8);
754f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+2]) << (2 * 8);
755f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+3]) << (3 * 8);
756f7018c21STomi Valkeinen 				__raw_writel(mask, fb_base + pos + j*bincr);
757f7018c21STomi Valkeinen 			}
758f7018c21STomi Valkeinen 			pos += line_length;
759f7018c21STomi Valkeinen 			data += rincr;
760f7018c21STomi Valkeinen 		}
761f7018c21STomi Valkeinen 		wmb();
762f7018c21STomi Valkeinen 
763f7018c21STomi Valkeinen 		pixelmask = (1ul << (width & 31)) - 1;
764f7018c21STomi Valkeinen 		if (pixelmask) {
765f7018c21STomi Valkeinen 			__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
766f7018c21STomi Valkeinen 			wmb();
767f7018c21STomi Valkeinen 
768f7018c21STomi Valkeinen 			pos = pos0 + bwidth*bincr;
769f7018c21STomi Valkeinen 			data = data0 + bwidth;
770f7018c21STomi Valkeinen 			bwidth = ((width & 31) + 7) / 8;
771f7018c21STomi Valkeinen 
772f7018c21STomi Valkeinen 			for (i = 0; i < height; ++i) {
773f7018c21STomi Valkeinen 				u32 mask = 0;
774f7018c21STomi Valkeinen 				for (j = 0; j < bwidth; ++j)
775f7018c21STomi Valkeinen 					mask |= bitrev8(data[j]) << (j * 8);
776f7018c21STomi Valkeinen 				__raw_writel(mask, fb_base + pos);
777f7018c21STomi Valkeinen 				pos += line_length;
778f7018c21STomi Valkeinen 				data += rincr;
779f7018c21STomi Valkeinen 			}
780f7018c21STomi Valkeinen 			wmb();
781f7018c21STomi Valkeinen 			__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
782f7018c21STomi Valkeinen 		}
783f7018c21STomi Valkeinen 	} else {
784f7018c21STomi Valkeinen 		unsigned long pos0 = pos;
785f7018c21STomi Valkeinen 		const unsigned char *data0 = data;
786f7018c21STomi Valkeinen 		unsigned long bincr = (is8bpp ? 8 : 8*4);
787f7018c21STomi Valkeinen 		unsigned long bwidth;
788f7018c21STomi Valkeinen 
789f7018c21STomi Valkeinen 		/* Finally, handle the generic case of misaligned start.
790f7018c21STomi Valkeinen 		   Here we split the write into 16-bit spans.  This allows
791f7018c21STomi Valkeinen 		   us to use only one pixel mask, instead of four as would
792f7018c21STomi Valkeinen 		   be required by writing 24-bit spans.  */
793f7018c21STomi Valkeinen 
794f7018c21STomi Valkeinen 		pixelmask = 0xffff << shift;
795f7018c21STomi Valkeinen 		__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
796f7018c21STomi Valkeinen 		wmb();
797f7018c21STomi Valkeinen 
798f7018c21STomi Valkeinen 		bwidth = (width / 8) & -2;
799f7018c21STomi Valkeinen 		for (i = 0; i < height; ++i) {
800f7018c21STomi Valkeinen 			for (j = 0; j < bwidth; j += 2) {
801f7018c21STomi Valkeinen 				u32 mask = 0;
802f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+0]) << (0 * 8);
803f7018c21STomi Valkeinen 				mask |= bitrev8(data[j+1]) << (1 * 8);
804f7018c21STomi Valkeinen 				mask <<= shift;
805f7018c21STomi Valkeinen 				__raw_writel(mask, fb_base + pos + j*bincr);
806f7018c21STomi Valkeinen 			}
807f7018c21STomi Valkeinen 			pos += line_length;
808f7018c21STomi Valkeinen 			data += rincr;
809f7018c21STomi Valkeinen 		}
810f7018c21STomi Valkeinen 		wmb();
811f7018c21STomi Valkeinen 
812f7018c21STomi Valkeinen 		pixelmask = ((1ul << (width & 15)) - 1) << shift;
813f7018c21STomi Valkeinen 		if (pixelmask) {
814f7018c21STomi Valkeinen 			__raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
815f7018c21STomi Valkeinen 			wmb();
816f7018c21STomi Valkeinen 
817f7018c21STomi Valkeinen 			pos = pos0 + bwidth*bincr;
818f7018c21STomi Valkeinen 			data = data0 + bwidth;
819f7018c21STomi Valkeinen 			bwidth = (width & 15) > 8;
820f7018c21STomi Valkeinen 
821f7018c21STomi Valkeinen 			for (i = 0; i < height; ++i) {
822f7018c21STomi Valkeinen 				u32 mask = bitrev8(data[0]);
823f7018c21STomi Valkeinen 				if (bwidth)
824f7018c21STomi Valkeinen 					mask |= bitrev8(data[1]) << 8;
825f7018c21STomi Valkeinen 				mask <<= shift;
826f7018c21STomi Valkeinen 				__raw_writel(mask, fb_base + pos);
827f7018c21STomi Valkeinen 				pos += line_length;
828f7018c21STomi Valkeinen 				data += rincr;
829f7018c21STomi Valkeinen 			}
830f7018c21STomi Valkeinen 			wmb();
831f7018c21STomi Valkeinen 		}
832f7018c21STomi Valkeinen 		__raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
833f7018c21STomi Valkeinen 	}
834f7018c21STomi Valkeinen 
835f7018c21STomi Valkeinen 	/* Disable opaque stipple mode.  */
836f7018c21STomi Valkeinen 	__raw_writel((is8bpp
837f7018c21STomi Valkeinen 		      ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
838f7018c21STomi Valkeinen 		      : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
839f7018c21STomi Valkeinen 		     regs_base + TGA_MODE_REG);
840f7018c21STomi Valkeinen }
841f7018c21STomi Valkeinen 
842f7018c21STomi Valkeinen static void
843f7018c21STomi Valkeinen tgafb_clut_imageblit(struct fb_info *info, const struct fb_image *image)
844f7018c21STomi Valkeinen {
845f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
846f7018c21STomi Valkeinen 	u32 color, dx, dy, width, height, vxres, vyres;
847f7018c21STomi Valkeinen 	u32 *palette = ((u32 *)info->pseudo_palette);
848f7018c21STomi Valkeinen 	unsigned long pos, line_length, i, j;
849f7018c21STomi Valkeinen 	const unsigned char *data;
8502f71315eSSam Ravnborg 	void __iomem *fb_base;
851f7018c21STomi Valkeinen 
852f7018c21STomi Valkeinen 	dx = image->dx;
853f7018c21STomi Valkeinen 	dy = image->dy;
854f7018c21STomi Valkeinen 	width = image->width;
855f7018c21STomi Valkeinen 	height = image->height;
856f7018c21STomi Valkeinen 	vxres = info->var.xres_virtual;
857f7018c21STomi Valkeinen 	vyres = info->var.yres_virtual;
858f7018c21STomi Valkeinen 	line_length = info->fix.line_length;
859f7018c21STomi Valkeinen 
860f7018c21STomi Valkeinen 	/* Crop the image to the screen.  */
861f7018c21STomi Valkeinen 	if (dx > vxres || dy > vyres)
862f7018c21STomi Valkeinen 		return;
863f7018c21STomi Valkeinen 	if (dx + width > vxres)
864f7018c21STomi Valkeinen 		width = vxres - dx;
865f7018c21STomi Valkeinen 	if (dy + height > vyres)
866f7018c21STomi Valkeinen 		height = vyres - dy;
867f7018c21STomi Valkeinen 
868f7018c21STomi Valkeinen 	fb_base = par->tga_fb_base;
869f7018c21STomi Valkeinen 
870f7018c21STomi Valkeinen 	pos = dy * line_length + (dx * 4);
871f7018c21STomi Valkeinen 	data = image->data;
872f7018c21STomi Valkeinen 
873f7018c21STomi Valkeinen 	/* Now copy the image, color_expanding via the palette. */
874f7018c21STomi Valkeinen 	for (i = 0; i < height; i++) {
875f7018c21STomi Valkeinen 		for (j = 0; j < width; j++) {
876f7018c21STomi Valkeinen 			color = palette[*data++];
877f7018c21STomi Valkeinen 			__raw_writel(color, fb_base + pos + j*4);
878f7018c21STomi Valkeinen 		}
879f7018c21STomi Valkeinen 		pos += line_length;
880f7018c21STomi Valkeinen 	}
881f7018c21STomi Valkeinen }
882f7018c21STomi Valkeinen 
883f7018c21STomi Valkeinen /**
884f7018c21STomi Valkeinen  *      tgafb_imageblit - REQUIRED function. Can use generic routines if
885f7018c21STomi Valkeinen  *                        non acclerated hardware and packed pixel based.
886f7018c21STomi Valkeinen  *                        Copies a image from system memory to the screen.
887f7018c21STomi Valkeinen  *
888f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
889f7018c21STomi Valkeinen  *      @image: structure defining the image.
890f7018c21STomi Valkeinen  */
891f7018c21STomi Valkeinen static void
892f7018c21STomi Valkeinen tgafb_imageblit(struct fb_info *info, const struct fb_image *image)
893f7018c21STomi Valkeinen {
894f7018c21STomi Valkeinen 	unsigned int is8bpp = info->var.bits_per_pixel == 8;
895f7018c21STomi Valkeinen 
896f7018c21STomi Valkeinen 	/* If a mono image, regardless of FB depth, go do it. */
897f7018c21STomi Valkeinen 	if (image->depth == 1) {
898f7018c21STomi Valkeinen 		tgafb_mono_imageblit(info, image);
899f7018c21STomi Valkeinen 		return;
900f7018c21STomi Valkeinen 	}
901f7018c21STomi Valkeinen 
902f7018c21STomi Valkeinen 	/* For copies that aren't pixel expansion, there's little we
903f7018c21STomi Valkeinen 	   can do better than the generic code.  */
904f7018c21STomi Valkeinen 	/* ??? There is a DMA write mode; I wonder if that could be
905f7018c21STomi Valkeinen 	   made to pull the data from the image buffer...  */
906f7018c21STomi Valkeinen 	if (image->depth == info->var.bits_per_pixel) {
907f7018c21STomi Valkeinen 		cfb_imageblit(info, image);
908f7018c21STomi Valkeinen 		return;
909f7018c21STomi Valkeinen 	}
910f7018c21STomi Valkeinen 
911f7018c21STomi Valkeinen 	/* If 24-plane FB and the image is 8-plane with CLUT, we can do it. */
912f7018c21STomi Valkeinen 	if (!is8bpp && image->depth == 8) {
913f7018c21STomi Valkeinen 		tgafb_clut_imageblit(info, image);
914f7018c21STomi Valkeinen 		return;
915f7018c21STomi Valkeinen 	}
916f7018c21STomi Valkeinen 
917f7018c21STomi Valkeinen 	/* Silently return... */
918f7018c21STomi Valkeinen }
919f7018c21STomi Valkeinen 
920f7018c21STomi Valkeinen /**
921f7018c21STomi Valkeinen  *      tgafb_fillrect - REQUIRED function. Can use generic routines if
922f7018c21STomi Valkeinen  *                       non acclerated hardware and packed pixel based.
923f7018c21STomi Valkeinen  *                       Draws a rectangle on the screen.
924f7018c21STomi Valkeinen  *
925f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
926f7018c21STomi Valkeinen  *      @rect: structure defining the rectagle and operation.
927f7018c21STomi Valkeinen  */
928f7018c21STomi Valkeinen static void
929f7018c21STomi Valkeinen tgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
930f7018c21STomi Valkeinen {
931f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
932f7018c21STomi Valkeinen 	int is8bpp = info->var.bits_per_pixel == 8;
933f7018c21STomi Valkeinen 	u32 dx, dy, width, height, vxres, vyres, color;
934f7018c21STomi Valkeinen 	unsigned long pos, align, line_length, i, j;
935f7018c21STomi Valkeinen 	void __iomem *regs_base;
936f7018c21STomi Valkeinen 	void __iomem *fb_base;
937f7018c21STomi Valkeinen 
938f7018c21STomi Valkeinen 	dx = rect->dx;
939f7018c21STomi Valkeinen 	dy = rect->dy;
940f7018c21STomi Valkeinen 	width = rect->width;
941f7018c21STomi Valkeinen 	height = rect->height;
942f7018c21STomi Valkeinen 	vxres = info->var.xres_virtual;
943f7018c21STomi Valkeinen 	vyres = info->var.yres_virtual;
944f7018c21STomi Valkeinen 	line_length = info->fix.line_length;
945f7018c21STomi Valkeinen 	regs_base = par->tga_regs_base;
946f7018c21STomi Valkeinen 	fb_base = par->tga_fb_base;
947f7018c21STomi Valkeinen 
948f7018c21STomi Valkeinen 	/* Crop the rectangle to the screen.  */
949f7018c21STomi Valkeinen 	if (dx > vxres || dy > vyres || !width || !height)
950f7018c21STomi Valkeinen 		return;
951f7018c21STomi Valkeinen 	if (dx + width > vxres)
952f7018c21STomi Valkeinen 		width = vxres - dx;
953f7018c21STomi Valkeinen 	if (dy + height > vyres)
954f7018c21STomi Valkeinen 		height = vyres - dy;
955f7018c21STomi Valkeinen 
956f7018c21STomi Valkeinen 	pos = dy * line_length + dx * (is8bpp ? 1 : 4);
957f7018c21STomi Valkeinen 
958f7018c21STomi Valkeinen 	/* ??? We could implement ROP_XOR with opaque fill mode
959f7018c21STomi Valkeinen 	   and a RasterOp setting of GXxor, but as far as I can
960f7018c21STomi Valkeinen 	   tell, this mode is not actually used in the kernel.
961f7018c21STomi Valkeinen 	   Thus I am ignoring it for now.  */
962f7018c21STomi Valkeinen 	if (rect->rop != ROP_COPY) {
963f7018c21STomi Valkeinen 		cfb_fillrect(info, rect);
964f7018c21STomi Valkeinen 		return;
965f7018c21STomi Valkeinen 	}
966f7018c21STomi Valkeinen 
967f7018c21STomi Valkeinen 	/* Expand the color value to fill 8 pixels.  */
968f7018c21STomi Valkeinen 	color = rect->color;
969f7018c21STomi Valkeinen 	if (is8bpp) {
970f7018c21STomi Valkeinen 		color |= color << 8;
971f7018c21STomi Valkeinen 		color |= color << 16;
972f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
973f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
974f7018c21STomi Valkeinen 	} else {
975f7018c21STomi Valkeinen 		if (color < 16)
976f7018c21STomi Valkeinen 			color = ((u32 *)info->pseudo_palette)[color];
977f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
978f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
979f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG);
980f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG);
981f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG);
982f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG);
983f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG);
984f7018c21STomi Valkeinen 		__raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG);
985f7018c21STomi Valkeinen 	}
986f7018c21STomi Valkeinen 
987f7018c21STomi Valkeinen 	/* The DATA register holds the fill mask for block fill mode.
988f7018c21STomi Valkeinen 	   Since we're not stippling, this is all ones.  */
989f7018c21STomi Valkeinen 	__raw_writel(0xffffffff, regs_base + TGA_DATA_REG);
990f7018c21STomi Valkeinen 
991f7018c21STomi Valkeinen 	/* Enable block fill mode.  */
992f7018c21STomi Valkeinen 	__raw_writel((is8bpp
993f7018c21STomi Valkeinen 		      ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL
994f7018c21STomi Valkeinen 		      : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL),
995f7018c21STomi Valkeinen 		     regs_base + TGA_MODE_REG);
996f7018c21STomi Valkeinen 	wmb();
997f7018c21STomi Valkeinen 
998f7018c21STomi Valkeinen 	/* We can fill 2k pixels per operation.  Notice blocks that fit
999f7018c21STomi Valkeinen 	   the width of the screen so that we can take advantage of this
1000f7018c21STomi Valkeinen 	   and fill more than one line per write.  */
100192589648SJoe Perches 	if (width == line_length) {
100292589648SJoe Perches 		width *= height;
100392589648SJoe Perches 		height = 1;
100492589648SJoe Perches 	}
1005f7018c21STomi Valkeinen 
1006f7018c21STomi Valkeinen 	/* The write into the frame buffer must be aligned to 4 bytes,
1007f7018c21STomi Valkeinen 	   but we are allowed to encode the offset within the word in
1008f7018c21STomi Valkeinen 	   the data word written.  */
1009f7018c21STomi Valkeinen 	align = (pos & 3) << 16;
1010f7018c21STomi Valkeinen 	pos &= -4;
1011f7018c21STomi Valkeinen 
1012f7018c21STomi Valkeinen 	if (width <= 2048) {
1013f7018c21STomi Valkeinen 		u32 data;
1014f7018c21STomi Valkeinen 
1015f7018c21STomi Valkeinen 		data = (width - 1) | align;
1016f7018c21STomi Valkeinen 
1017f7018c21STomi Valkeinen 		for (i = 0; i < height; ++i) {
1018f7018c21STomi Valkeinen 			__raw_writel(data, fb_base + pos);
1019f7018c21STomi Valkeinen 			pos += line_length;
1020f7018c21STomi Valkeinen 		}
1021f7018c21STomi Valkeinen 	} else {
1022f7018c21STomi Valkeinen 		unsigned long Bpp = (is8bpp ? 1 : 4);
1023f7018c21STomi Valkeinen 		unsigned long nwidth = width & -2048;
1024f7018c21STomi Valkeinen 		u32 fdata, ldata;
1025f7018c21STomi Valkeinen 
1026f7018c21STomi Valkeinen 		fdata = (2048 - 1) | align;
1027f7018c21STomi Valkeinen 		ldata = ((width & 2047) - 1) | align;
1028f7018c21STomi Valkeinen 
1029f7018c21STomi Valkeinen 		for (i = 0; i < height; ++i) {
1030f7018c21STomi Valkeinen 			for (j = 0; j < nwidth; j += 2048)
1031f7018c21STomi Valkeinen 				__raw_writel(fdata, fb_base + pos + j*Bpp);
1032f7018c21STomi Valkeinen 			if (j < width)
1033f7018c21STomi Valkeinen 				__raw_writel(ldata, fb_base + pos + j*Bpp);
1034f7018c21STomi Valkeinen 			pos += line_length;
1035f7018c21STomi Valkeinen 		}
1036f7018c21STomi Valkeinen 	}
1037f7018c21STomi Valkeinen 	wmb();
1038f7018c21STomi Valkeinen 
1039f7018c21STomi Valkeinen 	/* Disable block fill mode.  */
1040f7018c21STomi Valkeinen 	__raw_writel((is8bpp
1041f7018c21STomi Valkeinen 		      ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
1042f7018c21STomi Valkeinen 		      : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
1043f7018c21STomi Valkeinen 		     regs_base + TGA_MODE_REG);
1044f7018c21STomi Valkeinen }
1045f7018c21STomi Valkeinen 
10462f71315eSSam Ravnborg /*
1047f7018c21STomi Valkeinen  *      tgafb_copyarea - REQUIRED function. Can use generic routines if
1048f7018c21STomi Valkeinen  *                       non acclerated hardware and packed pixel based.
1049f7018c21STomi Valkeinen  *                       Copies on area of the screen to another area.
1050f7018c21STomi Valkeinen  *
1051f7018c21STomi Valkeinen  *      @info: frame buffer structure that represents a single frame buffer
1052f7018c21STomi Valkeinen  *      @area: structure defining the source and destination.
1053f7018c21STomi Valkeinen  */
1054f7018c21STomi Valkeinen 
1055f7018c21STomi Valkeinen /* Handle the special case of copying entire lines, e.g. during scrolling.
1056f7018c21STomi Valkeinen    We can avoid a lot of needless computation in this case.  In the 8bpp
1057f7018c21STomi Valkeinen    case we need to use the COPY64 registers instead of mask writes into
1058f7018c21STomi Valkeinen    the frame buffer to achieve maximum performance.  */
1059f7018c21STomi Valkeinen 
1060f7018c21STomi Valkeinen static inline void
1061f7018c21STomi Valkeinen copyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy,
1062f7018c21STomi Valkeinen 		   u32 height, u32 width)
1063f7018c21STomi Valkeinen {
1064f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
1065f7018c21STomi Valkeinen 	void __iomem *tga_regs = par->tga_regs_base;
1066f7018c21STomi Valkeinen 	unsigned long dpos, spos, i, n64;
1067f7018c21STomi Valkeinen 
1068f7018c21STomi Valkeinen 	/* Set up the MODE and PIXELSHIFT registers.  */
1069f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1070f7018c21STomi Valkeinen 	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1071f7018c21STomi Valkeinen 	wmb();
1072f7018c21STomi Valkeinen 
1073f7018c21STomi Valkeinen 	n64 = (height * width) / 64;
1074f7018c21STomi Valkeinen 
1075f7018c21STomi Valkeinen 	if (sy < dy) {
1076f7018c21STomi Valkeinen 		spos = (sy + height) * width;
1077f7018c21STomi Valkeinen 		dpos = (dy + height) * width;
1078f7018c21STomi Valkeinen 
1079f7018c21STomi Valkeinen 		for (i = 0; i < n64; ++i) {
1080f7018c21STomi Valkeinen 			spos -= 64;
1081f7018c21STomi Valkeinen 			dpos -= 64;
1082f7018c21STomi Valkeinen 			__raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1083f7018c21STomi Valkeinen 			wmb();
1084f7018c21STomi Valkeinen 			__raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1085f7018c21STomi Valkeinen 			wmb();
1086f7018c21STomi Valkeinen 		}
1087f7018c21STomi Valkeinen 	} else {
1088f7018c21STomi Valkeinen 		spos = sy * width;
1089f7018c21STomi Valkeinen 		dpos = dy * width;
1090f7018c21STomi Valkeinen 
1091f7018c21STomi Valkeinen 		for (i = 0; i < n64; ++i) {
1092f7018c21STomi Valkeinen 			__raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1093f7018c21STomi Valkeinen 			wmb();
1094f7018c21STomi Valkeinen 			__raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1095f7018c21STomi Valkeinen 			wmb();
1096f7018c21STomi Valkeinen 			spos += 64;
1097f7018c21STomi Valkeinen 			dpos += 64;
1098f7018c21STomi Valkeinen 		}
1099f7018c21STomi Valkeinen 	}
1100f7018c21STomi Valkeinen 
1101f7018c21STomi Valkeinen 	/* Reset the MODE register to normal.  */
1102f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1103f7018c21STomi Valkeinen }
1104f7018c21STomi Valkeinen 
1105f7018c21STomi Valkeinen static inline void
1106f7018c21STomi Valkeinen copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
1107f7018c21STomi Valkeinen 		    u32 height, u32 width)
1108f7018c21STomi Valkeinen {
1109f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
1110f7018c21STomi Valkeinen 	void __iomem *tga_regs = par->tga_regs_base;
1111f7018c21STomi Valkeinen 	void __iomem *tga_fb = par->tga_fb_base;
1112f7018c21STomi Valkeinen 	void __iomem *src;
1113f7018c21STomi Valkeinen 	void __iomem *dst;
1114f7018c21STomi Valkeinen 	unsigned long i, n16;
1115f7018c21STomi Valkeinen 
1116f7018c21STomi Valkeinen 	/* Set up the MODE and PIXELSHIFT registers.  */
1117f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1118f7018c21STomi Valkeinen 	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1119f7018c21STomi Valkeinen 	wmb();
1120f7018c21STomi Valkeinen 
1121f7018c21STomi Valkeinen 	n16 = (height * width) / 16;
1122f7018c21STomi Valkeinen 
1123f7018c21STomi Valkeinen 	if (sy < dy) {
1124f7018c21STomi Valkeinen 		src = tga_fb + (sy + height) * width * 4;
1125f7018c21STomi Valkeinen 		dst = tga_fb + (dy + height) * width * 4;
1126f7018c21STomi Valkeinen 
1127f7018c21STomi Valkeinen 		for (i = 0; i < n16; ++i) {
1128f7018c21STomi Valkeinen 			src -= 64;
1129f7018c21STomi Valkeinen 			dst -= 64;
1130f7018c21STomi Valkeinen 			__raw_writel(0xffff, src);
1131f7018c21STomi Valkeinen 			wmb();
1132f7018c21STomi Valkeinen 			__raw_writel(0xffff, dst);
1133f7018c21STomi Valkeinen 			wmb();
1134f7018c21STomi Valkeinen 		}
1135f7018c21STomi Valkeinen 	} else {
1136f7018c21STomi Valkeinen 		src = tga_fb + sy * width * 4;
1137f7018c21STomi Valkeinen 		dst = tga_fb + dy * width * 4;
1138f7018c21STomi Valkeinen 
1139f7018c21STomi Valkeinen 		for (i = 0; i < n16; ++i) {
1140f7018c21STomi Valkeinen 			__raw_writel(0xffff, src);
1141f7018c21STomi Valkeinen 			wmb();
1142f7018c21STomi Valkeinen 			__raw_writel(0xffff, dst);
1143f7018c21STomi Valkeinen 			wmb();
1144f7018c21STomi Valkeinen 			src += 64;
1145f7018c21STomi Valkeinen 			dst += 64;
1146f7018c21STomi Valkeinen 		}
1147f7018c21STomi Valkeinen 	}
1148f7018c21STomi Valkeinen 
1149f7018c21STomi Valkeinen 	/* Reset the MODE register to normal.  */
1150f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1151f7018c21STomi Valkeinen }
1152f7018c21STomi Valkeinen 
1153f7018c21STomi Valkeinen /* The (almost) general case of backward copy in 8bpp mode.  */
1154f7018c21STomi Valkeinen static inline void
1155f7018c21STomi Valkeinen copyarea_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1156f7018c21STomi Valkeinen 	      u32 height, u32 width, u32 line_length,
1157f7018c21STomi Valkeinen 	      const struct fb_copyarea *area)
1158f7018c21STomi Valkeinen {
1159f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *) info->par;
1160f7018c21STomi Valkeinen 	unsigned i, yincr;
1161f7018c21STomi Valkeinen 	int depos, sepos, backward, last_step, step;
1162f7018c21STomi Valkeinen 	u32 mask_last;
1163f7018c21STomi Valkeinen 	unsigned n32;
1164f7018c21STomi Valkeinen 	void __iomem *tga_regs;
1165f7018c21STomi Valkeinen 	void __iomem *tga_fb;
1166f7018c21STomi Valkeinen 
1167f7018c21STomi Valkeinen 	/* Do acceleration only if we are aligned on 8 pixels */
1168f7018c21STomi Valkeinen 	if ((dx | sx | width) & 7) {
1169f7018c21STomi Valkeinen 		cfb_copyarea(info, area);
1170f7018c21STomi Valkeinen 		return;
1171f7018c21STomi Valkeinen 	}
1172f7018c21STomi Valkeinen 
1173f7018c21STomi Valkeinen 	yincr = line_length;
1174f7018c21STomi Valkeinen 	if (dy > sy) {
1175f7018c21STomi Valkeinen 		dy += height - 1;
1176f7018c21STomi Valkeinen 		sy += height - 1;
1177f7018c21STomi Valkeinen 		yincr = -yincr;
1178f7018c21STomi Valkeinen 	}
1179f7018c21STomi Valkeinen 	backward = dy == sy && dx > sx && dx < sx + width;
1180f7018c21STomi Valkeinen 
1181f7018c21STomi Valkeinen 	/* Compute the offsets and alignments in the frame buffer.
1182f7018c21STomi Valkeinen 	   More than anything else, these control how we do copies.  */
1183f7018c21STomi Valkeinen 	depos = dy * line_length + dx;
1184f7018c21STomi Valkeinen 	sepos = sy * line_length + sx;
118592589648SJoe Perches 	if (backward) {
118692589648SJoe Perches 		depos += width;
118792589648SJoe Perches 		sepos += width;
118892589648SJoe Perches 	}
1189f7018c21STomi Valkeinen 
1190f7018c21STomi Valkeinen 	/* Next copy full words at a time.  */
1191f7018c21STomi Valkeinen 	n32 = width / 32;
1192f7018c21STomi Valkeinen 	last_step = width % 32;
1193f7018c21STomi Valkeinen 
1194f7018c21STomi Valkeinen 	/* Finally copy the unaligned head of the span.  */
1195f7018c21STomi Valkeinen 	mask_last = (1ul << last_step) - 1;
1196f7018c21STomi Valkeinen 
1197f7018c21STomi Valkeinen 	if (!backward) {
1198f7018c21STomi Valkeinen 		step = 32;
1199f7018c21STomi Valkeinen 		last_step = 32;
1200f7018c21STomi Valkeinen 	} else {
1201f7018c21STomi Valkeinen 		step = -32;
1202f7018c21STomi Valkeinen 		last_step = -last_step;
1203f7018c21STomi Valkeinen 		sepos -= 32;
1204f7018c21STomi Valkeinen 		depos -= 32;
1205f7018c21STomi Valkeinen 	}
1206f7018c21STomi Valkeinen 
1207f7018c21STomi Valkeinen 	tga_regs = par->tga_regs_base;
1208f7018c21STomi Valkeinen 	tga_fb = par->tga_fb_base;
1209f7018c21STomi Valkeinen 
1210f7018c21STomi Valkeinen 	/* Set up the MODE and PIXELSHIFT registers.  */
1211f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1212f7018c21STomi Valkeinen 	__raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1213f7018c21STomi Valkeinen 	wmb();
1214f7018c21STomi Valkeinen 
1215f7018c21STomi Valkeinen 	for (i = 0; i < height; ++i) {
1216f7018c21STomi Valkeinen 		unsigned long j;
1217f7018c21STomi Valkeinen 		void __iomem *sfb;
1218f7018c21STomi Valkeinen 		void __iomem *dfb;
1219f7018c21STomi Valkeinen 
1220f7018c21STomi Valkeinen 		sfb = tga_fb + sepos;
1221f7018c21STomi Valkeinen 		dfb = tga_fb + depos;
1222f7018c21STomi Valkeinen 
1223f7018c21STomi Valkeinen 		for (j = 0; j < n32; j++) {
1224f7018c21STomi Valkeinen 			if (j < 2 && j + 1 < n32 && !backward &&
1225f7018c21STomi Valkeinen 			    !(((unsigned long)sfb | (unsigned long)dfb) & 63)) {
1226f7018c21STomi Valkeinen 				do {
1227f7018c21STomi Valkeinen 					__raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1228f7018c21STomi Valkeinen 					wmb();
1229f7018c21STomi Valkeinen 					__raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1230f7018c21STomi Valkeinen 					wmb();
1231f7018c21STomi Valkeinen 					sfb += 64;
1232f7018c21STomi Valkeinen 					dfb += 64;
1233f7018c21STomi Valkeinen 					j += 2;
1234f7018c21STomi Valkeinen 				} while (j + 1 < n32);
1235f7018c21STomi Valkeinen 				j--;
1236f7018c21STomi Valkeinen 				continue;
1237f7018c21STomi Valkeinen 			}
1238f7018c21STomi Valkeinen 			__raw_writel(0xffffffff, sfb);
1239f7018c21STomi Valkeinen 			wmb();
1240f7018c21STomi Valkeinen 			__raw_writel(0xffffffff, dfb);
1241f7018c21STomi Valkeinen 			wmb();
1242f7018c21STomi Valkeinen 			sfb += step;
1243f7018c21STomi Valkeinen 			dfb += step;
1244f7018c21STomi Valkeinen 		}
1245f7018c21STomi Valkeinen 
1246f7018c21STomi Valkeinen 		if (mask_last) {
1247f7018c21STomi Valkeinen 			sfb += last_step - step;
1248f7018c21STomi Valkeinen 			dfb += last_step - step;
1249f7018c21STomi Valkeinen 			__raw_writel(mask_last, sfb);
1250f7018c21STomi Valkeinen 			wmb();
1251f7018c21STomi Valkeinen 			__raw_writel(mask_last, dfb);
1252f7018c21STomi Valkeinen 			wmb();
1253f7018c21STomi Valkeinen 		}
1254f7018c21STomi Valkeinen 
1255f7018c21STomi Valkeinen 		sepos += yincr;
1256f7018c21STomi Valkeinen 		depos += yincr;
1257f7018c21STomi Valkeinen 	}
1258f7018c21STomi Valkeinen 
1259f7018c21STomi Valkeinen 	/* Reset the MODE register to normal.  */
1260f7018c21STomi Valkeinen 	__raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1261f7018c21STomi Valkeinen }
1262f7018c21STomi Valkeinen 
1263f7018c21STomi Valkeinen static void
1264f7018c21STomi Valkeinen tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
1265f7018c21STomi Valkeinen {
1266f7018c21STomi Valkeinen 	unsigned long dx, dy, width, height, sx, sy, vxres, vyres;
1267f7018c21STomi Valkeinen 	unsigned long line_length, bpp;
1268f7018c21STomi Valkeinen 
1269f7018c21STomi Valkeinen 	dx = area->dx;
1270f7018c21STomi Valkeinen 	dy = area->dy;
1271f7018c21STomi Valkeinen 	width = area->width;
1272f7018c21STomi Valkeinen 	height = area->height;
1273f7018c21STomi Valkeinen 	sx = area->sx;
1274f7018c21STomi Valkeinen 	sy = area->sy;
1275f7018c21STomi Valkeinen 	vxres = info->var.xres_virtual;
1276f7018c21STomi Valkeinen 	vyres = info->var.yres_virtual;
1277f7018c21STomi Valkeinen 	line_length = info->fix.line_length;
1278f7018c21STomi Valkeinen 
1279f7018c21STomi Valkeinen 	/* The top left corners must be in the virtual screen.  */
1280f7018c21STomi Valkeinen 	if (dx > vxres || sx > vxres || dy > vyres || sy > vyres)
1281f7018c21STomi Valkeinen 		return;
1282f7018c21STomi Valkeinen 
1283f7018c21STomi Valkeinen 	/* Clip the destination.  */
1284f7018c21STomi Valkeinen 	if (dx + width > vxres)
1285f7018c21STomi Valkeinen 		width = vxres - dx;
1286f7018c21STomi Valkeinen 	if (dy + height > vyres)
1287f7018c21STomi Valkeinen 		height = vyres - dy;
1288f7018c21STomi Valkeinen 
1289f7018c21STomi Valkeinen 	/* The source must be completely inside the virtual screen.  */
1290f7018c21STomi Valkeinen 	if (sx + width > vxres || sy + height > vyres)
1291f7018c21STomi Valkeinen 		return;
1292f7018c21STomi Valkeinen 
1293f7018c21STomi Valkeinen 	bpp = info->var.bits_per_pixel;
1294f7018c21STomi Valkeinen 
1295f7018c21STomi Valkeinen 	/* Detect copies of the entire line.  */
1296f7018c21STomi Valkeinen 	if (!(line_length & 63) && width * (bpp >> 3) == line_length) {
1297f7018c21STomi Valkeinen 		if (bpp == 8)
1298f7018c21STomi Valkeinen 			copyarea_line_8bpp(info, dy, sy, height, width);
1299f7018c21STomi Valkeinen 		else
1300f7018c21STomi Valkeinen 			copyarea_line_32bpp(info, dy, sy, height, width);
1301f7018c21STomi Valkeinen 	}
1302f7018c21STomi Valkeinen 
1303f7018c21STomi Valkeinen 	/* ??? The documentation is unclear to me exactly how the pixelshift
1304f7018c21STomi Valkeinen 	   register works in 32bpp mode.  Since I don't have hardware to test,
1305f7018c21STomi Valkeinen 	   give up for now and fall back on the generic routines.  */
1306f7018c21STomi Valkeinen 	else if (bpp == 32)
1307f7018c21STomi Valkeinen 		cfb_copyarea(info, area);
1308f7018c21STomi Valkeinen 
1309f7018c21STomi Valkeinen 	else
1310f7018c21STomi Valkeinen 		copyarea_8bpp(info, dx, dy, sx, sy, height,
1311f7018c21STomi Valkeinen 			      width, line_length, area);
1312f7018c21STomi Valkeinen }
1313f7018c21STomi Valkeinen 
1314f7018c21STomi Valkeinen 
1315f7018c21STomi Valkeinen /*
1316f7018c21STomi Valkeinen  *  Initialisation
1317f7018c21STomi Valkeinen  */
1318f7018c21STomi Valkeinen 
1319f7018c21STomi Valkeinen static void
1320f7018c21STomi Valkeinen tgafb_init_fix(struct fb_info *info)
1321f7018c21STomi Valkeinen {
1322f7018c21STomi Valkeinen 	struct tga_par *par = (struct tga_par *)info->par;
1323f7018c21STomi Valkeinen 	int tga_bus_pci = dev_is_pci(par->dev);
1324f7018c21STomi Valkeinen 	int tga_bus_tc = TGA_BUS_TC(par->dev);
1325f7018c21STomi Valkeinen 	u8 tga_type = par->tga_type;
1326f7018c21STomi Valkeinen 	const char *tga_type_name = NULL;
1327f7018c21STomi Valkeinen 	unsigned memory_size;
1328f7018c21STomi Valkeinen 
1329f7018c21STomi Valkeinen 	switch (tga_type) {
1330f7018c21STomi Valkeinen 	case TGA_TYPE_8PLANE:
1331f7018c21STomi Valkeinen 		if (tga_bus_pci)
1332f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLXp-E1";
1333f7018c21STomi Valkeinen 		if (tga_bus_tc)
1334f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLX-E1";
1335f7018c21STomi Valkeinen 		memory_size = 2097152;
1336f7018c21STomi Valkeinen 		break;
1337f7018c21STomi Valkeinen 	case TGA_TYPE_24PLANE:
1338f7018c21STomi Valkeinen 		if (tga_bus_pci)
1339f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLXp-E2";
1340f7018c21STomi Valkeinen 		if (tga_bus_tc)
1341f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLX-E2";
1342f7018c21STomi Valkeinen 		memory_size = 8388608;
1343f7018c21STomi Valkeinen 		break;
1344f7018c21STomi Valkeinen 	case TGA_TYPE_24PLUSZ:
1345f7018c21STomi Valkeinen 		if (tga_bus_pci)
1346f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLXp-E3";
1347f7018c21STomi Valkeinen 		if (tga_bus_tc)
1348f7018c21STomi Valkeinen 			tga_type_name = "Digital ZLX-E3";
1349f7018c21STomi Valkeinen 		memory_size = 16777216;
1350f7018c21STomi Valkeinen 		break;
1351f7018c21STomi Valkeinen 	}
1352f7018c21STomi Valkeinen 	if (!tga_type_name) {
1353f7018c21STomi Valkeinen 		tga_type_name = "Unknown";
1354f7018c21STomi Valkeinen 		memory_size = 16777216;
1355f7018c21STomi Valkeinen 	}
1356f7018c21STomi Valkeinen 
13578d026858SWolfram Sang 	strscpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
1358f7018c21STomi Valkeinen 
1359f7018c21STomi Valkeinen 	info->fix.type = FB_TYPE_PACKED_PIXELS;
1360f7018c21STomi Valkeinen 	info->fix.type_aux = 0;
1361f7018c21STomi Valkeinen 	info->fix.visual = (tga_type == TGA_TYPE_8PLANE
1362f7018c21STomi Valkeinen 			    ? FB_VISUAL_PSEUDOCOLOR
1363f7018c21STomi Valkeinen 			    : FB_VISUAL_DIRECTCOLOR);
1364f7018c21STomi Valkeinen 
1365f7018c21STomi Valkeinen 	info->fix.smem_start = (size_t) par->tga_fb_base;
1366f7018c21STomi Valkeinen 	info->fix.smem_len = memory_size;
1367f7018c21STomi Valkeinen 	info->fix.mmio_start = (size_t) par->tga_regs_base;
1368f7018c21STomi Valkeinen 	info->fix.mmio_len = 512;
1369f7018c21STomi Valkeinen 
1370f7018c21STomi Valkeinen 	info->fix.xpanstep = 0;
1371f7018c21STomi Valkeinen 	info->fix.ypanstep = 0;
1372f7018c21STomi Valkeinen 	info->fix.ywrapstep = 0;
1373f7018c21STomi Valkeinen 
1374f7018c21STomi Valkeinen 	info->fix.accel = FB_ACCEL_DEC_TGA;
1375f7018c21STomi Valkeinen 
1376f7018c21STomi Valkeinen 	/*
1377f7018c21STomi Valkeinen 	 * These are needed by fb_set_logo_truepalette(), so we
1378f7018c21STomi Valkeinen 	 * set them here for 24-plane cards.
1379f7018c21STomi Valkeinen 	 */
1380f7018c21STomi Valkeinen 	if (tga_type != TGA_TYPE_8PLANE) {
1381f7018c21STomi Valkeinen 		info->var.red.length = 8;
1382f7018c21STomi Valkeinen 		info->var.green.length = 8;
1383f7018c21STomi Valkeinen 		info->var.blue.length = 8;
1384f7018c21STomi Valkeinen 		info->var.red.offset = 16;
1385f7018c21STomi Valkeinen 		info->var.green.offset = 8;
1386f7018c21STomi Valkeinen 		info->var.blue.offset = 0;
1387f7018c21STomi Valkeinen 	}
1388f7018c21STomi Valkeinen }
1389f7018c21STomi Valkeinen 
1390f7018c21STomi Valkeinen static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
1391f7018c21STomi Valkeinen {
1392f7018c21STomi Valkeinen 	/* We just use this to catch switches out of graphics mode. */
1393f7018c21STomi Valkeinen 	tgafb_set_par(info); /* A bit of overkill for BASE_ADDR reset. */
1394f7018c21STomi Valkeinen 	return 0;
1395f7018c21STomi Valkeinen }
1396f7018c21STomi Valkeinen 
1397f7018c21STomi Valkeinen static int tgafb_register(struct device *dev)
1398f7018c21STomi Valkeinen {
1399f7018c21STomi Valkeinen 	static const struct fb_videomode modedb_tc = {
1400f7018c21STomi Valkeinen 		/* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
1401f7018c21STomi Valkeinen 		"1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
1402f7018c21STomi Valkeinen 		FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
1403f7018c21STomi Valkeinen 	};
1404f7018c21STomi Valkeinen 
1405f7018c21STomi Valkeinen 	static unsigned int const fb_offset_presets[4] = {
1406f7018c21STomi Valkeinen 		TGA_8PLANE_FB_OFFSET,
1407f7018c21STomi Valkeinen 		TGA_24PLANE_FB_OFFSET,
1408f7018c21STomi Valkeinen 		0xffffffff,
1409f7018c21STomi Valkeinen 		TGA_24PLUSZ_FB_OFFSET
1410f7018c21STomi Valkeinen 	};
1411f7018c21STomi Valkeinen 
1412f7018c21STomi Valkeinen 	const struct fb_videomode *modedb_tga = NULL;
1413f7018c21STomi Valkeinen 	resource_size_t bar0_start = 0, bar0_len = 0;
1414f7018c21STomi Valkeinen 	const char *mode_option_tga = NULL;
1415f7018c21STomi Valkeinen 	int tga_bus_pci = dev_is_pci(dev);
1416f7018c21STomi Valkeinen 	int tga_bus_tc = TGA_BUS_TC(dev);
1417f7018c21STomi Valkeinen 	unsigned int modedbsize_tga = 0;
1418f7018c21STomi Valkeinen 	void __iomem *mem_base;
1419f7018c21STomi Valkeinen 	struct fb_info *info;
1420f7018c21STomi Valkeinen 	struct tga_par *par;
1421f7018c21STomi Valkeinen 	u8 tga_type;
1422f7018c21STomi Valkeinen 	int ret = 0;
1423f7018c21STomi Valkeinen 
1424f7018c21STomi Valkeinen 	/* Enable device in PCI config.  */
1425f7018c21STomi Valkeinen 	if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
1426f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
1427f7018c21STomi Valkeinen 		return -ENODEV;
1428f7018c21STomi Valkeinen 	}
1429f7018c21STomi Valkeinen 
1430f7018c21STomi Valkeinen 	/* Allocate the fb and par structures.  */
1431f7018c21STomi Valkeinen 	info = framebuffer_alloc(sizeof(struct tga_par), dev);
14320adcdbcbSBartlomiej Zolnierkiewicz 	if (!info)
1433f7018c21STomi Valkeinen 		return -ENOMEM;
1434f7018c21STomi Valkeinen 
1435f7018c21STomi Valkeinen 	par = info->par;
1436f7018c21STomi Valkeinen 	dev_set_drvdata(dev, info);
1437f7018c21STomi Valkeinen 
1438f7018c21STomi Valkeinen 	/* Request the mem regions.  */
1439f7018c21STomi Valkeinen 	ret = -ENODEV;
1440f7018c21STomi Valkeinen 	if (tga_bus_pci) {
1441f7018c21STomi Valkeinen 		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1442f7018c21STomi Valkeinen 		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1443f7018c21STomi Valkeinen 	}
1444f7018c21STomi Valkeinen 	if (tga_bus_tc) {
1445f7018c21STomi Valkeinen 		bar0_start = to_tc_dev(dev)->resource.start;
1446f7018c21STomi Valkeinen 		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1447f7018c21STomi Valkeinen 	}
1448f7018c21STomi Valkeinen 	if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
1449f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: cannot reserve FB region\n");
1450f7018c21STomi Valkeinen 		goto err0;
1451f7018c21STomi Valkeinen 	}
1452f7018c21STomi Valkeinen 
1453f7018c21STomi Valkeinen 	/* Map the framebuffer.  */
14544bdc0d67SChristoph Hellwig 	mem_base = ioremap(bar0_start, bar0_len);
1455f7018c21STomi Valkeinen 	if (!mem_base) {
1456f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: Cannot map MMIO\n");
1457f7018c21STomi Valkeinen 		goto err1;
1458f7018c21STomi Valkeinen 	}
1459f7018c21STomi Valkeinen 
1460f7018c21STomi Valkeinen 	/* Grab info about the card.  */
1461f7018c21STomi Valkeinen 	tga_type = (readl(mem_base) >> 12) & 0x0f;
1462f7018c21STomi Valkeinen 	par->dev = dev;
1463f7018c21STomi Valkeinen 	par->tga_mem_base = mem_base;
1464f7018c21STomi Valkeinen 	par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
1465f7018c21STomi Valkeinen 	par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
1466f7018c21STomi Valkeinen 	par->tga_type = tga_type;
1467f7018c21STomi Valkeinen 	if (tga_bus_pci)
1468f7018c21STomi Valkeinen 		par->tga_chip_rev = (to_pci_dev(dev))->revision;
1469f7018c21STomi Valkeinen 	if (tga_bus_tc)
1470f7018c21STomi Valkeinen 		par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
1471f7018c21STomi Valkeinen 
1472f7018c21STomi Valkeinen 	/* Setup framebuffer.  */
1473*b3e148d7SThomas Zimmermann 	info->flags = FBINFO_HWACCEL_COPYAREA |
1474f7018c21STomi Valkeinen 		      FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT;
1475f7018c21STomi Valkeinen 	info->fbops = &tgafb_ops;
1476f7018c21STomi Valkeinen 	info->screen_base = par->tga_fb_base;
1477f7018c21STomi Valkeinen 	info->pseudo_palette = par->palette;
1478f7018c21STomi Valkeinen 
1479f7018c21STomi Valkeinen 	/* This should give a reasonable default video mode.  */
1480f7018c21STomi Valkeinen 	if (tga_bus_pci) {
1481f7018c21STomi Valkeinen 		mode_option_tga = mode_option_pci;
1482f7018c21STomi Valkeinen 	}
1483f7018c21STomi Valkeinen 	if (tga_bus_tc) {
1484f7018c21STomi Valkeinen 		mode_option_tga = mode_option_tc;
1485f7018c21STomi Valkeinen 		modedb_tga = &modedb_tc;
1486f7018c21STomi Valkeinen 		modedbsize_tga = 1;
1487f7018c21STomi Valkeinen 	}
1488f7018c21STomi Valkeinen 
1489f7018c21STomi Valkeinen 	tgafb_init_fix(info);
1490f7018c21STomi Valkeinen 
1491f7018c21STomi Valkeinen 	ret = fb_find_mode(&info->var, info,
1492f7018c21STomi Valkeinen 			   mode_option ? mode_option : mode_option_tga,
1493f7018c21STomi Valkeinen 			   modedb_tga, modedbsize_tga, NULL,
1494f7018c21STomi Valkeinen 			   tga_type == TGA_TYPE_8PLANE ? 8 : 32);
1495f7018c21STomi Valkeinen 	if (ret == 0 || ret == 4) {
1496f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: Could not find valid video mode\n");
1497f7018c21STomi Valkeinen 		ret = -EINVAL;
1498f7018c21STomi Valkeinen 		goto err1;
1499f7018c21STomi Valkeinen 	}
1500f7018c21STomi Valkeinen 
1501f7018c21STomi Valkeinen 	if (fb_alloc_cmap(&info->cmap, 256, 0)) {
1502f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: Could not allocate color map\n");
1503f7018c21STomi Valkeinen 		ret = -ENOMEM;
1504f7018c21STomi Valkeinen 		goto err1;
1505f7018c21STomi Valkeinen 	}
1506f7018c21STomi Valkeinen 
1507f7018c21STomi Valkeinen 	tgafb_set_par(info);
1508f7018c21STomi Valkeinen 
1509f7018c21STomi Valkeinen 	if (register_framebuffer(info) < 0) {
1510f7018c21STomi Valkeinen 		printk(KERN_ERR "tgafb: Could not register framebuffer\n");
1511f7018c21STomi Valkeinen 		ret = -EINVAL;
1512f7018c21STomi Valkeinen 		goto err2;
1513f7018c21STomi Valkeinen 	}
1514f7018c21STomi Valkeinen 
1515f7018c21STomi Valkeinen 	if (tga_bus_pci) {
1516f7018c21STomi Valkeinen 		pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
1517f7018c21STomi Valkeinen 			par->tga_chip_rev);
1518f7018c21STomi Valkeinen 		pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
1519f7018c21STomi Valkeinen 			to_pci_dev(dev)->bus->number,
1520f7018c21STomi Valkeinen 			PCI_SLOT(to_pci_dev(dev)->devfn),
1521f7018c21STomi Valkeinen 			PCI_FUNC(to_pci_dev(dev)->devfn));
1522f7018c21STomi Valkeinen 	}
1523f7018c21STomi Valkeinen 	if (tga_bus_tc)
1524f7018c21STomi Valkeinen 		pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
1525f7018c21STomi Valkeinen 			par->tga_chip_rev);
1526f7018c21STomi Valkeinen 	fb_info(info, "%s frame buffer device at 0x%lx\n",
1527f7018c21STomi Valkeinen 		info->fix.id, (long)bar0_start);
1528f7018c21STomi Valkeinen 
1529f7018c21STomi Valkeinen 	return 0;
1530f7018c21STomi Valkeinen 
1531f7018c21STomi Valkeinen  err2:
1532f7018c21STomi Valkeinen 	fb_dealloc_cmap(&info->cmap);
1533f7018c21STomi Valkeinen  err1:
1534f7018c21STomi Valkeinen 	if (mem_base)
1535f7018c21STomi Valkeinen 		iounmap(mem_base);
1536f7018c21STomi Valkeinen 	release_mem_region(bar0_start, bar0_len);
1537f7018c21STomi Valkeinen  err0:
1538f7018c21STomi Valkeinen 	framebuffer_release(info);
1539f7018c21STomi Valkeinen 	return ret;
1540f7018c21STomi Valkeinen }
1541f7018c21STomi Valkeinen 
1542f7018c21STomi Valkeinen static void tgafb_unregister(struct device *dev)
1543f7018c21STomi Valkeinen {
1544f7018c21STomi Valkeinen 	resource_size_t bar0_start = 0, bar0_len = 0;
1545f7018c21STomi Valkeinen 	int tga_bus_pci = dev_is_pci(dev);
1546f7018c21STomi Valkeinen 	int tga_bus_tc = TGA_BUS_TC(dev);
1547f7018c21STomi Valkeinen 	struct fb_info *info = NULL;
1548f7018c21STomi Valkeinen 	struct tga_par *par;
1549f7018c21STomi Valkeinen 
1550f7018c21STomi Valkeinen 	info = dev_get_drvdata(dev);
1551f7018c21STomi Valkeinen 	if (!info)
1552f7018c21STomi Valkeinen 		return;
1553f7018c21STomi Valkeinen 
1554f7018c21STomi Valkeinen 	par = info->par;
1555f7018c21STomi Valkeinen 	unregister_framebuffer(info);
1556f7018c21STomi Valkeinen 	fb_dealloc_cmap(&info->cmap);
1557f7018c21STomi Valkeinen 	iounmap(par->tga_mem_base);
1558f7018c21STomi Valkeinen 	if (tga_bus_pci) {
1559f7018c21STomi Valkeinen 		bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1560f7018c21STomi Valkeinen 		bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1561f7018c21STomi Valkeinen 	}
1562f7018c21STomi Valkeinen 	if (tga_bus_tc) {
1563f7018c21STomi Valkeinen 		bar0_start = to_tc_dev(dev)->resource.start;
1564f7018c21STomi Valkeinen 		bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1565f7018c21STomi Valkeinen 	}
1566f7018c21STomi Valkeinen 	release_mem_region(bar0_start, bar0_len);
1567f7018c21STomi Valkeinen 	framebuffer_release(info);
1568f7018c21STomi Valkeinen }
1569f7018c21STomi Valkeinen 
1570f7018c21STomi Valkeinen static void tgafb_exit(void)
1571f7018c21STomi Valkeinen {
1572f7018c21STomi Valkeinen 	tc_unregister_driver(&tgafb_tc_driver);
1573f7018c21STomi Valkeinen 	pci_unregister_driver(&tgafb_pci_driver);
1574f7018c21STomi Valkeinen }
1575f7018c21STomi Valkeinen 
1576f7018c21STomi Valkeinen #ifndef MODULE
1577f7018c21STomi Valkeinen static int tgafb_setup(char *arg)
1578f7018c21STomi Valkeinen {
1579f7018c21STomi Valkeinen 	char *this_opt;
1580f7018c21STomi Valkeinen 
1581f7018c21STomi Valkeinen 	if (arg && *arg) {
1582f7018c21STomi Valkeinen 		while ((this_opt = strsep(&arg, ","))) {
1583f7018c21STomi Valkeinen 			if (!*this_opt)
1584f7018c21STomi Valkeinen 				continue;
1585f7018c21STomi Valkeinen 			if (!strncmp(this_opt, "mode:", 5))
1586f7018c21STomi Valkeinen 				mode_option = this_opt+5;
1587f7018c21STomi Valkeinen 			else
1588f7018c21STomi Valkeinen 				printk(KERN_ERR
1589f7018c21STomi Valkeinen 				       "tgafb: unknown parameter %s\n",
1590f7018c21STomi Valkeinen 				       this_opt);
1591f7018c21STomi Valkeinen 		}
1592f7018c21STomi Valkeinen 	}
1593f7018c21STomi Valkeinen 
1594f7018c21STomi Valkeinen 	return 0;
1595f7018c21STomi Valkeinen }
1596f7018c21STomi Valkeinen #endif /* !MODULE */
1597f7018c21STomi Valkeinen 
1598f7018c21STomi Valkeinen static int tgafb_init(void)
1599f7018c21STomi Valkeinen {
1600f7018c21STomi Valkeinen 	int status;
1601f7018c21STomi Valkeinen #ifndef MODULE
1602f7018c21STomi Valkeinen 	char *option = NULL;
16030ba2fa8cSThomas Zimmermann #endif
1604f7018c21STomi Valkeinen 
16050ba2fa8cSThomas Zimmermann 	if (fb_modesetting_disabled("tgafb"))
16060ba2fa8cSThomas Zimmermann 		return -ENODEV;
16070ba2fa8cSThomas Zimmermann 
16080ba2fa8cSThomas Zimmermann #ifndef MODULE
1609f7018c21STomi Valkeinen 	if (fb_get_options("tgafb", &option))
1610f7018c21STomi Valkeinen 		return -ENODEV;
1611f7018c21STomi Valkeinen 	tgafb_setup(option);
1612f7018c21STomi Valkeinen #endif
1613f7018c21STomi Valkeinen 	status = pci_register_driver(&tgafb_pci_driver);
1614f7018c21STomi Valkeinen 	if (!status)
1615f7018c21STomi Valkeinen 		status = tc_register_driver(&tgafb_tc_driver);
1616f7018c21STomi Valkeinen 	return status;
1617f7018c21STomi Valkeinen }
1618f7018c21STomi Valkeinen 
1619f7018c21STomi Valkeinen /*
1620f7018c21STomi Valkeinen  *  Modularisation
1621f7018c21STomi Valkeinen  */
1622f7018c21STomi Valkeinen 
1623f7018c21STomi Valkeinen module_init(tgafb_init);
1624f7018c21STomi Valkeinen module_exit(tgafb_exit);
1625f7018c21STomi Valkeinen 
1626f7018c21STomi Valkeinen MODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
1627f7018c21STomi Valkeinen MODULE_LICENSE("GPL");
1628