blob: 1b3b1c718e80d4cd775b359464a73321ac70a3b6 [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. Rozyckib738b992007-10-16 01:29:56 -07008 * Copyright (C) 2006, 2007 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>
Maciej W. Rozyckib738b992007-10-16 01:29:56 -070016#include <linux/compiler.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/delay.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070018#include <linux/device.h>
19#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/fb.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070021#include <linux/init.h>
22#include <linux/ioport.h>
23#include <linux/kernel.h>
24#include <linux/mm.h>
25#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/pci.h>
27#include <linux/selection.h>
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070028#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 *);
James Simmons28b230e2007-05-08 00:37:50 -070062static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070064static int __devinit tgafb_register(struct device *dev);
65static void __devexit tgafb_unregister(struct device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070067static const char *mode_option;
68static const char *mode_option_pci = "640x480@60";
69static const char *mode_option_tc = "1280x1024@72";
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070072static struct pci_driver tgafb_pci_driver;
73static struct tc_driver tgafb_tc_driver;
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075/*
76 * Frame buffer operations
77 */
78
79static struct fb_ops tgafb_ops = {
80 .owner = THIS_MODULE,
81 .fb_check_var = tgafb_check_var,
82 .fb_set_par = tgafb_set_par,
83 .fb_setcolreg = tgafb_setcolreg,
84 .fb_blank = tgafb_blank,
James Simmons28b230e2007-05-08 00:37:50 -070085 .fb_pan_display = tgafb_pan_display,
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 .fb_fillrect = tgafb_fillrect,
87 .fb_copyarea = tgafb_copyarea,
88 .fb_imageblit = tgafb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -070089};
90
91
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070092#ifdef CONFIG_PCI
Linus Torvalds1da177e2005-04-16 15:20:36 -070093/*
94 * PCI registration operations
95 */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070096static int __devinit tgafb_pci_register(struct pci_dev *,
97 const struct pci_device_id *);
98static void __devexit tgafb_pci_unregister(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070099
100static struct pci_device_id const tgafb_pci_table[] = {
Maciej W. Rozyckifef45902007-02-12 00:54:58 -0800101 { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
102 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103};
Maciej W. Rozyckifef45902007-02-12 00:54:58 -0800104MODULE_DEVICE_TABLE(pci, tgafb_pci_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700106static struct pci_driver tgafb_pci_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 .name = "tgafb",
108 .id_table = tgafb_pci_table,
109 .probe = tgafb_pci_register,
110 .remove = __devexit_p(tgafb_pci_unregister),
111};
112
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700113static int __devinit
114tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
115{
116 return tgafb_register(&pdev->dev);
117}
118
119static void __devexit
120tgafb_pci_unregister(struct pci_dev *pdev)
121{
122 tgafb_unregister(&pdev->dev);
123}
124#endif /* CONFIG_PCI */
125
126#ifdef CONFIG_TC
127/*
128 * TC registration operations
129 */
130static int __devinit tgafb_tc_register(struct device *);
131static int __devexit tgafb_tc_unregister(struct device *);
132
133static struct tc_device_id const tgafb_tc_table[] = {
134 { "DEC ", "PMAGD-AA" },
135 { "DEC ", "PMAGD " },
136 { }
137};
138MODULE_DEVICE_TABLE(tc, tgafb_tc_table);
139
140static struct tc_driver tgafb_tc_driver = {
141 .id_table = tgafb_tc_table,
142 .driver = {
143 .name = "tgafb",
144 .bus = &tc_bus_type,
145 .probe = tgafb_tc_register,
146 .remove = __devexit_p(tgafb_tc_unregister),
147 },
148};
149
150static int __devinit
151tgafb_tc_register(struct device *dev)
152{
153 int status = tgafb_register(dev);
154 if (!status)
155 get_device(dev);
156 return status;
157}
158
159static int __devexit
160tgafb_tc_unregister(struct device *dev)
161{
162 put_device(dev);
163 tgafb_unregister(dev);
164 return 0;
165}
166#endif /* CONFIG_TC */
167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169/**
170 * tgafb_check_var - Optional function. Validates a var passed in.
171 * @var: frame buffer variable screen structure
172 * @info: frame buffer structure that represents a single frame buffer
173 */
174static int
175tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
176{
177 struct tga_par *par = (struct tga_par *)info->par;
178
179 if (par->tga_type == TGA_TYPE_8PLANE) {
180 if (var->bits_per_pixel != 8)
181 return -EINVAL;
182 } else {
183 if (var->bits_per_pixel != 32)
184 return -EINVAL;
185 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800186 var->red.length = var->green.length = var->blue.length = 8;
187 if (var->bits_per_pixel == 32) {
188 var->red.offset = 16;
189 var->green.offset = 8;
190 var->blue.offset = 0;
191 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
193 if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
194 return -EINVAL;
195 if (var->nonstd)
196 return -EINVAL;
197 if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
198 return -EINVAL;
199 if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
200 return -EINVAL;
201
202 /* Some of the acceleration routines assume the line width is
203 a multiple of 64 bytes. */
204 if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 64)
205 return -EINVAL;
206
207 return 0;
208}
209
210/**
211 * tgafb_set_par - Optional function. Alters the hardware state.
212 * @info: frame buffer structure that represents a single frame buffer
213 */
214static int
215tgafb_set_par(struct fb_info *info)
216{
217 static unsigned int const deep_presets[4] = {
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700218 0x00004000,
219 0x0000440d,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 0xffffffff,
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700221 0x0000441d
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 };
223 static unsigned int const rasterop_presets[4] = {
224 0x00000003,
225 0x00000303,
226 0xffffffff,
227 0x00000303
228 };
229 static unsigned int const mode_presets[4] = {
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800230 0x00000000,
231 0x00000300,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 0xffffffff,
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800233 0x00000300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 };
235 static unsigned int const base_addr_presets[4] = {
236 0x00000000,
237 0x00000001,
238 0xffffffff,
239 0x00000001
240 };
241
242 struct tga_par *par = (struct tga_par *) info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700243 int tga_bus_pci = TGA_BUS_PCI(par->dev);
244 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 u32 htimings, vtimings, pll_freq;
246 u8 tga_type;
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800247 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 /* Encode video timings. */
250 htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB)
251 | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB));
252 vtimings = (info->var.yres & TGA_VERT_ACTIVE);
253 htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP;
254 vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP;
255 htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC;
256 vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC;
257 htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP;
258 vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP;
259
260 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
261 htimings |= TGA_HORIZ_POLARITY;
262 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
263 vtimings |= TGA_VERT_POLARITY;
264
265 par->htimings = htimings;
266 par->vtimings = vtimings;
267
268 par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN);
269
270 /* Store other useful values in par. */
271 par->xres = info->var.xres;
272 par->yres = info->var.yres;
273 par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
274 par->bits_per_pixel = info->var.bits_per_pixel;
275
276 tga_type = par->tga_type;
277
278 /* First, disable video. */
279 TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG);
280
281 /* Write the DEEP register. */
282 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
283 continue;
284 mb();
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800285 TGA_WRITE_REG(par, deep_presets[tga_type] |
286 (par->sync_on_green ? 0x0 : 0x00010000),
287 TGA_DEEP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
289 continue;
290 mb();
291
292 /* Write some more registers. */
293 TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG);
294 TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG);
295 TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
296
297 /* Calculate & write the PLL. */
298 tgafb_set_pll(par, pll_freq);
299
300 /* Write some more registers. */
301 TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG);
302 TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG);
303
304 /* Init video timing regs. */
305 TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG);
306 TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
307
308 /* Initalise RAMDAC. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700309 if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 /* Init BT485 RAMDAC registers. */
312 BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
313 BT485_CMD_0);
314 BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE);
315 BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */
316 BT485_WRITE(par, 0x40, BT485_CMD_1);
317 BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */
318 BT485_WRITE(par, 0xff, BT485_PIXEL_MASK);
319
320 /* Fill palette registers. */
321 BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE);
322 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
323
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800324 for (i = 0; i < 256 * 3; i += 4) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800325 TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800327 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800329 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800331 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 TGA_RAMDAC_REG);
333 }
334
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700335 } else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
336
337 /* Init BT459 RAMDAC registers. */
338 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
339 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
340 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
341 (par->sync_on_green ? 0xc0 : 0x40));
342
343 BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
344
345 /* Fill the palette. */
346 BT459_LOAD_ADDR(par, 0x0000);
347 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
348
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700349 for (i = 0; i < 256 * 3; i += 4) {
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700350 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
351 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
352 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
353 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
354 }
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 } else { /* 24-plane or 24plusZ */
357
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800358 /* Init BT463 RAMDAC registers. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
360 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
361 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2,
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800362 (par->sync_on_green ? 0xc0 : 0x40));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
364 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
365 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
366 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
367 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
368
369 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
370 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
371 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
372 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
373
374 /* Fill the palette. */
375 BT463_LOAD_ADDR(par, 0x0000);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800376 TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800378#ifdef CONFIG_HW_CONSOLE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800380 int j = color_table[i];
381
382 TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
383 TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
384 TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800386 for (i = 0; i < 512 * 3; i += 4) {
387#else
388 for (i = 0; i < 528 * 3; i += 4) {
389#endif
390 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
391 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
392 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
393 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 }
395
396 /* Fill window type table after start of vertical retrace. */
397 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
398 continue;
399 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
400 mb();
401 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
402 continue;
403 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
404
405 BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800406 TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800409 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
410 TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG);
411 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
413
414 }
415
416 /* Finally, enable video scan (and pray for the monitor... :-) */
417 TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG);
418
419 return 0;
420}
421
422#define DIFFCHECK(X) \
423do { \
424 if (m <= 0x3f) { \
425 int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \
426 if (delta < 0) \
427 delta = -delta; \
428 if (delta < min_diff) \
429 min_diff = delta, vm = m, va = a, vr = r; \
430 } \
431} while (0)
432
433static void
434tgafb_set_pll(struct tga_par *par, int f)
435{
436 int n, shift, base, min_diff, target;
437 int r,a,m,vm = 34, va = 1, vr = 30;
438
439 for (r = 0 ; r < 12 ; r++)
440 TGA_WRITE_REG(par, !r, TGA_CLOCK_REG);
441
442 if (f > TGA_PLL_MAX_FREQ)
443 f = TGA_PLL_MAX_FREQ;
444
445 if (f >= TGA_PLL_MAX_FREQ / 2)
446 shift = 0;
447 else if (f >= TGA_PLL_MAX_FREQ / 4)
448 shift = 1;
449 else
450 shift = 2;
451
452 TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG);
453 TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG);
454
455 for (r = 0 ; r < 10 ; r++)
456 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
457
458 if (f <= 120000) {
459 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
460 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
461 }
462 else if (f <= 200000) {
463 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
464 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
465 }
466 else {
467 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
468 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
469 }
470
471 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
472 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
473 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
474 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
475 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
476 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
477
478 target = (f << shift) / TGA_PLL_BASE_FREQ;
479 min_diff = TGA_PLL_MAX_FREQ;
480
481 r = 7 / target;
482 if (!r) r = 1;
483
484 base = target * r;
485 while (base < 449) {
486 for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) {
487 m = ((n + 3) / 7) - 1;
488 a = 0;
489 DIFFCHECK((m + 1) * 7);
490 m++;
491 DIFFCHECK((m + 1) * 7);
492 m = (n / 6) - 1;
493 if ((a = n % 6))
494 DIFFCHECK(n);
495 }
496 r++;
497 base += target;
498 }
499
500 vr--;
501
502 for (r = 0; r < 8; r++)
503 TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG);
504 for (r = 0; r < 8 ; r++)
505 TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG);
506 for (r = 0; r < 7 ; r++)
507 TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG);
508 TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG);
509}
510
511
512/**
513 * tgafb_setcolreg - Optional function. Sets a color register.
514 * @regno: boolean, 0 copy local, 1 get_user() function
515 * @red: frame buffer colormap structure
516 * @green: The green value which can be up to 16 bits wide
517 * @blue: The blue value which can be up to 16 bits wide.
518 * @transp: If supported the alpha value which can be up to 16 bits wide.
519 * @info: frame buffer info structure
520 */
521static int
522tgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
523 unsigned transp, struct fb_info *info)
524{
525 struct tga_par *par = (struct tga_par *) info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700526 int tga_bus_pci = TGA_BUS_PCI(par->dev);
527 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 if (regno > 255)
530 return 1;
531 red >>= 8;
532 green >>= 8;
533 blue >>= 8;
534
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700535 if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
537 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
538 TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
539 TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
540 TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700541 } else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
542 BT459_LOAD_ADDR(par, regno);
543 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
544 TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
545 TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
546 TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800547 } else {
548 if (regno < 16) {
549 u32 value = (regno << 16) | (regno << 8) | regno;
550 ((u32 *)info->pseudo_palette)[regno] = value;
551 }
552 BT463_LOAD_ADDR(par, regno);
553 TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
554 TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
555 TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
556 TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 }
558
559 return 0;
560}
561
562
563/**
564 * tgafb_blank - Optional function. Blanks the display.
565 * @blank_mode: the blank mode we want.
566 * @info: frame buffer structure that represents a single frame buffer
567 */
568static int
569tgafb_blank(int blank, struct fb_info *info)
570{
571 struct tga_par *par = (struct tga_par *) info->par;
572 u32 vhcr, vvcr, vvvr;
573 unsigned long flags;
574
575 local_irq_save(flags);
576
577 vhcr = TGA_READ_REG(par, TGA_HORIZ_REG);
578 vvcr = TGA_READ_REG(par, TGA_VERT_REG);
579 vvvr = TGA_READ_REG(par, TGA_VALID_REG);
580 vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK);
581
582 switch (blank) {
583 case FB_BLANK_UNBLANK: /* Unblanking */
584 if (par->vesa_blanked) {
585 TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG);
586 TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG);
587 par->vesa_blanked = 0;
588 }
589 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG);
590 break;
591
592 case FB_BLANK_NORMAL: /* Normal blanking */
593 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK,
594 TGA_VALID_REG);
595 break;
596
597 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
598 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
599 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
600 par->vesa_blanked = 1;
601 break;
602
603 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
604 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
605 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
606 par->vesa_blanked = 1;
607 break;
608
609 case FB_BLANK_POWERDOWN: /* Poweroff */
610 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
611 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
612 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
613 par->vesa_blanked = 1;
614 break;
615 }
616
617 local_irq_restore(flags);
618 return 0;
619}
620
621
622/*
623 * Acceleration.
624 */
625
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626static void
James Simmons28b230e2007-05-08 00:37:50 -0700627tgafb_mono_imageblit(struct fb_info *info, const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 struct tga_par *par = (struct tga_par *) info->par;
630 u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask;
631 unsigned long rincr, line_length, shift, pos, is8bpp;
632 unsigned long i, j;
633 const unsigned char *data;
634 void __iomem *regs_base;
635 void __iomem *fb_base;
636
James Simmons28b230e2007-05-08 00:37:50 -0700637 is8bpp = info->var.bits_per_pixel == 8;
638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 dx = image->dx;
640 dy = image->dy;
641 width = image->width;
642 height = image->height;
643 vxres = info->var.xres_virtual;
644 vyres = info->var.yres_virtual;
645 line_length = info->fix.line_length;
646 rincr = (width + 7) / 8;
647
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700648 /* A shift below cannot cope with. */
649 if (unlikely(width == 0))
650 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 /* Crop the image to the screen. */
652 if (dx > vxres || dy > vyres)
653 return;
654 if (dx + width > vxres)
655 width = vxres - dx;
656 if (dy + height > vyres)
657 height = vyres - dy;
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 regs_base = par->tga_regs_base;
660 fb_base = par->tga_fb_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
662 /* Expand the color values to fill 32-bits. */
663 /* ??? Would be nice to notice colour changes elsewhere, so
664 that we can do this only when necessary. */
665 fgcolor = image->fg_color;
666 bgcolor = image->bg_color;
667 if (is8bpp) {
668 fgcolor |= fgcolor << 8;
669 fgcolor |= fgcolor << 16;
670 bgcolor |= bgcolor << 8;
671 bgcolor |= bgcolor << 16;
672 } else {
673 if (fgcolor < 16)
674 fgcolor = ((u32 *)info->pseudo_palette)[fgcolor];
675 if (bgcolor < 16)
676 bgcolor = ((u32 *)info->pseudo_palette)[bgcolor];
677 }
678 __raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG);
679 __raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG);
680
681 /* Acquire proper alignment; set up the PIXELMASK register
682 so that we only write the proper character cell. */
683 pos = dy * line_length;
684 if (is8bpp) {
685 pos += dx;
686 shift = pos & 3;
687 pos &= -4;
688 } else {
689 pos += dx * 4;
690 shift = (pos & 7) >> 2;
691 pos &= -8;
692 }
693
694 data = (const unsigned char *) image->data;
695
696 /* Enable opaque stipple mode. */
697 __raw_writel((is8bpp
698 ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE
699 : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE),
700 regs_base + TGA_MODE_REG);
701
702 if (width + shift <= 32) {
703 unsigned long bwidth;
704
705 /* Handle common case of imaging a single character, in
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700706 a font less than or 32 pixels wide. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700708 /* Avoid a shift by 32; width > 0 implied. */
709 pixelmask = (2ul << (width - 1)) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 pixelmask <<= shift;
711 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
712 wmb();
713
714 bwidth = (width + 7) / 8;
715
716 for (i = 0; i < height; ++i) {
717 u32 mask = 0;
718
719 /* The image data is bit big endian; we need
720 little endian. */
721 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800722 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 __raw_writel(mask << shift, fb_base + pos);
725
726 pos += line_length;
727 data += rincr;
728 }
729 wmb();
730 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
731 } else if (shift == 0) {
732 unsigned long pos0 = pos;
733 const unsigned char *data0 = data;
734 unsigned long bincr = (is8bpp ? 8 : 8*4);
735 unsigned long bwidth;
736
737 /* Handle another common case in which accel_putcs
738 generates a large bitmap, which happens to be aligned.
739 Allow the tail to be misaligned. This case is
740 interesting because we've not got to hold partial
741 bytes across the words being written. */
742
743 wmb();
744
745 bwidth = (width / 8) & -4;
746 for (i = 0; i < height; ++i) {
747 for (j = 0; j < bwidth; j += 4) {
748 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800749 mask |= bitrev8(data[j+0]) << (0 * 8);
750 mask |= bitrev8(data[j+1]) << (1 * 8);
751 mask |= bitrev8(data[j+2]) << (2 * 8);
752 mask |= bitrev8(data[j+3]) << (3 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 __raw_writel(mask, fb_base + pos + j*bincr);
754 }
755 pos += line_length;
756 data += rincr;
757 }
758 wmb();
759
760 pixelmask = (1ul << (width & 31)) - 1;
761 if (pixelmask) {
762 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
763 wmb();
764
765 pos = pos0 + bwidth*bincr;
766 data = data0 + bwidth;
767 bwidth = ((width & 31) + 7) / 8;
768
769 for (i = 0; i < height; ++i) {
770 u32 mask = 0;
771 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800772 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 __raw_writel(mask, fb_base + pos);
774 pos += line_length;
775 data += rincr;
776 }
777 wmb();
778 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
779 }
780 } else {
781 unsigned long pos0 = pos;
782 const unsigned char *data0 = data;
783 unsigned long bincr = (is8bpp ? 8 : 8*4);
784 unsigned long bwidth;
785
786 /* Finally, handle the generic case of misaligned start.
787 Here we split the write into 16-bit spans. This allows
788 us to use only one pixel mask, instead of four as would
789 be required by writing 24-bit spans. */
790
791 pixelmask = 0xffff << shift;
792 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
793 wmb();
794
795 bwidth = (width / 8) & -2;
796 for (i = 0; i < height; ++i) {
797 for (j = 0; j < bwidth; j += 2) {
798 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800799 mask |= bitrev8(data[j+0]) << (0 * 8);
800 mask |= bitrev8(data[j+1]) << (1 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 mask <<= shift;
802 __raw_writel(mask, fb_base + pos + j*bincr);
803 }
804 pos += line_length;
805 data += rincr;
806 }
807 wmb();
808
809 pixelmask = ((1ul << (width & 15)) - 1) << shift;
810 if (pixelmask) {
811 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
812 wmb();
813
814 pos = pos0 + bwidth*bincr;
815 data = data0 + bwidth;
816 bwidth = (width & 15) > 8;
817
818 for (i = 0; i < height; ++i) {
Akinobu Mita1c667682006-12-08 02:36:26 -0800819 u32 mask = bitrev8(data[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 if (bwidth)
Akinobu Mita1c667682006-12-08 02:36:26 -0800821 mask |= bitrev8(data[1]) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 mask <<= shift;
823 __raw_writel(mask, fb_base + pos);
824 pos += line_length;
825 data += rincr;
826 }
827 wmb();
828 }
829 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
830 }
831
832 /* Disable opaque stipple mode. */
833 __raw_writel((is8bpp
834 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
835 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
836 regs_base + TGA_MODE_REG);
837}
838
James Simmons28b230e2007-05-08 00:37:50 -0700839static void
840tgafb_clut_imageblit(struct fb_info *info, const struct fb_image *image)
841{
842 struct tga_par *par = (struct tga_par *) info->par;
843 u32 color, dx, dy, width, height, vxres, vyres;
844 u32 *palette = ((u32 *)info->pseudo_palette);
845 unsigned long pos, line_length, i, j;
846 const unsigned char *data;
Al Viro0bd84962007-07-26 17:36:09 +0100847 void __iomem *regs_base, *fb_base;
James Simmons28b230e2007-05-08 00:37:50 -0700848
849 dx = image->dx;
850 dy = image->dy;
851 width = image->width;
852 height = image->height;
853 vxres = info->var.xres_virtual;
854 vyres = info->var.yres_virtual;
855 line_length = info->fix.line_length;
856
857 /* Crop the image to the screen. */
858 if (dx > vxres || dy > vyres)
859 return;
860 if (dx + width > vxres)
861 width = vxres - dx;
862 if (dy + height > vyres)
863 height = vyres - dy;
864
865 regs_base = par->tga_regs_base;
866 fb_base = par->tga_fb_base;
867
868 pos = dy * line_length + (dx * 4);
869 data = image->data;
870
871 /* Now copy the image, color_expanding via the palette. */
872 for (i = 0; i < height; i++) {
873 for (j = 0; j < width; j++) {
874 color = palette[*data++];
875 __raw_writel(color, fb_base + pos + j*4);
876 }
877 pos += line_length;
878 }
879}
880
881/**
882 * tgafb_imageblit - REQUIRED function. Can use generic routines if
883 * non acclerated hardware and packed pixel based.
884 * Copies a image from system memory to the screen.
885 *
886 * @info: frame buffer structure that represents a single frame buffer
887 * @image: structure defining the image.
888 */
889static void
890tgafb_imageblit(struct fb_info *info, const struct fb_image *image)
891{
892 unsigned int is8bpp = info->var.bits_per_pixel == 8;
893
894 /* If a mono image, regardless of FB depth, go do it. */
895 if (image->depth == 1) {
896 tgafb_mono_imageblit(info, image);
897 return;
898 }
899
900 /* For copies that aren't pixel expansion, there's little we
901 can do better than the generic code. */
902 /* ??? There is a DMA write mode; I wonder if that could be
903 made to pull the data from the image buffer... */
904 if (image->depth == info->var.bits_per_pixel) {
905 cfb_imageblit(info, image);
906 return;
907 }
908
909 /* If 24-plane FB and the image is 8-plane with CLUT, we can do it. */
910 if (!is8bpp && image->depth == 8) {
911 tgafb_clut_imageblit(info, image);
912 return;
913 }
914
915 /* Silently return... */
916}
917
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918/**
919 * tgafb_fillrect - REQUIRED function. Can use generic routines if
920 * non acclerated hardware and packed pixel based.
921 * Draws a rectangle on the screen.
922 *
923 * @info: frame buffer structure that represents a single frame buffer
924 * @rect: structure defining the rectagle and operation.
925 */
926static void
927tgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
928{
929 struct tga_par *par = (struct tga_par *) info->par;
930 int is8bpp = info->var.bits_per_pixel == 8;
931 u32 dx, dy, width, height, vxres, vyres, color;
932 unsigned long pos, align, line_length, i, j;
933 void __iomem *regs_base;
934 void __iomem *fb_base;
935
936 dx = rect->dx;
937 dy = rect->dy;
938 width = rect->width;
939 height = rect->height;
940 vxres = info->var.xres_virtual;
941 vyres = info->var.yres_virtual;
942 line_length = info->fix.line_length;
943 regs_base = par->tga_regs_base;
944 fb_base = par->tga_fb_base;
945
946 /* Crop the rectangle to the screen. */
947 if (dx > vxres || dy > vyres || !width || !height)
948 return;
949 if (dx + width > vxres)
950 width = vxres - dx;
951 if (dy + height > vyres)
952 height = vyres - dy;
953
954 pos = dy * line_length + dx * (is8bpp ? 1 : 4);
955
956 /* ??? We could implement ROP_XOR with opaque fill mode
957 and a RasterOp setting of GXxor, but as far as I can
958 tell, this mode is not actually used in the kernel.
959 Thus I am ignoring it for now. */
960 if (rect->rop != ROP_COPY) {
961 cfb_fillrect(info, rect);
962 return;
963 }
964
965 /* Expand the color value to fill 8 pixels. */
966 color = rect->color;
967 if (is8bpp) {
968 color |= color << 8;
969 color |= color << 16;
970 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
971 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
972 } else {
973 if (color < 16)
974 color = ((u32 *)info->pseudo_palette)[color];
975 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
976 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
977 __raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG);
978 __raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG);
979 __raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG);
980 __raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG);
981 __raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG);
982 __raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG);
983 }
984
985 /* The DATA register holds the fill mask for block fill mode.
986 Since we're not stippling, this is all ones. */
987 __raw_writel(0xffffffff, regs_base + TGA_DATA_REG);
988
989 /* Enable block fill mode. */
990 __raw_writel((is8bpp
991 ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL
992 : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL),
993 regs_base + TGA_MODE_REG);
994 wmb();
995
996 /* We can fill 2k pixels per operation. Notice blocks that fit
997 the width of the screen so that we can take advantage of this
998 and fill more than one line per write. */
999 if (width == line_length)
1000 width *= height, height = 1;
1001
1002 /* The write into the frame buffer must be aligned to 4 bytes,
1003 but we are allowed to encode the offset within the word in
1004 the data word written. */
1005 align = (pos & 3) << 16;
1006 pos &= -4;
1007
1008 if (width <= 2048) {
1009 u32 data;
1010
1011 data = (width - 1) | align;
1012
1013 for (i = 0; i < height; ++i) {
1014 __raw_writel(data, fb_base + pos);
1015 pos += line_length;
1016 }
1017 } else {
1018 unsigned long Bpp = (is8bpp ? 1 : 4);
1019 unsigned long nwidth = width & -2048;
1020 u32 fdata, ldata;
1021
1022 fdata = (2048 - 1) | align;
1023 ldata = ((width & 2047) - 1) | align;
1024
1025 for (i = 0; i < height; ++i) {
1026 for (j = 0; j < nwidth; j += 2048)
1027 __raw_writel(fdata, fb_base + pos + j*Bpp);
1028 if (j < width)
1029 __raw_writel(ldata, fb_base + pos + j*Bpp);
1030 pos += line_length;
1031 }
1032 }
1033 wmb();
1034
1035 /* Disable block fill mode. */
1036 __raw_writel((is8bpp
1037 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
1038 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
1039 regs_base + TGA_MODE_REG);
1040}
1041
1042/**
1043 * tgafb_copyarea - REQUIRED function. Can use generic routines if
1044 * non acclerated hardware and packed pixel based.
1045 * Copies on area of the screen to another area.
1046 *
1047 * @info: frame buffer structure that represents a single frame buffer
1048 * @area: structure defining the source and destination.
1049 */
1050
1051/* Handle the special case of copying entire lines, e.g. during scrolling.
1052 We can avoid a lot of needless computation in this case. In the 8bpp
1053 case we need to use the COPY64 registers instead of mask writes into
1054 the frame buffer to achieve maximum performance. */
1055
1056static inline void
1057copyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy,
1058 u32 height, u32 width)
1059{
1060 struct tga_par *par = (struct tga_par *) info->par;
1061 void __iomem *tga_regs = par->tga_regs_base;
1062 unsigned long dpos, spos, i, n64;
1063
1064 /* Set up the MODE and PIXELSHIFT registers. */
1065 __raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1066 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1067 wmb();
1068
1069 n64 = (height * width) / 64;
1070
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001071 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 spos = (sy + height) * width;
1073 dpos = (dy + height) * width;
1074
1075 for (i = 0; i < n64; ++i) {
1076 spos -= 64;
1077 dpos -= 64;
1078 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1079 wmb();
1080 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1081 wmb();
1082 }
1083 } else {
1084 spos = sy * width;
1085 dpos = dy * width;
1086
1087 for (i = 0; i < n64; ++i) {
1088 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1089 wmb();
1090 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1091 wmb();
1092 spos += 64;
1093 dpos += 64;
1094 }
1095 }
1096
1097 /* Reset the MODE register to normal. */
1098 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1099}
1100
1101static inline void
1102copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
1103 u32 height, u32 width)
1104{
1105 struct tga_par *par = (struct tga_par *) info->par;
1106 void __iomem *tga_regs = par->tga_regs_base;
1107 void __iomem *tga_fb = par->tga_fb_base;
1108 void __iomem *src;
1109 void __iomem *dst;
1110 unsigned long i, n16;
1111
1112 /* Set up the MODE and PIXELSHIFT registers. */
1113 __raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1114 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1115 wmb();
1116
1117 n16 = (height * width) / 16;
1118
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001119 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 src = tga_fb + (sy + height) * width * 4;
1121 dst = tga_fb + (dy + height) * width * 4;
1122
1123 for (i = 0; i < n16; ++i) {
1124 src -= 64;
1125 dst -= 64;
1126 __raw_writel(0xffff, src);
1127 wmb();
1128 __raw_writel(0xffff, dst);
1129 wmb();
1130 }
1131 } else {
1132 src = tga_fb + sy * width * 4;
1133 dst = tga_fb + dy * width * 4;
1134
1135 for (i = 0; i < n16; ++i) {
1136 __raw_writel(0xffff, src);
1137 wmb();
1138 __raw_writel(0xffff, dst);
1139 wmb();
1140 src += 64;
1141 dst += 64;
1142 }
1143 }
1144
1145 /* Reset the MODE register to normal. */
1146 __raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1147}
1148
1149/* The general case of forward copy in 8bpp mode. */
1150static inline void
1151copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1152 u32 height, u32 width, u32 line_length)
1153{
1154 struct tga_par *par = (struct tga_par *) info->par;
1155 unsigned long i, copied, left;
1156 unsigned long dpos, spos, dalign, salign, yincr;
1157 u32 smask_first, dmask_first, dmask_last;
1158 int pixel_shift, need_prime, need_second;
1159 unsigned long n64, n32, xincr_first;
1160 void __iomem *tga_regs;
1161 void __iomem *tga_fb;
1162
1163 yincr = line_length;
1164 if (dy > sy) {
1165 dy += height - 1;
1166 sy += height - 1;
1167 yincr = -yincr;
1168 }
1169
1170 /* Compute the offsets and alignments in the frame buffer.
1171 More than anything else, these control how we do copies. */
1172 dpos = dy * line_length + dx;
1173 spos = sy * line_length + sx;
1174 dalign = dpos & 7;
1175 salign = spos & 7;
1176 dpos &= -8;
1177 spos &= -8;
1178
1179 /* Compute the value for the PIXELSHIFT register. This controls
1180 both non-co-aligned source and destination and copy direction. */
1181 if (dalign >= salign)
1182 pixel_shift = dalign - salign;
1183 else
1184 pixel_shift = 8 - (salign - dalign);
1185
1186 /* Figure out if we need an additional priming step for the
1187 residue register. */
1188 need_prime = (salign > dalign);
1189 if (need_prime)
1190 dpos -= 8;
1191
1192 /* Begin by copying the leading unaligned destination. Copy enough
1193 to make the next destination address 32-byte aligned. */
1194 copied = 32 - (dalign + (dpos & 31));
1195 if (copied == 32)
1196 copied = 0;
1197 xincr_first = (copied + 7) & -8;
1198 smask_first = dmask_first = (1ul << copied) - 1;
1199 smask_first <<= salign;
1200 dmask_first <<= dalign + need_prime*8;
1201 if (need_prime && copied > 24)
1202 copied -= 8;
1203 left = width - copied;
1204
1205 /* Care for small copies. */
1206 if (copied > width) {
1207 u32 t;
1208 t = (1ul << width) - 1;
1209 t <<= dalign + need_prime*8;
1210 dmask_first &= t;
1211 left = 0;
1212 }
1213
1214 /* Attempt to use 64-byte copies. This is only possible if the
1215 source and destination are co-aligned at 64 bytes. */
1216 n64 = need_second = 0;
1217 if ((dpos & 63) == (spos & 63)
1218 && (height == 1 || line_length % 64 == 0)) {
1219 /* We may need a 32-byte copy to ensure 64 byte alignment. */
1220 need_second = (dpos + xincr_first) & 63;
1221 if ((need_second & 32) != need_second)
1222 printk(KERN_ERR "tgafb: need_second wrong\n");
1223 if (left >= need_second + 64) {
1224 left -= need_second;
1225 n64 = left / 64;
1226 left %= 64;
1227 } else
1228 need_second = 0;
1229 }
1230
1231 /* Copy trailing full 32-byte sections. This will be the main
1232 loop if the 64 byte loop can't be used. */
1233 n32 = left / 32;
1234 left %= 32;
1235
1236 /* Copy the trailing unaligned destination. */
1237 dmask_last = (1ul << left) - 1;
1238
1239 tga_regs = par->tga_regs_base;
1240 tga_fb = par->tga_fb_base;
1241
1242 /* Set up the MODE and PIXELSHIFT registers. */
1243 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1244 __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
1245 wmb();
1246
1247 for (i = 0; i < height; ++i) {
1248 unsigned long j;
1249 void __iomem *sfb;
1250 void __iomem *dfb;
1251
1252 sfb = tga_fb + spos;
1253 dfb = tga_fb + dpos;
1254 if (dmask_first) {
1255 __raw_writel(smask_first, sfb);
1256 wmb();
1257 __raw_writel(dmask_first, dfb);
1258 wmb();
1259 sfb += xincr_first;
1260 dfb += xincr_first;
1261 }
1262
1263 if (need_second) {
1264 __raw_writel(0xffffffff, sfb);
1265 wmb();
1266 __raw_writel(0xffffffff, dfb);
1267 wmb();
1268 sfb += 32;
1269 dfb += 32;
1270 }
1271
1272 if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
1273 printk(KERN_ERR
1274 "tgafb: misaligned copy64 (s:%p, d:%p)\n",
1275 sfb, dfb);
1276
1277 for (j = 0; j < n64; ++j) {
1278 __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1279 wmb();
1280 __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1281 wmb();
1282 sfb += 64;
1283 dfb += 64;
1284 }
1285
1286 for (j = 0; j < n32; ++j) {
1287 __raw_writel(0xffffffff, sfb);
1288 wmb();
1289 __raw_writel(0xffffffff, dfb);
1290 wmb();
1291 sfb += 32;
1292 dfb += 32;
1293 }
1294
1295 if (dmask_last) {
1296 __raw_writel(0xffffffff, sfb);
1297 wmb();
1298 __raw_writel(dmask_last, dfb);
1299 wmb();
1300 }
1301
1302 spos += yincr;
1303 dpos += yincr;
1304 }
1305
1306 /* Reset the MODE register to normal. */
1307 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1308}
1309
1310/* The (almost) general case of backward copy in 8bpp mode. */
1311static inline void
1312copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1313 u32 height, u32 width, u32 line_length,
1314 const struct fb_copyarea *area)
1315{
1316 struct tga_par *par = (struct tga_par *) info->par;
1317 unsigned long i, left, yincr;
1318 unsigned long depos, sepos, dealign, sealign;
1319 u32 mask_first, mask_last;
1320 unsigned long n32;
1321 void __iomem *tga_regs;
1322 void __iomem *tga_fb;
1323
1324 yincr = line_length;
1325 if (dy > sy) {
1326 dy += height - 1;
1327 sy += height - 1;
1328 yincr = -yincr;
1329 }
1330
1331 /* Compute the offsets and alignments in the frame buffer.
1332 More than anything else, these control how we do copies. */
1333 depos = dy * line_length + dx + width;
1334 sepos = sy * line_length + sx + width;
1335 dealign = depos & 7;
1336 sealign = sepos & 7;
1337
1338 /* ??? The documentation appears to be incorrect (or very
1339 misleading) wrt how pixel shifting works in backward copy
1340 mode, i.e. when PIXELSHIFT is negative. I give up for now.
1341 Do handle the common case of co-aligned backward copies,
1342 but frob everything else back on generic code. */
1343 if (dealign != sealign) {
1344 cfb_copyarea(info, area);
1345 return;
1346 }
1347
1348 /* We begin the copy with the trailing pixels of the
1349 unaligned destination. */
1350 mask_first = (1ul << dealign) - 1;
1351 left = width - dealign;
1352
1353 /* Care for small copies. */
1354 if (dealign > width) {
1355 mask_first ^= (1ul << (dealign - width)) - 1;
1356 left = 0;
1357 }
1358
1359 /* Next copy full words at a time. */
1360 n32 = left / 32;
1361 left %= 32;
1362
1363 /* Finally copy the unaligned head of the span. */
1364 mask_last = -1 << (32 - left);
1365
1366 tga_regs = par->tga_regs_base;
1367 tga_fb = par->tga_fb_base;
1368
1369 /* Set up the MODE and PIXELSHIFT registers. */
1370 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1371 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1372 wmb();
1373
1374 for (i = 0; i < height; ++i) {
1375 unsigned long j;
1376 void __iomem *sfb;
1377 void __iomem *dfb;
1378
1379 sfb = tga_fb + sepos;
1380 dfb = tga_fb + depos;
1381 if (mask_first) {
1382 __raw_writel(mask_first, sfb);
1383 wmb();
1384 __raw_writel(mask_first, dfb);
1385 wmb();
1386 }
1387
1388 for (j = 0; j < n32; ++j) {
1389 sfb -= 32;
1390 dfb -= 32;
1391 __raw_writel(0xffffffff, sfb);
1392 wmb();
1393 __raw_writel(0xffffffff, dfb);
1394 wmb();
1395 }
1396
1397 if (mask_last) {
1398 sfb -= 32;
1399 dfb -= 32;
1400 __raw_writel(mask_last, sfb);
1401 wmb();
1402 __raw_writel(mask_last, dfb);
1403 wmb();
1404 }
1405
1406 sepos += yincr;
1407 depos += yincr;
1408 }
1409
1410 /* Reset the MODE register to normal. */
1411 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1412}
1413
1414static void
1415tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
1416{
1417 unsigned long dx, dy, width, height, sx, sy, vxres, vyres;
1418 unsigned long line_length, bpp;
1419
1420 dx = area->dx;
1421 dy = area->dy;
1422 width = area->width;
1423 height = area->height;
1424 sx = area->sx;
1425 sy = area->sy;
1426 vxres = info->var.xres_virtual;
1427 vyres = info->var.yres_virtual;
1428 line_length = info->fix.line_length;
1429
1430 /* The top left corners must be in the virtual screen. */
1431 if (dx > vxres || sx > vxres || dy > vyres || sy > vyres)
1432 return;
1433
1434 /* Clip the destination. */
1435 if (dx + width > vxres)
1436 width = vxres - dx;
1437 if (dy + height > vyres)
1438 height = vyres - dy;
1439
1440 /* The source must be completely inside the virtual screen. */
1441 if (sx + width > vxres || sy + height > vyres)
1442 return;
1443
1444 bpp = info->var.bits_per_pixel;
1445
1446 /* Detect copies of the entire line. */
1447 if (width * (bpp >> 3) == line_length) {
1448 if (bpp == 8)
1449 copyarea_line_8bpp(info, dy, sy, height, width);
1450 else
1451 copyarea_line_32bpp(info, dy, sy, height, width);
1452 }
1453
1454 /* ??? The documentation is unclear to me exactly how the pixelshift
1455 register works in 32bpp mode. Since I don't have hardware to test,
1456 give up for now and fall back on the generic routines. */
1457 else if (bpp == 32)
1458 cfb_copyarea(info, area);
1459
1460 /* Detect overlapping source and destination that requires
1461 a backward copy. */
1462 else if (dy == sy && dx > sx && dx < sx + width)
1463 copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
1464 width, line_length, area);
1465 else
1466 copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
1467 width, line_length);
1468}
1469
1470
1471/*
1472 * Initialisation
1473 */
1474
1475static void
1476tgafb_init_fix(struct fb_info *info)
1477{
1478 struct tga_par *par = (struct tga_par *)info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001479 int tga_bus_pci = TGA_BUS_PCI(par->dev);
1480 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 u8 tga_type = par->tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001482 const char *tga_type_name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
1484 switch (tga_type) {
1485 case TGA_TYPE_8PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001486 if (tga_bus_pci)
1487 tga_type_name = "Digital ZLXp-E1";
1488 if (tga_bus_tc)
1489 tga_type_name = "Digital ZLX-E1";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 break;
1491 case TGA_TYPE_24PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001492 if (tga_bus_pci)
1493 tga_type_name = "Digital ZLXp-E2";
1494 if (tga_bus_tc)
1495 tga_type_name = "Digital ZLX-E2";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 break;
1497 case TGA_TYPE_24PLUSZ:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001498 if (tga_bus_pci)
1499 tga_type_name = "Digital ZLXp-E3";
1500 if (tga_bus_tc)
1501 tga_type_name = "Digital ZLX-E3";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502 break;
1503 default:
1504 tga_type_name = "Unknown";
1505 break;
1506 }
1507
1508 strlcpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
1509
1510 info->fix.type = FB_TYPE_PACKED_PIXELS;
1511 info->fix.type_aux = 0;
1512 info->fix.visual = (tga_type == TGA_TYPE_8PLANE
1513 ? FB_VISUAL_PSEUDOCOLOR
Maciej W. Rozyckibe601182007-02-12 00:54:54 -08001514 : FB_VISUAL_DIRECTCOLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
1516 info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
1517 info->fix.smem_start = (size_t) par->tga_fb_base;
1518 info->fix.smem_len = info->fix.line_length * par->yres;
1519 info->fix.mmio_start = (size_t) par->tga_regs_base;
1520 info->fix.mmio_len = 512;
1521
1522 info->fix.xpanstep = 0;
1523 info->fix.ypanstep = 0;
1524 info->fix.ywrapstep = 0;
1525
1526 info->fix.accel = FB_ACCEL_DEC_TGA;
James Simmons28b230e2007-05-08 00:37:50 -07001527
1528 /*
1529 * These are needed by fb_set_logo_truepalette(), so we
1530 * set them here for 24-plane cards.
1531 */
1532 if (tga_type != TGA_TYPE_8PLANE) {
1533 info->var.red.length = 8;
1534 info->var.green.length = 8;
1535 info->var.blue.length = 8;
1536 info->var.red.offset = 16;
1537 info->var.green.offset = 8;
1538 info->var.blue.offset = 0;
1539 }
1540}
1541
1542static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
1543{
1544 /* We just use this to catch switches out of graphics mode. */
1545 tgafb_set_par(info); /* A bit of overkill for BASE_ADDR reset. */
1546 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547}
1548
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001549static int __devinit
1550tgafb_register(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001552 static const struct fb_videomode modedb_tc = {
1553 /* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
1554 "1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
1555 FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
1556 };
1557
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 static unsigned int const fb_offset_presets[4] = {
1559 TGA_8PLANE_FB_OFFSET,
1560 TGA_24PLANE_FB_OFFSET,
1561 0xffffffff,
1562 TGA_24PLUSZ_FB_OFFSET
1563 };
1564
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001565 const struct fb_videomode *modedb_tga = NULL;
1566 resource_size_t bar0_start = 0, bar0_len = 0;
1567 const char *mode_option_tga = NULL;
1568 int tga_bus_pci = TGA_BUS_PCI(dev);
1569 int tga_bus_tc = TGA_BUS_TC(dev);
1570 unsigned int modedbsize_tga = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571 void __iomem *mem_base;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001572 struct fb_info *info;
1573 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 u8 tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001575 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 /* Enable device in PCI config. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001578 if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
1580 return -ENODEV;
1581 }
1582
1583 /* Allocate the fb and par structures. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001584 info = framebuffer_alloc(sizeof(struct tga_par), dev);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001585 if (!info) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586 printk(KERN_ERR "tgafb: Cannot allocate memory\n");
1587 return -ENOMEM;
1588 }
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001589
1590 par = info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001591 dev_set_drvdata(dev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 /* Request the mem regions. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 ret = -ENODEV;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001595 if (tga_bus_pci) {
1596 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1597 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1598 }
1599 if (tga_bus_tc) {
1600 bar0_start = to_tc_dev(dev)->resource.start;
1601 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1602 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
1604 printk(KERN_ERR "tgafb: cannot reserve FB region\n");
1605 goto err0;
1606 }
1607
1608 /* Map the framebuffer. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001609 mem_base = ioremap_nocache(bar0_start, bar0_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 if (!mem_base) {
1611 printk(KERN_ERR "tgafb: Cannot map MMIO\n");
1612 goto err1;
1613 }
1614
1615 /* Grab info about the card. */
1616 tga_type = (readl(mem_base) >> 12) & 0x0f;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001617 par->dev = dev;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001618 par->tga_mem_base = mem_base;
1619 par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
1620 par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
1621 par->tga_type = tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001622 if (tga_bus_pci)
Auke Kok44c10132007-06-08 15:46:36 -07001623 par->tga_chip_rev = (to_pci_dev(dev))->revision;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001624 if (tga_bus_tc)
1625 par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626
1627 /* Setup framebuffer. */
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001628 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
1629 FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT;
1630 info->fbops = &tgafb_ops;
1631 info->screen_base = par->tga_fb_base;
Antonino A. Daplaseb3daa82007-07-17 04:05:41 -07001632 info->pseudo_palette = par->palette;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
1634 /* This should give a reasonable default video mode. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001635 if (tga_bus_pci) {
1636 mode_option_tga = mode_option_pci;
1637 }
1638 if (tga_bus_tc) {
1639 mode_option_tga = mode_option_tc;
1640 modedb_tga = &modedb_tc;
1641 modedbsize_tga = 1;
1642 }
1643 ret = fb_find_mode(&info->var, info,
1644 mode_option ? mode_option : mode_option_tga,
1645 modedb_tga, modedbsize_tga, NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 tga_type == TGA_TYPE_8PLANE ? 8 : 32);
1647 if (ret == 0 || ret == 4) {
1648 printk(KERN_ERR "tgafb: Could not find valid video mode\n");
1649 ret = -EINVAL;
1650 goto err1;
1651 }
1652
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001653 if (fb_alloc_cmap(&info->cmap, 256, 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 printk(KERN_ERR "tgafb: Could not allocate color map\n");
1655 ret = -ENOMEM;
1656 goto err1;
1657 }
1658
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001659 tgafb_set_par(info);
1660 tgafb_init_fix(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001662 if (register_framebuffer(info) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 printk(KERN_ERR "tgafb: Could not register framebuffer\n");
1664 ret = -EINVAL;
Andres Salomon327fc872009-03-31 15:25:28 -07001665 goto err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 }
1667
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001668 if (tga_bus_pci) {
1669 pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
1670 par->tga_chip_rev);
1671 pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
1672 to_pci_dev(dev)->bus->number,
1673 PCI_SLOT(to_pci_dev(dev)->devfn),
1674 PCI_FUNC(to_pci_dev(dev)->devfn));
1675 }
1676 if (tga_bus_tc)
1677 pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
1678 par->tga_chip_rev);
1679 pr_info("fb%d: %s frame buffer device at 0x%lx\n",
1680 info->node, info->fix.id, (long)bar0_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
1682 return 0;
1683
Andres Salomon327fc872009-03-31 15:25:28 -07001684 err2:
1685 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 err1:
Amol Lade4bf0512006-12-08 02:40:04 -08001687 if (mem_base)
1688 iounmap(mem_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 release_mem_region(bar0_start, bar0_len);
1690 err0:
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001691 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 return ret;
1693}
1694
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001695static void __devexit
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001696tgafb_unregister(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001698 resource_size_t bar0_start = 0, bar0_len = 0;
1699 int tga_bus_pci = TGA_BUS_PCI(dev);
1700 int tga_bus_tc = TGA_BUS_TC(dev);
1701 struct fb_info *info = NULL;
1702 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001704 info = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 if (!info)
1706 return;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001707
1708 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 unregister_framebuffer(info);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001710 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 iounmap(par->tga_mem_base);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001712 if (tga_bus_pci) {
1713 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1714 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1715 }
1716 if (tga_bus_tc) {
1717 bar0_start = to_tc_dev(dev)->resource.start;
1718 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1719 }
1720 release_mem_region(bar0_start, bar0_len);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001721 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722}
1723
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001724static void __devexit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725tgafb_exit(void)
1726{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001727 tc_unregister_driver(&tgafb_tc_driver);
1728 pci_unregister_driver(&tgafb_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
1731#ifndef MODULE
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001732static int __devinit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733tgafb_setup(char *arg)
1734{
1735 char *this_opt;
1736
1737 if (arg && *arg) {
1738 while ((this_opt = strsep(&arg, ","))) {
1739 if (!*this_opt)
1740 continue;
1741 if (!strncmp(this_opt, "mode:", 5))
1742 mode_option = this_opt+5;
1743 else
1744 printk(KERN_ERR
1745 "tgafb: unknown parameter %s\n",
1746 this_opt);
1747 }
1748 }
1749
1750 return 0;
1751}
1752#endif /* !MODULE */
1753
Maciej W. Rozycki1b2f2fe2007-02-12 00:54:56 -08001754static int __devinit
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755tgafb_init(void)
1756{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001757 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758#ifndef MODULE
1759 char *option = NULL;
1760
1761 if (fb_get_options("tgafb", &option))
1762 return -ENODEV;
1763 tgafb_setup(option);
1764#endif
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001765 status = pci_register_driver(&tgafb_pci_driver);
1766 if (!status)
1767 status = tc_register_driver(&tgafb_tc_driver);
1768 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769}
1770
1771/*
1772 * Modularisation
1773 */
1774
1775module_init(tgafb_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776module_exit(tgafb_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001778MODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779MODULE_LICENSE("GPL");