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