blob: 5345fe03cdfe14f93b5f2e38aa619daea9e069a0 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device
3 *
4 * Copyright (C) 1995 Jay Estabrook
5 * Copyright (C) 1997 Geert Uytterhoeven
6 * Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha
7 * Copyright (C) 2002 Richard Henderson
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07008 * Copyright (C) 2006 Maciej W. Rozycki
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 *
10 * This file is subject to the terms and conditions of the GNU General Public
11 * License. See the file COPYING in the main directory of this archive for
12 * more details.
13 */
14
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070015#include <linux/bitrev.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/delay.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070017#include <linux/device.h>
18#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/fb.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070020#include <linux/init.h>
21#include <linux/ioport.h>
22#include <linux/kernel.h>
23#include <linux/mm.h>
24#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/pci.h>
26#include <linux/selection.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070027#include <linux/slab.h>
28#include <linux/string.h>
29#include <linux/tc.h>
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <asm/io.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070032
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <video/tgafb.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070035#ifdef CONFIG_PCI
36#define TGA_BUS_PCI(dev) (dev->bus == &pci_bus_type)
37#else
38#define TGA_BUS_PCI(dev) 0
39#endif
40
41#ifdef CONFIG_TC
42#define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type)
43#else
44#define TGA_BUS_TC(dev) 0
45#endif
46
Linus Torvalds1da177e2005-04-16 15:20:36 -070047/*
48 * Local functions.
49 */
50
51static int tgafb_check_var(struct fb_var_screeninfo *, struct fb_info *);
52static int tgafb_set_par(struct fb_info *);
53static void tgafb_set_pll(struct tga_par *, int);
54static int tgafb_setcolreg(unsigned, unsigned, unsigned, unsigned,
55 unsigned, struct fb_info *);
56static int tgafb_blank(int, struct fb_info *);
57static void tgafb_init_fix(struct fb_info *);
58
59static void tgafb_imageblit(struct fb_info *, const struct fb_image *);
60static void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *);
61static void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *);
62
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070063static int __devinit tgafb_register(struct device *dev);
64static void __devexit tgafb_unregister(struct device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070066static const char *mode_option;
67static const char *mode_option_pci = "640x480@60";
68static const char *mode_option_tc = "1280x1024@72";
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070071static struct pci_driver tgafb_pci_driver;
72static struct tc_driver tgafb_tc_driver;
73
Linus Torvalds1da177e2005-04-16 15:20:36 -070074/*
75 * Frame buffer operations
76 */
77
78static struct fb_ops tgafb_ops = {
79 .owner = THIS_MODULE,
80 .fb_check_var = tgafb_check_var,
81 .fb_set_par = tgafb_set_par,
82 .fb_setcolreg = tgafb_setcolreg,
83 .fb_blank = tgafb_blank,
84 .fb_fillrect = tgafb_fillrect,
85 .fb_copyarea = tgafb_copyarea,
86 .fb_imageblit = tgafb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -070087};
88
89
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070090#ifdef CONFIG_PCI
Linus Torvalds1da177e2005-04-16 15:20:36 -070091/*
92 * PCI registration operations
93 */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070094static int __devinit tgafb_pci_register(struct pci_dev *,
95 const struct pci_device_id *);
96static void __devexit tgafb_pci_unregister(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
98static struct pci_device_id const tgafb_pci_table[] = {
Maciej W. Rozyckifef45902007-02-12 00:54:58 -080099 { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
100 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101};
Maciej W. Rozyckifef45902007-02-12 00:54:58 -0800102MODULE_DEVICE_TABLE(pci, tgafb_pci_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700104static struct pci_driver tgafb_pci_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 .name = "tgafb",
106 .id_table = tgafb_pci_table,
107 .probe = tgafb_pci_register,
108 .remove = __devexit_p(tgafb_pci_unregister),
109};
110
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700111static int __devinit
112tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
113{
114 return tgafb_register(&pdev->dev);
115}
116
117static void __devexit
118tgafb_pci_unregister(struct pci_dev *pdev)
119{
120 tgafb_unregister(&pdev->dev);
121}
122#endif /* CONFIG_PCI */
123
124#ifdef CONFIG_TC
125/*
126 * TC registration operations
127 */
128static int __devinit tgafb_tc_register(struct device *);
129static int __devexit tgafb_tc_unregister(struct device *);
130
131static struct tc_device_id const tgafb_tc_table[] = {
132 { "DEC ", "PMAGD-AA" },
133 { "DEC ", "PMAGD " },
134 { }
135};
136MODULE_DEVICE_TABLE(tc, tgafb_tc_table);
137
138static struct tc_driver tgafb_tc_driver = {
139 .id_table = tgafb_tc_table,
140 .driver = {
141 .name = "tgafb",
142 .bus = &tc_bus_type,
143 .probe = tgafb_tc_register,
144 .remove = __devexit_p(tgafb_tc_unregister),
145 },
146};
147
148static int __devinit
149tgafb_tc_register(struct device *dev)
150{
151 int status = tgafb_register(dev);
152 if (!status)
153 get_device(dev);
154 return status;
155}
156
157static int __devexit
158tgafb_tc_unregister(struct device *dev)
159{
160 put_device(dev);
161 tgafb_unregister(dev);
162 return 0;
163}
164#endif /* CONFIG_TC */
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
167/**
168 * tgafb_check_var - Optional function. Validates a var passed in.
169 * @var: frame buffer variable screen structure
170 * @info: frame buffer structure that represents a single frame buffer
171 */
172static int
173tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
174{
175 struct tga_par *par = (struct tga_par *)info->par;
176
177 if (par->tga_type == TGA_TYPE_8PLANE) {
178 if (var->bits_per_pixel != 8)
179 return -EINVAL;
180 } else {
181 if (var->bits_per_pixel != 32)
182 return -EINVAL;
183 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800184 var->red.length = var->green.length = var->blue.length = 8;
185 if (var->bits_per_pixel == 32) {
186 var->red.offset = 16;
187 var->green.offset = 8;
188 var->blue.offset = 0;
189 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
191 if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
192 return -EINVAL;
193 if (var->nonstd)
194 return -EINVAL;
195 if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
196 return -EINVAL;
197 if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
198 return -EINVAL;
199
200 /* Some of the acceleration routines assume the line width is
201 a multiple of 64 bytes. */
202 if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 64)
203 return -EINVAL;
204
205 return 0;
206}
207
208/**
209 * tgafb_set_par - Optional function. Alters the hardware state.
210 * @info: frame buffer structure that represents a single frame buffer
211 */
212static int
213tgafb_set_par(struct fb_info *info)
214{
215 static unsigned int const deep_presets[4] = {
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700216 0x00004000,
217 0x0000440d,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 0xffffffff,
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700219 0x0000441d
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 };
221 static unsigned int const rasterop_presets[4] = {
222 0x00000003,
223 0x00000303,
224 0xffffffff,
225 0x00000303
226 };
227 static unsigned int const mode_presets[4] = {
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800228 0x00000000,
229 0x00000300,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 0xffffffff,
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800231 0x00000300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 };
233 static unsigned int const base_addr_presets[4] = {
234 0x00000000,
235 0x00000001,
236 0xffffffff,
237 0x00000001
238 };
239
240 struct tga_par *par = (struct tga_par *) info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700241 int tga_bus_pci = TGA_BUS_PCI(par->dev);
242 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 u32 htimings, vtimings, pll_freq;
244 u8 tga_type;
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800245 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 /* Encode video timings. */
248 htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB)
249 | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB));
250 vtimings = (info->var.yres & TGA_VERT_ACTIVE);
251 htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP;
252 vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP;
253 htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC;
254 vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC;
255 htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP;
256 vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP;
257
258 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
259 htimings |= TGA_HORIZ_POLARITY;
260 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
261 vtimings |= TGA_VERT_POLARITY;
262
263 par->htimings = htimings;
264 par->vtimings = vtimings;
265
266 par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN);
267
268 /* Store other useful values in par. */
269 par->xres = info->var.xres;
270 par->yres = info->var.yres;
271 par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
272 par->bits_per_pixel = info->var.bits_per_pixel;
273
274 tga_type = par->tga_type;
275
276 /* First, disable video. */
277 TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG);
278
279 /* Write the DEEP register. */
280 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
281 continue;
282 mb();
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800283 TGA_WRITE_REG(par, deep_presets[tga_type] |
284 (par->sync_on_green ? 0x0 : 0x00010000),
285 TGA_DEEP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
287 continue;
288 mb();
289
290 /* Write some more registers. */
291 TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG);
292 TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG);
293 TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
294
295 /* Calculate & write the PLL. */
296 tgafb_set_pll(par, pll_freq);
297
298 /* Write some more registers. */
299 TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG);
300 TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG);
301
302 /* Init video timing regs. */
303 TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG);
304 TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
305
306 /* Initalise RAMDAC. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700307 if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 /* Init BT485 RAMDAC registers. */
310 BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
311 BT485_CMD_0);
312 BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE);
313 BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */
314 BT485_WRITE(par, 0x40, BT485_CMD_1);
315 BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */
316 BT485_WRITE(par, 0xff, BT485_PIXEL_MASK);
317
318 /* Fill palette registers. */
319 BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE);
320 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
321
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800322#ifdef CONFIG_HW_CONSOLE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800324 int j = color_table[i];
325
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 TGA_WRITE_REG(par, default_red[j]|(BT485_DATA_PAL<<8),
327 TGA_RAMDAC_REG);
328 TGA_WRITE_REG(par, default_grn[j]|(BT485_DATA_PAL<<8),
329 TGA_RAMDAC_REG);
330 TGA_WRITE_REG(par, default_blu[j]|(BT485_DATA_PAL<<8),
331 TGA_RAMDAC_REG);
332 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800333 for (i = 0; i < 240 * 3; i += 4) {
334#else
335 for (i = 0; i < 256 * 3; i += 4) {
336#endif
337 TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800339 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800341 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800343 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 TGA_RAMDAC_REG);
345 }
346
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700347 } else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
348
349 /* Init BT459 RAMDAC registers. */
350 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
351 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
352 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
353 (par->sync_on_green ? 0xc0 : 0x40));
354
355 BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
356
357 /* Fill the palette. */
358 BT459_LOAD_ADDR(par, 0x0000);
359 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
360
361#ifdef CONFIG_HW_CONSOLE
362 for (i = 0; i < 16; i++) {
363 int j = color_table[i];
364
365 TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
366 TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
367 TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
368 }
369 for (i = 0; i < 240 * 3; i += 4) {
370#else
371 for (i = 0; i < 256 * 3; i += 4) {
372#endif
373 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
374 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
375 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
376 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
377 }
378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 } else { /* 24-plane or 24plusZ */
380
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800381 /* Init BT463 RAMDAC registers. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
383 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
384 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2,
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800385 (par->sync_on_green ? 0xc0 : 0x40));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
387 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
388 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
389 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
390 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
391
392 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
393 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
394 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
395 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
396
397 /* Fill the palette. */
398 BT463_LOAD_ADDR(par, 0x0000);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800399 TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800401#ifdef CONFIG_HW_CONSOLE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800403 int j = color_table[i];
404
405 TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
406 TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
407 TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800409 for (i = 0; i < 512 * 3; i += 4) {
410#else
411 for (i = 0; i < 528 * 3; i += 4) {
412#endif
413 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
414 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
415 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
416 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 }
418
419 /* Fill window type table after start of vertical retrace. */
420 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
421 continue;
422 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
423 mb();
424 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
425 continue;
426 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
427
428 BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800429 TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430
431 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800432 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
433 TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG);
434 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 }
436
437 }
438
439 /* Finally, enable video scan (and pray for the monitor... :-) */
440 TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG);
441
442 return 0;
443}
444
445#define DIFFCHECK(X) \
446do { \
447 if (m <= 0x3f) { \
448 int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \
449 if (delta < 0) \
450 delta = -delta; \
451 if (delta < min_diff) \
452 min_diff = delta, vm = m, va = a, vr = r; \
453 } \
454} while (0)
455
456static void
457tgafb_set_pll(struct tga_par *par, int f)
458{
459 int n, shift, base, min_diff, target;
460 int r,a,m,vm = 34, va = 1, vr = 30;
461
462 for (r = 0 ; r < 12 ; r++)
463 TGA_WRITE_REG(par, !r, TGA_CLOCK_REG);
464
465 if (f > TGA_PLL_MAX_FREQ)
466 f = TGA_PLL_MAX_FREQ;
467
468 if (f >= TGA_PLL_MAX_FREQ / 2)
469 shift = 0;
470 else if (f >= TGA_PLL_MAX_FREQ / 4)
471 shift = 1;
472 else
473 shift = 2;
474
475 TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG);
476 TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG);
477
478 for (r = 0 ; r < 10 ; r++)
479 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
480
481 if (f <= 120000) {
482 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
483 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
484 }
485 else if (f <= 200000) {
486 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
487 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
488 }
489 else {
490 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
491 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
492 }
493
494 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
495 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
496 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
497 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
498 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
499 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
500
501 target = (f << shift) / TGA_PLL_BASE_FREQ;
502 min_diff = TGA_PLL_MAX_FREQ;
503
504 r = 7 / target;
505 if (!r) r = 1;
506
507 base = target * r;
508 while (base < 449) {
509 for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) {
510 m = ((n + 3) / 7) - 1;
511 a = 0;
512 DIFFCHECK((m + 1) * 7);
513 m++;
514 DIFFCHECK((m + 1) * 7);
515 m = (n / 6) - 1;
516 if ((a = n % 6))
517 DIFFCHECK(n);
518 }
519 r++;
520 base += target;
521 }
522
523 vr--;
524
525 for (r = 0; r < 8; r++)
526 TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG);
527 for (r = 0; r < 8 ; r++)
528 TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG);
529 for (r = 0; r < 7 ; r++)
530 TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG);
531 TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG);
532}
533
534
535/**
536 * tgafb_setcolreg - Optional function. Sets a color register.
537 * @regno: boolean, 0 copy local, 1 get_user() function
538 * @red: frame buffer colormap structure
539 * @green: The green value which can be up to 16 bits wide
540 * @blue: The blue value which can be up to 16 bits wide.
541 * @transp: If supported the alpha value which can be up to 16 bits wide.
542 * @info: frame buffer info structure
543 */
544static int
545tgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
546 unsigned transp, struct fb_info *info)
547{
548 struct tga_par *par = (struct tga_par *) info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700549 int tga_bus_pci = TGA_BUS_PCI(par->dev);
550 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
552 if (regno > 255)
553 return 1;
554 red >>= 8;
555 green >>= 8;
556 blue >>= 8;
557
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700558 if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
560 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
561 TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
562 TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
563 TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700564 } else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
565 BT459_LOAD_ADDR(par, regno);
566 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
567 TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
568 TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
569 TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800570 } else {
571 if (regno < 16) {
572 u32 value = (regno << 16) | (regno << 8) | regno;
573 ((u32 *)info->pseudo_palette)[regno] = value;
574 }
575 BT463_LOAD_ADDR(par, regno);
576 TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
577 TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
578 TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
579 TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 }
581
582 return 0;
583}
584
585
586/**
587 * tgafb_blank - Optional function. Blanks the display.
588 * @blank_mode: the blank mode we want.
589 * @info: frame buffer structure that represents a single frame buffer
590 */
591static int
592tgafb_blank(int blank, struct fb_info *info)
593{
594 struct tga_par *par = (struct tga_par *) info->par;
595 u32 vhcr, vvcr, vvvr;
596 unsigned long flags;
597
598 local_irq_save(flags);
599
600 vhcr = TGA_READ_REG(par, TGA_HORIZ_REG);
601 vvcr = TGA_READ_REG(par, TGA_VERT_REG);
602 vvvr = TGA_READ_REG(par, TGA_VALID_REG);
603 vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK);
604
605 switch (blank) {
606 case FB_BLANK_UNBLANK: /* Unblanking */
607 if (par->vesa_blanked) {
608 TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG);
609 TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG);
610 par->vesa_blanked = 0;
611 }
612 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG);
613 break;
614
615 case FB_BLANK_NORMAL: /* Normal blanking */
616 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK,
617 TGA_VALID_REG);
618 break;
619
620 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
621 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
622 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
623 par->vesa_blanked = 1;
624 break;
625
626 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
627 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
628 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
629 par->vesa_blanked = 1;
630 break;
631
632 case FB_BLANK_POWERDOWN: /* Poweroff */
633 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
634 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
635 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
636 par->vesa_blanked = 1;
637 break;
638 }
639
640 local_irq_restore(flags);
641 return 0;
642}
643
644
645/*
646 * Acceleration.
647 */
648
649/**
650 * tgafb_imageblit - REQUIRED function. Can use generic routines if
651 * non acclerated hardware and packed pixel based.
652 * Copies a image from system memory to the screen.
653 *
654 * @info: frame buffer structure that represents a single frame buffer
655 * @image: structure defining the image.
656 */
657static void
658tgafb_imageblit(struct fb_info *info, const struct fb_image *image)
659{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 struct tga_par *par = (struct tga_par *) info->par;
661 u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask;
662 unsigned long rincr, line_length, shift, pos, is8bpp;
663 unsigned long i, j;
664 const unsigned char *data;
665 void __iomem *regs_base;
666 void __iomem *fb_base;
667
668 dx = image->dx;
669 dy = image->dy;
670 width = image->width;
671 height = image->height;
672 vxres = info->var.xres_virtual;
673 vyres = info->var.yres_virtual;
674 line_length = info->fix.line_length;
675 rincr = (width + 7) / 8;
676
677 /* Crop the image to the screen. */
678 if (dx > vxres || dy > vyres)
679 return;
680 if (dx + width > vxres)
681 width = vxres - dx;
682 if (dy + height > vyres)
683 height = vyres - dy;
684
685 /* For copies that aren't pixel expansion, there's little we
686 can do better than the generic code. */
687 /* ??? There is a DMA write mode; I wonder if that could be
688 made to pull the data from the image buffer... */
689 if (image->depth > 1) {
690 cfb_imageblit(info, image);
691 return;
692 }
693
694 regs_base = par->tga_regs_base;
695 fb_base = par->tga_fb_base;
696 is8bpp = info->var.bits_per_pixel == 8;
697
698 /* Expand the color values to fill 32-bits. */
699 /* ??? Would be nice to notice colour changes elsewhere, so
700 that we can do this only when necessary. */
701 fgcolor = image->fg_color;
702 bgcolor = image->bg_color;
703 if (is8bpp) {
704 fgcolor |= fgcolor << 8;
705 fgcolor |= fgcolor << 16;
706 bgcolor |= bgcolor << 8;
707 bgcolor |= bgcolor << 16;
708 } else {
709 if (fgcolor < 16)
710 fgcolor = ((u32 *)info->pseudo_palette)[fgcolor];
711 if (bgcolor < 16)
712 bgcolor = ((u32 *)info->pseudo_palette)[bgcolor];
713 }
714 __raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG);
715 __raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG);
716
717 /* Acquire proper alignment; set up the PIXELMASK register
718 so that we only write the proper character cell. */
719 pos = dy * line_length;
720 if (is8bpp) {
721 pos += dx;
722 shift = pos & 3;
723 pos &= -4;
724 } else {
725 pos += dx * 4;
726 shift = (pos & 7) >> 2;
727 pos &= -8;
728 }
729
730 data = (const unsigned char *) image->data;
731
732 /* Enable opaque stipple mode. */
733 __raw_writel((is8bpp
734 ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE
735 : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE),
736 regs_base + TGA_MODE_REG);
737
738 if (width + shift <= 32) {
739 unsigned long bwidth;
740
741 /* Handle common case of imaging a single character, in
742 a font less than 32 pixels wide. */
743
744 pixelmask = (1 << width) - 1;
745 pixelmask <<= shift;
746 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
747 wmb();
748
749 bwidth = (width + 7) / 8;
750
751 for (i = 0; i < height; ++i) {
752 u32 mask = 0;
753
754 /* The image data is bit big endian; we need
755 little endian. */
756 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800757 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
759 __raw_writel(mask << shift, fb_base + pos);
760
761 pos += line_length;
762 data += rincr;
763 }
764 wmb();
765 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
766 } else if (shift == 0) {
767 unsigned long pos0 = pos;
768 const unsigned char *data0 = data;
769 unsigned long bincr = (is8bpp ? 8 : 8*4);
770 unsigned long bwidth;
771
772 /* Handle another common case in which accel_putcs
773 generates a large bitmap, which happens to be aligned.
774 Allow the tail to be misaligned. This case is
775 interesting because we've not got to hold partial
776 bytes across the words being written. */
777
778 wmb();
779
780 bwidth = (width / 8) & -4;
781 for (i = 0; i < height; ++i) {
782 for (j = 0; j < bwidth; j += 4) {
783 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800784 mask |= bitrev8(data[j+0]) << (0 * 8);
785 mask |= bitrev8(data[j+1]) << (1 * 8);
786 mask |= bitrev8(data[j+2]) << (2 * 8);
787 mask |= bitrev8(data[j+3]) << (3 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 __raw_writel(mask, fb_base + pos + j*bincr);
789 }
790 pos += line_length;
791 data += rincr;
792 }
793 wmb();
794
795 pixelmask = (1ul << (width & 31)) - 1;
796 if (pixelmask) {
797 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
798 wmb();
799
800 pos = pos0 + bwidth*bincr;
801 data = data0 + bwidth;
802 bwidth = ((width & 31) + 7) / 8;
803
804 for (i = 0; i < height; ++i) {
805 u32 mask = 0;
806 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800807 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 __raw_writel(mask, fb_base + pos);
809 pos += line_length;
810 data += rincr;
811 }
812 wmb();
813 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
814 }
815 } else {
816 unsigned long pos0 = pos;
817 const unsigned char *data0 = data;
818 unsigned long bincr = (is8bpp ? 8 : 8*4);
819 unsigned long bwidth;
820
821 /* Finally, handle the generic case of misaligned start.
822 Here we split the write into 16-bit spans. This allows
823 us to use only one pixel mask, instead of four as would
824 be required by writing 24-bit spans. */
825
826 pixelmask = 0xffff << shift;
827 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
828 wmb();
829
830 bwidth = (width / 8) & -2;
831 for (i = 0; i < height; ++i) {
832 for (j = 0; j < bwidth; j += 2) {
833 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800834 mask |= bitrev8(data[j+0]) << (0 * 8);
835 mask |= bitrev8(data[j+1]) << (1 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 mask <<= shift;
837 __raw_writel(mask, fb_base + pos + j*bincr);
838 }
839 pos += line_length;
840 data += rincr;
841 }
842 wmb();
843
844 pixelmask = ((1ul << (width & 15)) - 1) << shift;
845 if (pixelmask) {
846 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
847 wmb();
848
849 pos = pos0 + bwidth*bincr;
850 data = data0 + bwidth;
851 bwidth = (width & 15) > 8;
852
853 for (i = 0; i < height; ++i) {
Akinobu Mita1c667682006-12-08 02:36:26 -0800854 u32 mask = bitrev8(data[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 if (bwidth)
Akinobu Mita1c667682006-12-08 02:36:26 -0800856 mask |= bitrev8(data[1]) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 mask <<= shift;
858 __raw_writel(mask, fb_base + pos);
859 pos += line_length;
860 data += rincr;
861 }
862 wmb();
863 }
864 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
865 }
866
867 /* Disable opaque stipple mode. */
868 __raw_writel((is8bpp
869 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
870 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
871 regs_base + TGA_MODE_REG);
872}
873
874/**
875 * tgafb_fillrect - REQUIRED function. Can use generic routines if
876 * non acclerated hardware and packed pixel based.
877 * Draws a rectangle on the screen.
878 *
879 * @info: frame buffer structure that represents a single frame buffer
880 * @rect: structure defining the rectagle and operation.
881 */
882static void
883tgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
884{
885 struct tga_par *par = (struct tga_par *) info->par;
886 int is8bpp = info->var.bits_per_pixel == 8;
887 u32 dx, dy, width, height, vxres, vyres, color;
888 unsigned long pos, align, line_length, i, j;
889 void __iomem *regs_base;
890 void __iomem *fb_base;
891
892 dx = rect->dx;
893 dy = rect->dy;
894 width = rect->width;
895 height = rect->height;
896 vxres = info->var.xres_virtual;
897 vyres = info->var.yres_virtual;
898 line_length = info->fix.line_length;
899 regs_base = par->tga_regs_base;
900 fb_base = par->tga_fb_base;
901
902 /* Crop the rectangle to the screen. */
903 if (dx > vxres || dy > vyres || !width || !height)
904 return;
905 if (dx + width > vxres)
906 width = vxres - dx;
907 if (dy + height > vyres)
908 height = vyres - dy;
909
910 pos = dy * line_length + dx * (is8bpp ? 1 : 4);
911
912 /* ??? We could implement ROP_XOR with opaque fill mode
913 and a RasterOp setting of GXxor, but as far as I can
914 tell, this mode is not actually used in the kernel.
915 Thus I am ignoring it for now. */
916 if (rect->rop != ROP_COPY) {
917 cfb_fillrect(info, rect);
918 return;
919 }
920
921 /* Expand the color value to fill 8 pixels. */
922 color = rect->color;
923 if (is8bpp) {
924 color |= color << 8;
925 color |= color << 16;
926 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
927 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
928 } else {
929 if (color < 16)
930 color = ((u32 *)info->pseudo_palette)[color];
931 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
932 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
933 __raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG);
934 __raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG);
935 __raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG);
936 __raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG);
937 __raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG);
938 __raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG);
939 }
940
941 /* The DATA register holds the fill mask for block fill mode.
942 Since we're not stippling, this is all ones. */
943 __raw_writel(0xffffffff, regs_base + TGA_DATA_REG);
944
945 /* Enable block fill mode. */
946 __raw_writel((is8bpp
947 ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL
948 : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL),
949 regs_base + TGA_MODE_REG);
950 wmb();
951
952 /* We can fill 2k pixels per operation. Notice blocks that fit
953 the width of the screen so that we can take advantage of this
954 and fill more than one line per write. */
955 if (width == line_length)
956 width *= height, height = 1;
957
958 /* The write into the frame buffer must be aligned to 4 bytes,
959 but we are allowed to encode the offset within the word in
960 the data word written. */
961 align = (pos & 3) << 16;
962 pos &= -4;
963
964 if (width <= 2048) {
965 u32 data;
966
967 data = (width - 1) | align;
968
969 for (i = 0; i < height; ++i) {
970 __raw_writel(data, fb_base + pos);
971 pos += line_length;
972 }
973 } else {
974 unsigned long Bpp = (is8bpp ? 1 : 4);
975 unsigned long nwidth = width & -2048;
976 u32 fdata, ldata;
977
978 fdata = (2048 - 1) | align;
979 ldata = ((width & 2047) - 1) | align;
980
981 for (i = 0; i < height; ++i) {
982 for (j = 0; j < nwidth; j += 2048)
983 __raw_writel(fdata, fb_base + pos + j*Bpp);
984 if (j < width)
985 __raw_writel(ldata, fb_base + pos + j*Bpp);
986 pos += line_length;
987 }
988 }
989 wmb();
990
991 /* Disable block fill mode. */
992 __raw_writel((is8bpp
993 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
994 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
995 regs_base + TGA_MODE_REG);
996}
997
998/**
999 * tgafb_copyarea - REQUIRED function. Can use generic routines if
1000 * non acclerated hardware and packed pixel based.
1001 * Copies on area of the screen to another area.
1002 *
1003 * @info: frame buffer structure that represents a single frame buffer
1004 * @area: structure defining the source and destination.
1005 */
1006
1007/* Handle the special case of copying entire lines, e.g. during scrolling.
1008 We can avoid a lot of needless computation in this case. In the 8bpp
1009 case we need to use the COPY64 registers instead of mask writes into
1010 the frame buffer to achieve maximum performance. */
1011
1012static inline void
1013copyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy,
1014 u32 height, u32 width)
1015{
1016 struct tga_par *par = (struct tga_par *) info->par;
1017 void __iomem *tga_regs = par->tga_regs_base;
1018 unsigned long dpos, spos, i, n64;
1019
1020 /* Set up the MODE and PIXELSHIFT registers. */
1021 __raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1022 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1023 wmb();
1024
1025 n64 = (height * width) / 64;
1026
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001027 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 spos = (sy + height) * width;
1029 dpos = (dy + height) * width;
1030
1031 for (i = 0; i < n64; ++i) {
1032 spos -= 64;
1033 dpos -= 64;
1034 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1035 wmb();
1036 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1037 wmb();
1038 }
1039 } else {
1040 spos = sy * width;
1041 dpos = dy * width;
1042
1043 for (i = 0; i < n64; ++i) {
1044 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1045 wmb();
1046 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1047 wmb();
1048 spos += 64;
1049 dpos += 64;
1050 }
1051 }
1052
1053 /* Reset the MODE register to normal. */
1054 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1055}
1056
1057static inline void
1058copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
1059 u32 height, u32 width)
1060{
1061 struct tga_par *par = (struct tga_par *) info->par;
1062 void __iomem *tga_regs = par->tga_regs_base;
1063 void __iomem *tga_fb = par->tga_fb_base;
1064 void __iomem *src;
1065 void __iomem *dst;
1066 unsigned long i, n16;
1067
1068 /* Set up the MODE and PIXELSHIFT registers. */
1069 __raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1070 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1071 wmb();
1072
1073 n16 = (height * width) / 16;
1074
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001075 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 src = tga_fb + (sy + height) * width * 4;
1077 dst = tga_fb + (dy + height) * width * 4;
1078
1079 for (i = 0; i < n16; ++i) {
1080 src -= 64;
1081 dst -= 64;
1082 __raw_writel(0xffff, src);
1083 wmb();
1084 __raw_writel(0xffff, dst);
1085 wmb();
1086 }
1087 } else {
1088 src = tga_fb + sy * width * 4;
1089 dst = tga_fb + dy * width * 4;
1090
1091 for (i = 0; i < n16; ++i) {
1092 __raw_writel(0xffff, src);
1093 wmb();
1094 __raw_writel(0xffff, dst);
1095 wmb();
1096 src += 64;
1097 dst += 64;
1098 }
1099 }
1100
1101 /* Reset the MODE register to normal. */
1102 __raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1103}
1104
1105/* The general case of forward copy in 8bpp mode. */
1106static inline void
1107copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1108 u32 height, u32 width, u32 line_length)
1109{
1110 struct tga_par *par = (struct tga_par *) info->par;
1111 unsigned long i, copied, left;
1112 unsigned long dpos, spos, dalign, salign, yincr;
1113 u32 smask_first, dmask_first, dmask_last;
1114 int pixel_shift, need_prime, need_second;
1115 unsigned long n64, n32, xincr_first;
1116 void __iomem *tga_regs;
1117 void __iomem *tga_fb;
1118
1119 yincr = line_length;
1120 if (dy > sy) {
1121 dy += height - 1;
1122 sy += height - 1;
1123 yincr = -yincr;
1124 }
1125
1126 /* Compute the offsets and alignments in the frame buffer.
1127 More than anything else, these control how we do copies. */
1128 dpos = dy * line_length + dx;
1129 spos = sy * line_length + sx;
1130 dalign = dpos & 7;
1131 salign = spos & 7;
1132 dpos &= -8;
1133 spos &= -8;
1134
1135 /* Compute the value for the PIXELSHIFT register. This controls
1136 both non-co-aligned source and destination and copy direction. */
1137 if (dalign >= salign)
1138 pixel_shift = dalign - salign;
1139 else
1140 pixel_shift = 8 - (salign - dalign);
1141
1142 /* Figure out if we need an additional priming step for the
1143 residue register. */
1144 need_prime = (salign > dalign);
1145 if (need_prime)
1146 dpos -= 8;
1147
1148 /* Begin by copying the leading unaligned destination. Copy enough
1149 to make the next destination address 32-byte aligned. */
1150 copied = 32 - (dalign + (dpos & 31));
1151 if (copied == 32)
1152 copied = 0;
1153 xincr_first = (copied + 7) & -8;
1154 smask_first = dmask_first = (1ul << copied) - 1;
1155 smask_first <<= salign;
1156 dmask_first <<= dalign + need_prime*8;
1157 if (need_prime && copied > 24)
1158 copied -= 8;
1159 left = width - copied;
1160
1161 /* Care for small copies. */
1162 if (copied > width) {
1163 u32 t;
1164 t = (1ul << width) - 1;
1165 t <<= dalign + need_prime*8;
1166 dmask_first &= t;
1167 left = 0;
1168 }
1169
1170 /* Attempt to use 64-byte copies. This is only possible if the
1171 source and destination are co-aligned at 64 bytes. */
1172 n64 = need_second = 0;
1173 if ((dpos & 63) == (spos & 63)
1174 && (height == 1 || line_length % 64 == 0)) {
1175 /* We may need a 32-byte copy to ensure 64 byte alignment. */
1176 need_second = (dpos + xincr_first) & 63;
1177 if ((need_second & 32) != need_second)
1178 printk(KERN_ERR "tgafb: need_second wrong\n");
1179 if (left >= need_second + 64) {
1180 left -= need_second;
1181 n64 = left / 64;
1182 left %= 64;
1183 } else
1184 need_second = 0;
1185 }
1186
1187 /* Copy trailing full 32-byte sections. This will be the main
1188 loop if the 64 byte loop can't be used. */
1189 n32 = left / 32;
1190 left %= 32;
1191
1192 /* Copy the trailing unaligned destination. */
1193 dmask_last = (1ul << left) - 1;
1194
1195 tga_regs = par->tga_regs_base;
1196 tga_fb = par->tga_fb_base;
1197
1198 /* Set up the MODE and PIXELSHIFT registers. */
1199 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1200 __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
1201 wmb();
1202
1203 for (i = 0; i < height; ++i) {
1204 unsigned long j;
1205 void __iomem *sfb;
1206 void __iomem *dfb;
1207
1208 sfb = tga_fb + spos;
1209 dfb = tga_fb + dpos;
1210 if (dmask_first) {
1211 __raw_writel(smask_first, sfb);
1212 wmb();
1213 __raw_writel(dmask_first, dfb);
1214 wmb();
1215 sfb += xincr_first;
1216 dfb += xincr_first;
1217 }
1218
1219 if (need_second) {
1220 __raw_writel(0xffffffff, sfb);
1221 wmb();
1222 __raw_writel(0xffffffff, dfb);
1223 wmb();
1224 sfb += 32;
1225 dfb += 32;
1226 }
1227
1228 if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
1229 printk(KERN_ERR
1230 "tgafb: misaligned copy64 (s:%p, d:%p)\n",
1231 sfb, dfb);
1232
1233 for (j = 0; j < n64; ++j) {
1234 __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1235 wmb();
1236 __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1237 wmb();
1238 sfb += 64;
1239 dfb += 64;
1240 }
1241
1242 for (j = 0; j < n32; ++j) {
1243 __raw_writel(0xffffffff, sfb);
1244 wmb();
1245 __raw_writel(0xffffffff, dfb);
1246 wmb();
1247 sfb += 32;
1248 dfb += 32;
1249 }
1250
1251 if (dmask_last) {
1252 __raw_writel(0xffffffff, sfb);
1253 wmb();
1254 __raw_writel(dmask_last, dfb);
1255 wmb();
1256 }
1257
1258 spos += yincr;
1259 dpos += yincr;
1260 }
1261
1262 /* Reset the MODE register to normal. */
1263 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1264}
1265
1266/* The (almost) general case of backward copy in 8bpp mode. */
1267static inline void
1268copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1269 u32 height, u32 width, u32 line_length,
1270 const struct fb_copyarea *area)
1271{
1272 struct tga_par *par = (struct tga_par *) info->par;
1273 unsigned long i, left, yincr;
1274 unsigned long depos, sepos, dealign, sealign;
1275 u32 mask_first, mask_last;
1276 unsigned long n32;
1277 void __iomem *tga_regs;
1278 void __iomem *tga_fb;
1279
1280 yincr = line_length;
1281 if (dy > sy) {
1282 dy += height - 1;
1283 sy += height - 1;
1284 yincr = -yincr;
1285 }
1286
1287 /* Compute the offsets and alignments in the frame buffer.
1288 More than anything else, these control how we do copies. */
1289 depos = dy * line_length + dx + width;
1290 sepos = sy * line_length + sx + width;
1291 dealign = depos & 7;
1292 sealign = sepos & 7;
1293
1294 /* ??? The documentation appears to be incorrect (or very
1295 misleading) wrt how pixel shifting works in backward copy
1296 mode, i.e. when PIXELSHIFT is negative. I give up for now.
1297 Do handle the common case of co-aligned backward copies,
1298 but frob everything else back on generic code. */
1299 if (dealign != sealign) {
1300 cfb_copyarea(info, area);
1301 return;
1302 }
1303
1304 /* We begin the copy with the trailing pixels of the
1305 unaligned destination. */
1306 mask_first = (1ul << dealign) - 1;
1307 left = width - dealign;
1308
1309 /* Care for small copies. */
1310 if (dealign > width) {
1311 mask_first ^= (1ul << (dealign - width)) - 1;
1312 left = 0;
1313 }
1314
1315 /* Next copy full words at a time. */
1316 n32 = left / 32;
1317 left %= 32;
1318
1319 /* Finally copy the unaligned head of the span. */
1320 mask_last = -1 << (32 - left);
1321
1322 tga_regs = par->tga_regs_base;
1323 tga_fb = par->tga_fb_base;
1324
1325 /* Set up the MODE and PIXELSHIFT registers. */
1326 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1327 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1328 wmb();
1329
1330 for (i = 0; i < height; ++i) {
1331 unsigned long j;
1332 void __iomem *sfb;
1333 void __iomem *dfb;
1334
1335 sfb = tga_fb + sepos;
1336 dfb = tga_fb + depos;
1337 if (mask_first) {
1338 __raw_writel(mask_first, sfb);
1339 wmb();
1340 __raw_writel(mask_first, dfb);
1341 wmb();
1342 }
1343
1344 for (j = 0; j < n32; ++j) {
1345 sfb -= 32;
1346 dfb -= 32;
1347 __raw_writel(0xffffffff, sfb);
1348 wmb();
1349 __raw_writel(0xffffffff, dfb);
1350 wmb();
1351 }
1352
1353 if (mask_last) {
1354 sfb -= 32;
1355 dfb -= 32;
1356 __raw_writel(mask_last, sfb);
1357 wmb();
1358 __raw_writel(mask_last, dfb);
1359 wmb();
1360 }
1361
1362 sepos += yincr;
1363 depos += yincr;
1364 }
1365
1366 /* Reset the MODE register to normal. */
1367 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1368}
1369
1370static void
1371tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
1372{
1373 unsigned long dx, dy, width, height, sx, sy, vxres, vyres;
1374 unsigned long line_length, bpp;
1375
1376 dx = area->dx;
1377 dy = area->dy;
1378 width = area->width;
1379 height = area->height;
1380 sx = area->sx;
1381 sy = area->sy;
1382 vxres = info->var.xres_virtual;
1383 vyres = info->var.yres_virtual;
1384 line_length = info->fix.line_length;
1385
1386 /* The top left corners must be in the virtual screen. */
1387 if (dx > vxres || sx > vxres || dy > vyres || sy > vyres)
1388 return;
1389
1390 /* Clip the destination. */
1391 if (dx + width > vxres)
1392 width = vxres - dx;
1393 if (dy + height > vyres)
1394 height = vyres - dy;
1395
1396 /* The source must be completely inside the virtual screen. */
1397 if (sx + width > vxres || sy + height > vyres)
1398 return;
1399
1400 bpp = info->var.bits_per_pixel;
1401
1402 /* Detect copies of the entire line. */
1403 if (width * (bpp >> 3) == line_length) {
1404 if (bpp == 8)
1405 copyarea_line_8bpp(info, dy, sy, height, width);
1406 else
1407 copyarea_line_32bpp(info, dy, sy, height, width);
1408 }
1409
1410 /* ??? The documentation is unclear to me exactly how the pixelshift
1411 register works in 32bpp mode. Since I don't have hardware to test,
1412 give up for now and fall back on the generic routines. */
1413 else if (bpp == 32)
1414 cfb_copyarea(info, area);
1415
1416 /* Detect overlapping source and destination that requires
1417 a backward copy. */
1418 else if (dy == sy && dx > sx && dx < sx + width)
1419 copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
1420 width, line_length, area);
1421 else
1422 copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
1423 width, line_length);
1424}
1425
1426
1427/*
1428 * Initialisation
1429 */
1430
1431static void
1432tgafb_init_fix(struct fb_info *info)
1433{
1434 struct tga_par *par = (struct tga_par *)info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001435 int tga_bus_pci = TGA_BUS_PCI(par->dev);
1436 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 u8 tga_type = par->tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001438 const char *tga_type_name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
1440 switch (tga_type) {
1441 case TGA_TYPE_8PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001442 if (tga_bus_pci)
1443 tga_type_name = "Digital ZLXp-E1";
1444 if (tga_bus_tc)
1445 tga_type_name = "Digital ZLX-E1";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 break;
1447 case TGA_TYPE_24PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001448 if (tga_bus_pci)
1449 tga_type_name = "Digital ZLXp-E2";
1450 if (tga_bus_tc)
1451 tga_type_name = "Digital ZLX-E2";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 break;
1453 case TGA_TYPE_24PLUSZ:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001454 if (tga_bus_pci)
1455 tga_type_name = "Digital ZLXp-E3";
1456 if (tga_bus_tc)
1457 tga_type_name = "Digital ZLX-E3";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 break;
1459 default:
1460 tga_type_name = "Unknown";
1461 break;
1462 }
1463
1464 strlcpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
1465
1466 info->fix.type = FB_TYPE_PACKED_PIXELS;
1467 info->fix.type_aux = 0;
1468 info->fix.visual = (tga_type == TGA_TYPE_8PLANE
1469 ? FB_VISUAL_PSEUDOCOLOR
Maciej W. Rozyckibe601182007-02-12 00:54:54 -08001470 : FB_VISUAL_DIRECTCOLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
1473 info->fix.smem_start = (size_t) par->tga_fb_base;
1474 info->fix.smem_len = info->fix.line_length * par->yres;
1475 info->fix.mmio_start = (size_t) par->tga_regs_base;
1476 info->fix.mmio_len = 512;
1477
1478 info->fix.xpanstep = 0;
1479 info->fix.ypanstep = 0;
1480 info->fix.ywrapstep = 0;
1481
1482 info->fix.accel = FB_ACCEL_DEC_TGA;
1483}
1484
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001485static int __devinit
1486tgafb_register(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001488 static const struct fb_videomode modedb_tc = {
1489 /* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
1490 "1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
1491 FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
1492 };
1493
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 static unsigned int const fb_offset_presets[4] = {
1495 TGA_8PLANE_FB_OFFSET,
1496 TGA_24PLANE_FB_OFFSET,
1497 0xffffffff,
1498 TGA_24PLUSZ_FB_OFFSET
1499 };
1500
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001501 const struct fb_videomode *modedb_tga = NULL;
1502 resource_size_t bar0_start = 0, bar0_len = 0;
1503 const char *mode_option_tga = NULL;
1504 int tga_bus_pci = TGA_BUS_PCI(dev);
1505 int tga_bus_tc = TGA_BUS_TC(dev);
1506 unsigned int modedbsize_tga = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 void __iomem *mem_base;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001508 struct fb_info *info;
1509 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 u8 tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001511 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 /* Enable device in PCI config. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001514 if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
1516 return -ENODEV;
1517 }
1518
1519 /* Allocate the fb and par structures. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001520 info = framebuffer_alloc(sizeof(struct tga_par), dev);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001521 if (!info) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 printk(KERN_ERR "tgafb: Cannot allocate memory\n");
1523 return -ENOMEM;
1524 }
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001525
1526 par = info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001527 dev_set_drvdata(dev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
1529 /* Request the mem regions. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 ret = -ENODEV;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001531 if (tga_bus_pci) {
1532 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1533 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1534 }
1535 if (tga_bus_tc) {
1536 bar0_start = to_tc_dev(dev)->resource.start;
1537 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1538 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
1540 printk(KERN_ERR "tgafb: cannot reserve FB region\n");
1541 goto err0;
1542 }
1543
1544 /* Map the framebuffer. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001545 mem_base = ioremap_nocache(bar0_start, bar0_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 if (!mem_base) {
1547 printk(KERN_ERR "tgafb: Cannot map MMIO\n");
1548 goto err1;
1549 }
1550
1551 /* Grab info about the card. */
1552 tga_type = (readl(mem_base) >> 12) & 0x0f;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001553 par->dev = dev;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001554 par->tga_mem_base = mem_base;
1555 par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
1556 par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
1557 par->tga_type = tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001558 if (tga_bus_pci)
1559 pci_read_config_byte(to_pci_dev(dev), PCI_REVISION_ID,
1560 &par->tga_chip_rev);
1561 if (tga_bus_tc)
1562 par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
1564 /* Setup framebuffer. */
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001565 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
1566 FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT;
1567 info->fbops = &tgafb_ops;
1568 info->screen_base = par->tga_fb_base;
1569 info->pseudo_palette = (void *)(par + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
1571 /* This should give a reasonable default video mode. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001572 if (tga_bus_pci) {
1573 mode_option_tga = mode_option_pci;
1574 }
1575 if (tga_bus_tc) {
1576 mode_option_tga = mode_option_tc;
1577 modedb_tga = &modedb_tc;
1578 modedbsize_tga = 1;
1579 }
1580 ret = fb_find_mode(&info->var, info,
1581 mode_option ? mode_option : mode_option_tga,
1582 modedb_tga, modedbsize_tga, NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 tga_type == TGA_TYPE_8PLANE ? 8 : 32);
1584 if (ret == 0 || ret == 4) {
1585 printk(KERN_ERR "tgafb: Could not find valid video mode\n");
1586 ret = -EINVAL;
1587 goto err1;
1588 }
1589
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001590 if (fb_alloc_cmap(&info->cmap, 256, 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 printk(KERN_ERR "tgafb: Could not allocate color map\n");
1592 ret = -ENOMEM;
1593 goto err1;
1594 }
1595
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001596 tgafb_set_par(info);
1597 tgafb_init_fix(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001599 if (register_framebuffer(info) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 printk(KERN_ERR "tgafb: Could not register framebuffer\n");
1601 ret = -EINVAL;
1602 goto err1;
1603 }
1604
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001605 if (tga_bus_pci) {
1606 pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
1607 par->tga_chip_rev);
1608 pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
1609 to_pci_dev(dev)->bus->number,
1610 PCI_SLOT(to_pci_dev(dev)->devfn),
1611 PCI_FUNC(to_pci_dev(dev)->devfn));
1612 }
1613 if (tga_bus_tc)
1614 pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
1615 par->tga_chip_rev);
1616 pr_info("fb%d: %s frame buffer device at 0x%lx\n",
1617 info->node, info->fix.id, (long)bar0_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 return 0;
1620
1621 err1:
Amol Lade4bf0512006-12-08 02:40:04 -08001622 if (mem_base)
1623 iounmap(mem_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 release_mem_region(bar0_start, bar0_len);
1625 err0:
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001626 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 return ret;
1628}
1629
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001630static void __devexit
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001631tgafb_unregister(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001633 resource_size_t bar0_start = 0, bar0_len = 0;
1634 int tga_bus_pci = TGA_BUS_PCI(dev);
1635 int tga_bus_tc = TGA_BUS_TC(dev);
1636 struct fb_info *info = NULL;
1637 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001639 info = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640 if (!info)
1641 return;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001642
1643 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 unregister_framebuffer(info);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001645 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 iounmap(par->tga_mem_base);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001647 if (tga_bus_pci) {
1648 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1649 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1650 }
1651 if (tga_bus_tc) {
1652 bar0_start = to_tc_dev(dev)->resource.start;
1653 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1654 }
1655 release_mem_region(bar0_start, bar0_len);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001656 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657}
1658
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001659static void __devexit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660tgafb_exit(void)
1661{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001662 tc_unregister_driver(&tgafb_tc_driver);
1663 pci_unregister_driver(&tgafb_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
1666#ifndef MODULE
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001667static int __devinit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668tgafb_setup(char *arg)
1669{
1670 char *this_opt;
1671
1672 if (arg && *arg) {
1673 while ((this_opt = strsep(&arg, ","))) {
1674 if (!*this_opt)
1675 continue;
1676 if (!strncmp(this_opt, "mode:", 5))
1677 mode_option = this_opt+5;
1678 else
1679 printk(KERN_ERR
1680 "tgafb: unknown parameter %s\n",
1681 this_opt);
1682 }
1683 }
1684
1685 return 0;
1686}
1687#endif /* !MODULE */
1688
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001689static int __devinit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690tgafb_init(void)
1691{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001692 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693#ifndef MODULE
1694 char *option = NULL;
1695
1696 if (fb_get_options("tgafb", &option))
1697 return -ENODEV;
1698 tgafb_setup(option);
1699#endif
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001700 status = pci_register_driver(&tgafb_pci_driver);
1701 if (!status)
1702 status = tc_register_driver(&tgafb_tc_driver);
1703 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704}
1705
1706/*
1707 * Modularisation
1708 */
1709
1710module_init(tgafb_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711module_exit(tgafb_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001713MODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714MODULE_LICENSE("GPL");