blob: 07c7df9ee77bbd54347fdf211066a2c03b227db1 [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_TC
36#define TGA_BUS_TC(dev) (dev->bus == &tc_bus_type)
37#else
38#define TGA_BUS_TC(dev) 0
39#endif
40
Linus Torvalds1da177e2005-04-16 15:20:36 -070041/*
42 * Local functions.
43 */
44
45static int tgafb_check_var(struct fb_var_screeninfo *, struct fb_info *);
46static int tgafb_set_par(struct fb_info *);
47static void tgafb_set_pll(struct tga_par *, int);
48static int tgafb_setcolreg(unsigned, unsigned, unsigned, unsigned,
49 unsigned, struct fb_info *);
50static int tgafb_blank(int, struct fb_info *);
51static void tgafb_init_fix(struct fb_info *);
52
53static void tgafb_imageblit(struct fb_info *, const struct fb_image *);
54static void tgafb_fillrect(struct fb_info *, const struct fb_fillrect *);
55static void tgafb_copyarea(struct fb_info *, const struct fb_copyarea *);
James Simmons28b230e2007-05-08 00:37:50 -070056static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -080058static int tgafb_register(struct device *dev);
59static void tgafb_unregister(struct device *dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070061static const char *mode_option;
62static const char *mode_option_pci = "640x480@60";
63static const char *mode_option_tc = "1280x1024@72";
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070066static struct pci_driver tgafb_pci_driver;
67static struct tc_driver tgafb_tc_driver;
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*
70 * Frame buffer operations
71 */
72
73static struct fb_ops tgafb_ops = {
74 .owner = THIS_MODULE,
75 .fb_check_var = tgafb_check_var,
76 .fb_set_par = tgafb_set_par,
77 .fb_setcolreg = tgafb_setcolreg,
78 .fb_blank = tgafb_blank,
James Simmons28b230e2007-05-08 00:37:50 -070079 .fb_pan_display = tgafb_pan_display,
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 .fb_fillrect = tgafb_fillrect,
81 .fb_copyarea = tgafb_copyarea,
82 .fb_imageblit = tgafb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -070083};
84
85
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070086#ifdef CONFIG_PCI
Linus Torvalds1da177e2005-04-16 15:20:36 -070087/*
88 * PCI registration operations
89 */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -080090static int tgafb_pci_register(struct pci_dev *, const struct pci_device_id *);
91static void tgafb_pci_unregister(struct pci_dev *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93static struct pci_device_id const tgafb_pci_table[] = {
Maciej W. Rozyckifef45902007-02-12 00:54:58 -080094 { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA) },
95 { }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096};
Maciej W. Rozyckifef45902007-02-12 00:54:58 -080097MODULE_DEVICE_TABLE(pci, tgafb_pci_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -070099static struct pci_driver tgafb_pci_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 .name = "tgafb",
101 .id_table = tgafb_pci_table,
102 .probe = tgafb_pci_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800103 .remove = tgafb_pci_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104};
105
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800106static int tgafb_pci_register(struct pci_dev *pdev,
107 const struct pci_device_id *ent)
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700108{
109 return tgafb_register(&pdev->dev);
110}
111
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800112static void tgafb_pci_unregister(struct pci_dev *pdev)
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700113{
114 tgafb_unregister(&pdev->dev);
115}
116#endif /* CONFIG_PCI */
117
118#ifdef CONFIG_TC
119/*
120 * TC registration operations
121 */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800122static int tgafb_tc_register(struct device *);
123static int tgafb_tc_unregister(struct device *);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700124
125static struct tc_device_id const tgafb_tc_table[] = {
126 { "DEC ", "PMAGD-AA" },
127 { "DEC ", "PMAGD " },
128 { }
129};
130MODULE_DEVICE_TABLE(tc, tgafb_tc_table);
131
132static struct tc_driver tgafb_tc_driver = {
133 .id_table = tgafb_tc_table,
134 .driver = {
135 .name = "tgafb",
136 .bus = &tc_bus_type,
137 .probe = tgafb_tc_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800138 .remove = tgafb_tc_unregister,
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700139 },
140};
141
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800142static int tgafb_tc_register(struct device *dev)
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700143{
144 int status = tgafb_register(dev);
145 if (!status)
146 get_device(dev);
147 return status;
148}
149
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800150static int tgafb_tc_unregister(struct device *dev)
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700151{
152 put_device(dev);
153 tgafb_unregister(dev);
154 return 0;
155}
156#endif /* CONFIG_TC */
157
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159/**
160 * tgafb_check_var - Optional function. Validates a var passed in.
161 * @var: frame buffer variable screen structure
162 * @info: frame buffer structure that represents a single frame buffer
163 */
164static int
165tgafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
166{
167 struct tga_par *par = (struct tga_par *)info->par;
168
169 if (par->tga_type == TGA_TYPE_8PLANE) {
170 if (var->bits_per_pixel != 8)
171 return -EINVAL;
172 } else {
173 if (var->bits_per_pixel != 32)
174 return -EINVAL;
175 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800176 var->red.length = var->green.length = var->blue.length = 8;
177 if (var->bits_per_pixel == 32) {
178 var->red.offset = 16;
179 var->green.offset = 8;
180 var->blue.offset = 0;
181 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
183 if (var->xres_virtual != var->xres || var->yres_virtual != var->yres)
184 return -EINVAL;
185 if (var->nonstd)
186 return -EINVAL;
187 if (1000000000 / var->pixclock > TGA_PLL_MAX_FREQ)
188 return -EINVAL;
189 if ((var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
190 return -EINVAL;
191
192 /* Some of the acceleration routines assume the line width is
193 a multiple of 64 bytes. */
194 if (var->xres * (par->tga_type == TGA_TYPE_8PLANE ? 1 : 4) % 64)
195 return -EINVAL;
196
197 return 0;
198}
199
200/**
201 * tgafb_set_par - Optional function. Alters the hardware state.
202 * @info: frame buffer structure that represents a single frame buffer
203 */
204static int
205tgafb_set_par(struct fb_info *info)
206{
207 static unsigned int const deep_presets[4] = {
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700208 0x00004000,
209 0x0000440d,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 0xffffffff,
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700211 0x0000441d
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 };
213 static unsigned int const rasterop_presets[4] = {
214 0x00000003,
215 0x00000303,
216 0xffffffff,
217 0x00000303
218 };
219 static unsigned int const mode_presets[4] = {
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800220 0x00000000,
221 0x00000300,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 0xffffffff,
Maciej W. Rozyckic7488ce2007-02-12 00:54:55 -0800223 0x00000300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 };
225 static unsigned int const base_addr_presets[4] = {
226 0x00000000,
227 0x00000001,
228 0xffffffff,
229 0x00000001
230 };
231
232 struct tga_par *par = (struct tga_par *) info->par;
Yijing Wang3ca356b2013-12-05 19:25:18 +0800233 int tga_bus_pci = dev_is_pci(par->dev);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700234 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 u32 htimings, vtimings, pll_freq;
236 u8 tga_type;
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800237 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
239 /* Encode video timings. */
240 htimings = (((info->var.xres/4) & TGA_HORIZ_ACT_LSB)
241 | (((info->var.xres/4) & 0x600 << 19) & TGA_HORIZ_ACT_MSB));
242 vtimings = (info->var.yres & TGA_VERT_ACTIVE);
243 htimings |= ((info->var.right_margin/4) << 9) & TGA_HORIZ_FP;
244 vtimings |= (info->var.lower_margin << 11) & TGA_VERT_FP;
245 htimings |= ((info->var.hsync_len/4) << 14) & TGA_HORIZ_SYNC;
246 vtimings |= (info->var.vsync_len << 16) & TGA_VERT_SYNC;
247 htimings |= ((info->var.left_margin/4) << 21) & TGA_HORIZ_BP;
248 vtimings |= (info->var.upper_margin << 22) & TGA_VERT_BP;
249
250 if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
251 htimings |= TGA_HORIZ_POLARITY;
252 if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
253 vtimings |= TGA_VERT_POLARITY;
254
255 par->htimings = htimings;
256 par->vtimings = vtimings;
257
258 par->sync_on_green = !!(info->var.sync & FB_SYNC_ON_GREEN);
259
260 /* Store other useful values in par. */
261 par->xres = info->var.xres;
262 par->yres = info->var.yres;
263 par->pll_freq = pll_freq = 1000000000 / info->var.pixclock;
264 par->bits_per_pixel = info->var.bits_per_pixel;
265
266 tga_type = par->tga_type;
267
268 /* First, disable video. */
269 TGA_WRITE_REG(par, TGA_VALID_VIDEO | TGA_VALID_BLANK, TGA_VALID_REG);
270
271 /* Write the DEEP register. */
272 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
273 continue;
274 mb();
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800275 TGA_WRITE_REG(par, deep_presets[tga_type] |
276 (par->sync_on_green ? 0x0 : 0x00010000),
277 TGA_DEEP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 while (TGA_READ_REG(par, TGA_CMD_STAT_REG) & 1) /* wait for not busy */
279 continue;
280 mb();
281
282 /* Write some more registers. */
283 TGA_WRITE_REG(par, rasterop_presets[tga_type], TGA_RASTEROP_REG);
284 TGA_WRITE_REG(par, mode_presets[tga_type], TGA_MODE_REG);
285 TGA_WRITE_REG(par, base_addr_presets[tga_type], TGA_BASE_ADDR_REG);
286
287 /* Calculate & write the PLL. */
288 tgafb_set_pll(par, pll_freq);
289
290 /* Write some more registers. */
291 TGA_WRITE_REG(par, 0xffffffff, TGA_PLANEMASK_REG);
292 TGA_WRITE_REG(par, 0xffffffff, TGA_PIXELMASK_REG);
293
294 /* Init video timing regs. */
295 TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG);
296 TGA_WRITE_REG(par, vtimings, TGA_VERT_REG);
297
Uwe Kleine-König421f91d2010-06-11 12:17:00 +0200298 /* Initialise RAMDAC. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700299 if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 /* Init BT485 RAMDAC registers. */
302 BT485_WRITE(par, 0xa2 | (par->sync_on_green ? 0x8 : 0x0),
303 BT485_CMD_0);
304 BT485_WRITE(par, 0x01, BT485_ADDR_PAL_WRITE);
305 BT485_WRITE(par, 0x14, BT485_CMD_3); /* cursor 64x64 */
306 BT485_WRITE(par, 0x40, BT485_CMD_1);
307 BT485_WRITE(par, 0x20, BT485_CMD_2); /* cursor off, for now */
308 BT485_WRITE(par, 0xff, BT485_PIXEL_MASK);
309
310 /* Fill palette registers. */
311 BT485_WRITE(par, 0x00, BT485_ADDR_PAL_WRITE);
312 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
313
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800314 for (i = 0; i < 256 * 3; i += 4) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800315 TGA_WRITE_REG(par, 0x55 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800317 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800319 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800321 TGA_WRITE_REG(par, 0x00 | (BT485_DATA_PAL << 8),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 TGA_RAMDAC_REG);
323 }
324
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700325 } else if (tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
326
327 /* Init BT459 RAMDAC registers. */
328 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_0, 0x40);
329 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_1, 0x00);
330 BT459_WRITE(par, BT459_REG_ACC, BT459_CMD_REG_2,
331 (par->sync_on_green ? 0xc0 : 0x40));
332
333 BT459_WRITE(par, BT459_REG_ACC, BT459_CUR_CMD_REG, 0x00);
334
335 /* Fill the palette. */
336 BT459_LOAD_ADDR(par, 0x0000);
337 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
338
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700339 for (i = 0; i < 256 * 3; i += 4) {
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700340 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
341 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
342 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
343 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
344 }
345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 } else { /* 24-plane or 24plusZ */
347
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800348 /* Init BT463 RAMDAC registers. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
350 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
351 BT463_WRITE(par, BT463_REG_ACC, BT463_CMD_REG_2,
Maciej W. Rozyckia524d9462007-02-12 00:54:57 -0800352 (par->sync_on_green ? 0xc0 : 0x40));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
355 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
356 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_2, 0xff);
357 BT463_WRITE(par, BT463_REG_ACC, BT463_READ_MASK_3, 0x0f);
358
359 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_0, 0x00);
360 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_1, 0x00);
361 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_2, 0x00);
362 BT463_WRITE(par, BT463_REG_ACC, BT463_BLINK_MASK_3, 0x00);
363
364 /* Fill the palette. */
365 BT463_LOAD_ADDR(par, 0x0000);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800366 TGA_WRITE_REG(par, BT463_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800368#ifdef CONFIG_HW_CONSOLE
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800370 int j = color_table[i];
371
372 TGA_WRITE_REG(par, default_red[j], TGA_RAMDAC_REG);
373 TGA_WRITE_REG(par, default_grn[j], TGA_RAMDAC_REG);
374 TGA_WRITE_REG(par, default_blu[j], TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 }
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800376 for (i = 0; i < 512 * 3; i += 4) {
377#else
378 for (i = 0; i < 528 * 3; i += 4) {
379#endif
380 TGA_WRITE_REG(par, 0x55, TGA_RAMDAC_REG);
381 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
382 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
383 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 }
385
386 /* Fill window type table after start of vertical retrace. */
387 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
388 continue;
389 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
390 mb();
391 while (!(TGA_READ_REG(par, TGA_INTR_STAT_REG) & 0x01))
392 continue;
393 TGA_WRITE_REG(par, 0x01, TGA_INTR_STAT_REG);
394
395 BT463_LOAD_ADDR(par, BT463_WINDOW_TYPE_BASE);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800396 TGA_WRITE_REG(par, BT463_REG_ACC << 2, TGA_RAMDAC_SETUP_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
398 for (i = 0; i < 16; i++) {
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800399 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
400 TGA_WRITE_REG(par, 0x01, TGA_RAMDAC_REG);
401 TGA_WRITE_REG(par, 0x00, TGA_RAMDAC_REG);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 }
403
404 }
405
406 /* Finally, enable video scan (and pray for the monitor... :-) */
407 TGA_WRITE_REG(par, TGA_VALID_VIDEO, TGA_VALID_REG);
408
409 return 0;
410}
411
412#define DIFFCHECK(X) \
413do { \
414 if (m <= 0x3f) { \
415 int delta = f - (TGA_PLL_BASE_FREQ * (X)) / (r << shift); \
416 if (delta < 0) \
417 delta = -delta; \
418 if (delta < min_diff) \
419 min_diff = delta, vm = m, va = a, vr = r; \
420 } \
421} while (0)
422
423static void
424tgafb_set_pll(struct tga_par *par, int f)
425{
426 int n, shift, base, min_diff, target;
427 int r,a,m,vm = 34, va = 1, vr = 30;
428
429 for (r = 0 ; r < 12 ; r++)
430 TGA_WRITE_REG(par, !r, TGA_CLOCK_REG);
431
432 if (f > TGA_PLL_MAX_FREQ)
433 f = TGA_PLL_MAX_FREQ;
434
435 if (f >= TGA_PLL_MAX_FREQ / 2)
436 shift = 0;
437 else if (f >= TGA_PLL_MAX_FREQ / 4)
438 shift = 1;
439 else
440 shift = 2;
441
442 TGA_WRITE_REG(par, shift & 1, TGA_CLOCK_REG);
443 TGA_WRITE_REG(par, shift >> 1, TGA_CLOCK_REG);
444
445 for (r = 0 ; r < 10 ; r++)
446 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
447
448 if (f <= 120000) {
449 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
450 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
451 }
452 else if (f <= 200000) {
453 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
454 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
455 }
456 else {
457 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
458 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
459 }
460
461 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
462 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
463 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
464 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
465 TGA_WRITE_REG(par, 0, TGA_CLOCK_REG);
466 TGA_WRITE_REG(par, 1, TGA_CLOCK_REG);
467
468 target = (f << shift) / TGA_PLL_BASE_FREQ;
469 min_diff = TGA_PLL_MAX_FREQ;
470
471 r = 7 / target;
472 if (!r) r = 1;
473
474 base = target * r;
475 while (base < 449) {
476 for (n = base < 7 ? 7 : base; n < base + target && n < 449; n++) {
477 m = ((n + 3) / 7) - 1;
478 a = 0;
479 DIFFCHECK((m + 1) * 7);
480 m++;
481 DIFFCHECK((m + 1) * 7);
482 m = (n / 6) - 1;
483 if ((a = n % 6))
484 DIFFCHECK(n);
485 }
486 r++;
487 base += target;
488 }
489
490 vr--;
491
492 for (r = 0; r < 8; r++)
493 TGA_WRITE_REG(par, (vm >> r) & 1, TGA_CLOCK_REG);
494 for (r = 0; r < 8 ; r++)
495 TGA_WRITE_REG(par, (va >> r) & 1, TGA_CLOCK_REG);
496 for (r = 0; r < 7 ; r++)
497 TGA_WRITE_REG(par, (vr >> r) & 1, TGA_CLOCK_REG);
498 TGA_WRITE_REG(par, ((vr >> 7) & 1)|2, TGA_CLOCK_REG);
499}
500
501
502/**
503 * tgafb_setcolreg - Optional function. Sets a color register.
504 * @regno: boolean, 0 copy local, 1 get_user() function
505 * @red: frame buffer colormap structure
506 * @green: The green value which can be up to 16 bits wide
507 * @blue: The blue value which can be up to 16 bits wide.
508 * @transp: If supported the alpha value which can be up to 16 bits wide.
509 * @info: frame buffer info structure
510 */
511static int
512tgafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
513 unsigned transp, struct fb_info *info)
514{
515 struct tga_par *par = (struct tga_par *) info->par;
Yijing Wang3ca356b2013-12-05 19:25:18 +0800516 int tga_bus_pci = dev_is_pci(par->dev);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700517 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
519 if (regno > 255)
520 return 1;
521 red >>= 8;
522 green >>= 8;
523 blue >>= 8;
524
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700525 if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_pci) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 BT485_WRITE(par, regno, BT485_ADDR_PAL_WRITE);
527 TGA_WRITE_REG(par, BT485_DATA_PAL, TGA_RAMDAC_SETUP_REG);
528 TGA_WRITE_REG(par, red|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
529 TGA_WRITE_REG(par, green|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
530 TGA_WRITE_REG(par, blue|(BT485_DATA_PAL<<8),TGA_RAMDAC_REG);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -0700531 } else if (par->tga_type == TGA_TYPE_8PLANE && tga_bus_tc) {
532 BT459_LOAD_ADDR(par, regno);
533 TGA_WRITE_REG(par, BT459_PALETTE << 2, TGA_RAMDAC_SETUP_REG);
534 TGA_WRITE_REG(par, red, TGA_RAMDAC_REG);
535 TGA_WRITE_REG(par, green, TGA_RAMDAC_REG);
536 TGA_WRITE_REG(par, blue, TGA_RAMDAC_REG);
Maciej W. Rozyckibe601182007-02-12 00:54:54 -0800537 } else {
538 if (regno < 16) {
539 u32 value = (regno << 16) | (regno << 8) | regno;
540 ((u32 *)info->pseudo_palette)[regno] = value;
541 }
542 BT463_LOAD_ADDR(par, regno);
543 TGA_WRITE_REG(par, BT463_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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
548
549 return 0;
550}
551
552
553/**
554 * tgafb_blank - Optional function. Blanks the display.
555 * @blank_mode: the blank mode we want.
556 * @info: frame buffer structure that represents a single frame buffer
557 */
558static int
559tgafb_blank(int blank, struct fb_info *info)
560{
561 struct tga_par *par = (struct tga_par *) info->par;
562 u32 vhcr, vvcr, vvvr;
563 unsigned long flags;
564
565 local_irq_save(flags);
566
567 vhcr = TGA_READ_REG(par, TGA_HORIZ_REG);
568 vvcr = TGA_READ_REG(par, TGA_VERT_REG);
569 vvvr = TGA_READ_REG(par, TGA_VALID_REG);
570 vvvr &= ~(TGA_VALID_VIDEO | TGA_VALID_BLANK);
571
572 switch (blank) {
573 case FB_BLANK_UNBLANK: /* Unblanking */
574 if (par->vesa_blanked) {
575 TGA_WRITE_REG(par, vhcr & 0xbfffffff, TGA_HORIZ_REG);
576 TGA_WRITE_REG(par, vvcr & 0xbfffffff, TGA_VERT_REG);
577 par->vesa_blanked = 0;
578 }
579 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO, TGA_VALID_REG);
580 break;
581
582 case FB_BLANK_NORMAL: /* Normal blanking */
583 TGA_WRITE_REG(par, vvvr | TGA_VALID_VIDEO | TGA_VALID_BLANK,
584 TGA_VALID_REG);
585 break;
586
587 case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
588 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
589 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
590 par->vesa_blanked = 1;
591 break;
592
593 case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
594 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
595 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
596 par->vesa_blanked = 1;
597 break;
598
599 case FB_BLANK_POWERDOWN: /* Poweroff */
600 TGA_WRITE_REG(par, vhcr | 0x40000000, TGA_HORIZ_REG);
601 TGA_WRITE_REG(par, vvcr | 0x40000000, TGA_VERT_REG);
602 TGA_WRITE_REG(par, vvvr | TGA_VALID_BLANK, TGA_VALID_REG);
603 par->vesa_blanked = 1;
604 break;
605 }
606
607 local_irq_restore(flags);
608 return 0;
609}
610
611
612/*
613 * Acceleration.
614 */
615
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616static void
James Simmons28b230e2007-05-08 00:37:50 -0700617tgafb_mono_imageblit(struct fb_info *info, const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 struct tga_par *par = (struct tga_par *) info->par;
620 u32 fgcolor, bgcolor, dx, dy, width, height, vxres, vyres, pixelmask;
621 unsigned long rincr, line_length, shift, pos, is8bpp;
622 unsigned long i, j;
623 const unsigned char *data;
624 void __iomem *regs_base;
625 void __iomem *fb_base;
626
James Simmons28b230e2007-05-08 00:37:50 -0700627 is8bpp = info->var.bits_per_pixel == 8;
628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 dx = image->dx;
630 dy = image->dy;
631 width = image->width;
632 height = image->height;
633 vxres = info->var.xres_virtual;
634 vyres = info->var.yres_virtual;
635 line_length = info->fix.line_length;
636 rincr = (width + 7) / 8;
637
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700638 /* A shift below cannot cope with. */
639 if (unlikely(width == 0))
640 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 /* Crop the image to the screen. */
642 if (dx > vxres || dy > vyres)
643 return;
644 if (dx + width > vxres)
645 width = vxres - dx;
646 if (dy + height > vyres)
647 height = vyres - dy;
648
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 regs_base = par->tga_regs_base;
650 fb_base = par->tga_fb_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
652 /* Expand the color values to fill 32-bits. */
653 /* ??? Would be nice to notice colour changes elsewhere, so
654 that we can do this only when necessary. */
655 fgcolor = image->fg_color;
656 bgcolor = image->bg_color;
657 if (is8bpp) {
658 fgcolor |= fgcolor << 8;
659 fgcolor |= fgcolor << 16;
660 bgcolor |= bgcolor << 8;
661 bgcolor |= bgcolor << 16;
662 } else {
663 if (fgcolor < 16)
664 fgcolor = ((u32 *)info->pseudo_palette)[fgcolor];
665 if (bgcolor < 16)
666 bgcolor = ((u32 *)info->pseudo_palette)[bgcolor];
667 }
668 __raw_writel(fgcolor, regs_base + TGA_FOREGROUND_REG);
669 __raw_writel(bgcolor, regs_base + TGA_BACKGROUND_REG);
670
671 /* Acquire proper alignment; set up the PIXELMASK register
672 so that we only write the proper character cell. */
673 pos = dy * line_length;
674 if (is8bpp) {
675 pos += dx;
676 shift = pos & 3;
677 pos &= -4;
678 } else {
679 pos += dx * 4;
680 shift = (pos & 7) >> 2;
681 pos &= -8;
682 }
683
684 data = (const unsigned char *) image->data;
685
686 /* Enable opaque stipple mode. */
687 __raw_writel((is8bpp
688 ? TGA_MODE_SBM_8BPP | TGA_MODE_OPAQUE_STIPPLE
689 : TGA_MODE_SBM_24BPP | TGA_MODE_OPAQUE_STIPPLE),
690 regs_base + TGA_MODE_REG);
691
692 if (width + shift <= 32) {
693 unsigned long bwidth;
694
695 /* Handle common case of imaging a single character, in
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700696 a font less than or 32 pixels wide. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Maciej W. Rozyckib738b992007-10-16 01:29:56 -0700698 /* Avoid a shift by 32; width > 0 implied. */
699 pixelmask = (2ul << (width - 1)) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 pixelmask <<= shift;
701 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
702 wmb();
703
704 bwidth = (width + 7) / 8;
705
706 for (i = 0; i < height; ++i) {
707 u32 mask = 0;
708
709 /* The image data is bit big endian; we need
710 little endian. */
711 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800712 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713
714 __raw_writel(mask << shift, fb_base + pos);
715
716 pos += line_length;
717 data += rincr;
718 }
719 wmb();
720 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
721 } else if (shift == 0) {
722 unsigned long pos0 = pos;
723 const unsigned char *data0 = data;
724 unsigned long bincr = (is8bpp ? 8 : 8*4);
725 unsigned long bwidth;
726
727 /* Handle another common case in which accel_putcs
728 generates a large bitmap, which happens to be aligned.
729 Allow the tail to be misaligned. This case is
730 interesting because we've not got to hold partial
731 bytes across the words being written. */
732
733 wmb();
734
735 bwidth = (width / 8) & -4;
736 for (i = 0; i < height; ++i) {
737 for (j = 0; j < bwidth; j += 4) {
738 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800739 mask |= bitrev8(data[j+0]) << (0 * 8);
740 mask |= bitrev8(data[j+1]) << (1 * 8);
741 mask |= bitrev8(data[j+2]) << (2 * 8);
742 mask |= bitrev8(data[j+3]) << (3 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 __raw_writel(mask, fb_base + pos + j*bincr);
744 }
745 pos += line_length;
746 data += rincr;
747 }
748 wmb();
749
750 pixelmask = (1ul << (width & 31)) - 1;
751 if (pixelmask) {
752 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
753 wmb();
754
755 pos = pos0 + bwidth*bincr;
756 data = data0 + bwidth;
757 bwidth = ((width & 31) + 7) / 8;
758
759 for (i = 0; i < height; ++i) {
760 u32 mask = 0;
761 for (j = 0; j < bwidth; ++j)
Akinobu Mita1c667682006-12-08 02:36:26 -0800762 mask |= bitrev8(data[j]) << (j * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 __raw_writel(mask, fb_base + pos);
764 pos += line_length;
765 data += rincr;
766 }
767 wmb();
768 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
769 }
770 } else {
771 unsigned long pos0 = pos;
772 const unsigned char *data0 = data;
773 unsigned long bincr = (is8bpp ? 8 : 8*4);
774 unsigned long bwidth;
775
776 /* Finally, handle the generic case of misaligned start.
777 Here we split the write into 16-bit spans. This allows
778 us to use only one pixel mask, instead of four as would
779 be required by writing 24-bit spans. */
780
781 pixelmask = 0xffff << shift;
782 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
783 wmb();
784
785 bwidth = (width / 8) & -2;
786 for (i = 0; i < height; ++i) {
787 for (j = 0; j < bwidth; j += 2) {
788 u32 mask = 0;
Akinobu Mita1c667682006-12-08 02:36:26 -0800789 mask |= bitrev8(data[j+0]) << (0 * 8);
790 mask |= bitrev8(data[j+1]) << (1 * 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 mask <<= shift;
792 __raw_writel(mask, fb_base + pos + j*bincr);
793 }
794 pos += line_length;
795 data += rincr;
796 }
797 wmb();
798
799 pixelmask = ((1ul << (width & 15)) - 1) << shift;
800 if (pixelmask) {
801 __raw_writel(pixelmask, regs_base + TGA_PIXELMASK_REG);
802 wmb();
803
804 pos = pos0 + bwidth*bincr;
805 data = data0 + bwidth;
806 bwidth = (width & 15) > 8;
807
808 for (i = 0; i < height; ++i) {
Akinobu Mita1c667682006-12-08 02:36:26 -0800809 u32 mask = bitrev8(data[0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 if (bwidth)
Akinobu Mita1c667682006-12-08 02:36:26 -0800811 mask |= bitrev8(data[1]) << 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 mask <<= shift;
813 __raw_writel(mask, fb_base + pos);
814 pos += line_length;
815 data += rincr;
816 }
817 wmb();
818 }
819 __raw_writel(0xffffffff, regs_base + TGA_PIXELMASK_REG);
820 }
821
822 /* Disable opaque stipple mode. */
823 __raw_writel((is8bpp
824 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
825 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
826 regs_base + TGA_MODE_REG);
827}
828
James Simmons28b230e2007-05-08 00:37:50 -0700829static void
830tgafb_clut_imageblit(struct fb_info *info, const struct fb_image *image)
831{
832 struct tga_par *par = (struct tga_par *) info->par;
833 u32 color, dx, dy, width, height, vxres, vyres;
834 u32 *palette = ((u32 *)info->pseudo_palette);
835 unsigned long pos, line_length, i, j;
836 const unsigned char *data;
Al Viro0bd84962007-07-26 17:36:09 +0100837 void __iomem *regs_base, *fb_base;
James Simmons28b230e2007-05-08 00:37:50 -0700838
839 dx = image->dx;
840 dy = image->dy;
841 width = image->width;
842 height = image->height;
843 vxres = info->var.xres_virtual;
844 vyres = info->var.yres_virtual;
845 line_length = info->fix.line_length;
846
847 /* Crop the image to the screen. */
848 if (dx > vxres || dy > vyres)
849 return;
850 if (dx + width > vxres)
851 width = vxres - dx;
852 if (dy + height > vyres)
853 height = vyres - dy;
854
855 regs_base = par->tga_regs_base;
856 fb_base = par->tga_fb_base;
857
858 pos = dy * line_length + (dx * 4);
859 data = image->data;
860
861 /* Now copy the image, color_expanding via the palette. */
862 for (i = 0; i < height; i++) {
863 for (j = 0; j < width; j++) {
864 color = palette[*data++];
865 __raw_writel(color, fb_base + pos + j*4);
866 }
867 pos += line_length;
868 }
869}
870
871/**
872 * tgafb_imageblit - REQUIRED function. Can use generic routines if
873 * non acclerated hardware and packed pixel based.
874 * Copies a image from system memory to the screen.
875 *
876 * @info: frame buffer structure that represents a single frame buffer
877 * @image: structure defining the image.
878 */
879static void
880tgafb_imageblit(struct fb_info *info, const struct fb_image *image)
881{
882 unsigned int is8bpp = info->var.bits_per_pixel == 8;
883
884 /* If a mono image, regardless of FB depth, go do it. */
885 if (image->depth == 1) {
886 tgafb_mono_imageblit(info, image);
887 return;
888 }
889
890 /* For copies that aren't pixel expansion, there's little we
891 can do better than the generic code. */
892 /* ??? There is a DMA write mode; I wonder if that could be
893 made to pull the data from the image buffer... */
894 if (image->depth == info->var.bits_per_pixel) {
895 cfb_imageblit(info, image);
896 return;
897 }
898
899 /* If 24-plane FB and the image is 8-plane with CLUT, we can do it. */
900 if (!is8bpp && image->depth == 8) {
901 tgafb_clut_imageblit(info, image);
902 return;
903 }
904
905 /* Silently return... */
906}
907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908/**
909 * tgafb_fillrect - REQUIRED function. Can use generic routines if
910 * non acclerated hardware and packed pixel based.
911 * Draws a rectangle on the screen.
912 *
913 * @info: frame buffer structure that represents a single frame buffer
914 * @rect: structure defining the rectagle and operation.
915 */
916static void
917tgafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
918{
919 struct tga_par *par = (struct tga_par *) info->par;
920 int is8bpp = info->var.bits_per_pixel == 8;
921 u32 dx, dy, width, height, vxres, vyres, color;
922 unsigned long pos, align, line_length, i, j;
923 void __iomem *regs_base;
924 void __iomem *fb_base;
925
926 dx = rect->dx;
927 dy = rect->dy;
928 width = rect->width;
929 height = rect->height;
930 vxres = info->var.xres_virtual;
931 vyres = info->var.yres_virtual;
932 line_length = info->fix.line_length;
933 regs_base = par->tga_regs_base;
934 fb_base = par->tga_fb_base;
935
936 /* Crop the rectangle to the screen. */
937 if (dx > vxres || dy > vyres || !width || !height)
938 return;
939 if (dx + width > vxres)
940 width = vxres - dx;
941 if (dy + height > vyres)
942 height = vyres - dy;
943
944 pos = dy * line_length + dx * (is8bpp ? 1 : 4);
945
946 /* ??? We could implement ROP_XOR with opaque fill mode
947 and a RasterOp setting of GXxor, but as far as I can
948 tell, this mode is not actually used in the kernel.
949 Thus I am ignoring it for now. */
950 if (rect->rop != ROP_COPY) {
951 cfb_fillrect(info, rect);
952 return;
953 }
954
955 /* Expand the color value to fill 8 pixels. */
956 color = rect->color;
957 if (is8bpp) {
958 color |= color << 8;
959 color |= color << 16;
960 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
961 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
962 } else {
963 if (color < 16)
964 color = ((u32 *)info->pseudo_palette)[color];
965 __raw_writel(color, regs_base + TGA_BLOCK_COLOR0_REG);
966 __raw_writel(color, regs_base + TGA_BLOCK_COLOR1_REG);
967 __raw_writel(color, regs_base + TGA_BLOCK_COLOR2_REG);
968 __raw_writel(color, regs_base + TGA_BLOCK_COLOR3_REG);
969 __raw_writel(color, regs_base + TGA_BLOCK_COLOR4_REG);
970 __raw_writel(color, regs_base + TGA_BLOCK_COLOR5_REG);
971 __raw_writel(color, regs_base + TGA_BLOCK_COLOR6_REG);
972 __raw_writel(color, regs_base + TGA_BLOCK_COLOR7_REG);
973 }
974
975 /* The DATA register holds the fill mask for block fill mode.
976 Since we're not stippling, this is all ones. */
977 __raw_writel(0xffffffff, regs_base + TGA_DATA_REG);
978
979 /* Enable block fill mode. */
980 __raw_writel((is8bpp
981 ? TGA_MODE_SBM_8BPP | TGA_MODE_BLOCK_FILL
982 : TGA_MODE_SBM_24BPP | TGA_MODE_BLOCK_FILL),
983 regs_base + TGA_MODE_REG);
984 wmb();
985
986 /* We can fill 2k pixels per operation. Notice blocks that fit
987 the width of the screen so that we can take advantage of this
988 and fill more than one line per write. */
989 if (width == line_length)
990 width *= height, height = 1;
991
992 /* The write into the frame buffer must be aligned to 4 bytes,
993 but we are allowed to encode the offset within the word in
994 the data word written. */
995 align = (pos & 3) << 16;
996 pos &= -4;
997
998 if (width <= 2048) {
999 u32 data;
1000
1001 data = (width - 1) | align;
1002
1003 for (i = 0; i < height; ++i) {
1004 __raw_writel(data, fb_base + pos);
1005 pos += line_length;
1006 }
1007 } else {
1008 unsigned long Bpp = (is8bpp ? 1 : 4);
1009 unsigned long nwidth = width & -2048;
1010 u32 fdata, ldata;
1011
1012 fdata = (2048 - 1) | align;
1013 ldata = ((width & 2047) - 1) | align;
1014
1015 for (i = 0; i < height; ++i) {
1016 for (j = 0; j < nwidth; j += 2048)
1017 __raw_writel(fdata, fb_base + pos + j*Bpp);
1018 if (j < width)
1019 __raw_writel(ldata, fb_base + pos + j*Bpp);
1020 pos += line_length;
1021 }
1022 }
1023 wmb();
1024
1025 /* Disable block fill mode. */
1026 __raw_writel((is8bpp
1027 ? TGA_MODE_SBM_8BPP | TGA_MODE_SIMPLE
1028 : TGA_MODE_SBM_24BPP | TGA_MODE_SIMPLE),
1029 regs_base + TGA_MODE_REG);
1030}
1031
1032/**
1033 * tgafb_copyarea - REQUIRED function. Can use generic routines if
1034 * non acclerated hardware and packed pixel based.
1035 * Copies on area of the screen to another area.
1036 *
1037 * @info: frame buffer structure that represents a single frame buffer
1038 * @area: structure defining the source and destination.
1039 */
1040
1041/* Handle the special case of copying entire lines, e.g. during scrolling.
1042 We can avoid a lot of needless computation in this case. In the 8bpp
1043 case we need to use the COPY64 registers instead of mask writes into
1044 the frame buffer to achieve maximum performance. */
1045
1046static inline void
1047copyarea_line_8bpp(struct fb_info *info, u32 dy, u32 sy,
1048 u32 height, u32 width)
1049{
1050 struct tga_par *par = (struct tga_par *) info->par;
1051 void __iomem *tga_regs = par->tga_regs_base;
1052 unsigned long dpos, spos, i, n64;
1053
1054 /* Set up the MODE and PIXELSHIFT registers. */
1055 __raw_writel(TGA_MODE_SBM_8BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1056 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1057 wmb();
1058
1059 n64 = (height * width) / 64;
1060
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001061 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 spos = (sy + height) * width;
1063 dpos = (dy + height) * width;
1064
1065 for (i = 0; i < n64; ++i) {
1066 spos -= 64;
1067 dpos -= 64;
1068 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1069 wmb();
1070 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1071 wmb();
1072 }
1073 } else {
1074 spos = sy * width;
1075 dpos = dy * width;
1076
1077 for (i = 0; i < n64; ++i) {
1078 __raw_writel(spos, tga_regs+TGA_COPY64_SRC);
1079 wmb();
1080 __raw_writel(dpos, tga_regs+TGA_COPY64_DST);
1081 wmb();
1082 spos += 64;
1083 dpos += 64;
1084 }
1085 }
1086
1087 /* Reset the MODE register to normal. */
1088 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1089}
1090
1091static inline void
1092copyarea_line_32bpp(struct fb_info *info, u32 dy, u32 sy,
1093 u32 height, u32 width)
1094{
1095 struct tga_par *par = (struct tga_par *) info->par;
1096 void __iomem *tga_regs = par->tga_regs_base;
1097 void __iomem *tga_fb = par->tga_fb_base;
1098 void __iomem *src;
1099 void __iomem *dst;
1100 unsigned long i, n16;
1101
1102 /* Set up the MODE and PIXELSHIFT registers. */
1103 __raw_writel(TGA_MODE_SBM_24BPP | TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1104 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1105 wmb();
1106
1107 n16 = (height * width) / 16;
1108
Maciej W. Rozycki36f71402007-02-12 00:54:53 -08001109 if (sy < dy) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 src = tga_fb + (sy + height) * width * 4;
1111 dst = tga_fb + (dy + height) * width * 4;
1112
1113 for (i = 0; i < n16; ++i) {
1114 src -= 64;
1115 dst -= 64;
1116 __raw_writel(0xffff, src);
1117 wmb();
1118 __raw_writel(0xffff, dst);
1119 wmb();
1120 }
1121 } else {
1122 src = tga_fb + sy * width * 4;
1123 dst = tga_fb + dy * width * 4;
1124
1125 for (i = 0; i < n16; ++i) {
1126 __raw_writel(0xffff, src);
1127 wmb();
1128 __raw_writel(0xffff, dst);
1129 wmb();
1130 src += 64;
1131 dst += 64;
1132 }
1133 }
1134
1135 /* Reset the MODE register to normal. */
1136 __raw_writel(TGA_MODE_SBM_24BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1137}
1138
1139/* The general case of forward copy in 8bpp mode. */
1140static inline void
1141copyarea_foreward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1142 u32 height, u32 width, u32 line_length)
1143{
1144 struct tga_par *par = (struct tga_par *) info->par;
1145 unsigned long i, copied, left;
1146 unsigned long dpos, spos, dalign, salign, yincr;
1147 u32 smask_first, dmask_first, dmask_last;
1148 int pixel_shift, need_prime, need_second;
1149 unsigned long n64, n32, xincr_first;
1150 void __iomem *tga_regs;
1151 void __iomem *tga_fb;
1152
1153 yincr = line_length;
1154 if (dy > sy) {
1155 dy += height - 1;
1156 sy += height - 1;
1157 yincr = -yincr;
1158 }
1159
1160 /* Compute the offsets and alignments in the frame buffer.
1161 More than anything else, these control how we do copies. */
1162 dpos = dy * line_length + dx;
1163 spos = sy * line_length + sx;
1164 dalign = dpos & 7;
1165 salign = spos & 7;
1166 dpos &= -8;
1167 spos &= -8;
1168
1169 /* Compute the value for the PIXELSHIFT register. This controls
1170 both non-co-aligned source and destination and copy direction. */
1171 if (dalign >= salign)
1172 pixel_shift = dalign - salign;
1173 else
1174 pixel_shift = 8 - (salign - dalign);
1175
1176 /* Figure out if we need an additional priming step for the
1177 residue register. */
1178 need_prime = (salign > dalign);
1179 if (need_prime)
1180 dpos -= 8;
1181
1182 /* Begin by copying the leading unaligned destination. Copy enough
1183 to make the next destination address 32-byte aligned. */
1184 copied = 32 - (dalign + (dpos & 31));
1185 if (copied == 32)
1186 copied = 0;
1187 xincr_first = (copied + 7) & -8;
1188 smask_first = dmask_first = (1ul << copied) - 1;
1189 smask_first <<= salign;
1190 dmask_first <<= dalign + need_prime*8;
1191 if (need_prime && copied > 24)
1192 copied -= 8;
1193 left = width - copied;
1194
1195 /* Care for small copies. */
1196 if (copied > width) {
1197 u32 t;
1198 t = (1ul << width) - 1;
1199 t <<= dalign + need_prime*8;
1200 dmask_first &= t;
1201 left = 0;
1202 }
1203
1204 /* Attempt to use 64-byte copies. This is only possible if the
1205 source and destination are co-aligned at 64 bytes. */
1206 n64 = need_second = 0;
1207 if ((dpos & 63) == (spos & 63)
1208 && (height == 1 || line_length % 64 == 0)) {
1209 /* We may need a 32-byte copy to ensure 64 byte alignment. */
1210 need_second = (dpos + xincr_first) & 63;
1211 if ((need_second & 32) != need_second)
1212 printk(KERN_ERR "tgafb: need_second wrong\n");
1213 if (left >= need_second + 64) {
1214 left -= need_second;
1215 n64 = left / 64;
1216 left %= 64;
1217 } else
1218 need_second = 0;
1219 }
1220
1221 /* Copy trailing full 32-byte sections. This will be the main
1222 loop if the 64 byte loop can't be used. */
1223 n32 = left / 32;
1224 left %= 32;
1225
1226 /* Copy the trailing unaligned destination. */
1227 dmask_last = (1ul << left) - 1;
1228
1229 tga_regs = par->tga_regs_base;
1230 tga_fb = par->tga_fb_base;
1231
1232 /* Set up the MODE and PIXELSHIFT registers. */
1233 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1234 __raw_writel(pixel_shift, tga_regs+TGA_PIXELSHIFT_REG);
1235 wmb();
1236
1237 for (i = 0; i < height; ++i) {
1238 unsigned long j;
1239 void __iomem *sfb;
1240 void __iomem *dfb;
1241
1242 sfb = tga_fb + spos;
1243 dfb = tga_fb + dpos;
1244 if (dmask_first) {
1245 __raw_writel(smask_first, sfb);
1246 wmb();
1247 __raw_writel(dmask_first, dfb);
1248 wmb();
1249 sfb += xincr_first;
1250 dfb += xincr_first;
1251 }
1252
1253 if (need_second) {
1254 __raw_writel(0xffffffff, sfb);
1255 wmb();
1256 __raw_writel(0xffffffff, dfb);
1257 wmb();
1258 sfb += 32;
1259 dfb += 32;
1260 }
1261
1262 if (n64 && (((unsigned long)sfb | (unsigned long)dfb) & 63))
1263 printk(KERN_ERR
1264 "tgafb: misaligned copy64 (s:%p, d:%p)\n",
1265 sfb, dfb);
1266
1267 for (j = 0; j < n64; ++j) {
1268 __raw_writel(sfb - tga_fb, tga_regs+TGA_COPY64_SRC);
1269 wmb();
1270 __raw_writel(dfb - tga_fb, tga_regs+TGA_COPY64_DST);
1271 wmb();
1272 sfb += 64;
1273 dfb += 64;
1274 }
1275
1276 for (j = 0; j < n32; ++j) {
1277 __raw_writel(0xffffffff, sfb);
1278 wmb();
1279 __raw_writel(0xffffffff, dfb);
1280 wmb();
1281 sfb += 32;
1282 dfb += 32;
1283 }
1284
1285 if (dmask_last) {
1286 __raw_writel(0xffffffff, sfb);
1287 wmb();
1288 __raw_writel(dmask_last, dfb);
1289 wmb();
1290 }
1291
1292 spos += yincr;
1293 dpos += yincr;
1294 }
1295
1296 /* Reset the MODE register to normal. */
1297 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1298}
1299
1300/* The (almost) general case of backward copy in 8bpp mode. */
1301static inline void
1302copyarea_backward_8bpp(struct fb_info *info, u32 dx, u32 dy, u32 sx, u32 sy,
1303 u32 height, u32 width, u32 line_length,
1304 const struct fb_copyarea *area)
1305{
1306 struct tga_par *par = (struct tga_par *) info->par;
1307 unsigned long i, left, yincr;
1308 unsigned long depos, sepos, dealign, sealign;
1309 u32 mask_first, mask_last;
1310 unsigned long n32;
1311 void __iomem *tga_regs;
1312 void __iomem *tga_fb;
1313
1314 yincr = line_length;
1315 if (dy > sy) {
1316 dy += height - 1;
1317 sy += height - 1;
1318 yincr = -yincr;
1319 }
1320
1321 /* Compute the offsets and alignments in the frame buffer.
1322 More than anything else, these control how we do copies. */
1323 depos = dy * line_length + dx + width;
1324 sepos = sy * line_length + sx + width;
1325 dealign = depos & 7;
1326 sealign = sepos & 7;
1327
1328 /* ??? The documentation appears to be incorrect (or very
1329 misleading) wrt how pixel shifting works in backward copy
1330 mode, i.e. when PIXELSHIFT is negative. I give up for now.
1331 Do handle the common case of co-aligned backward copies,
1332 but frob everything else back on generic code. */
1333 if (dealign != sealign) {
1334 cfb_copyarea(info, area);
1335 return;
1336 }
1337
1338 /* We begin the copy with the trailing pixels of the
1339 unaligned destination. */
1340 mask_first = (1ul << dealign) - 1;
1341 left = width - dealign;
1342
1343 /* Care for small copies. */
1344 if (dealign > width) {
1345 mask_first ^= (1ul << (dealign - width)) - 1;
1346 left = 0;
1347 }
1348
1349 /* Next copy full words at a time. */
1350 n32 = left / 32;
1351 left %= 32;
1352
1353 /* Finally copy the unaligned head of the span. */
1354 mask_last = -1 << (32 - left);
1355
1356 tga_regs = par->tga_regs_base;
1357 tga_fb = par->tga_fb_base;
1358
1359 /* Set up the MODE and PIXELSHIFT registers. */
1360 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_COPY, tga_regs+TGA_MODE_REG);
1361 __raw_writel(0, tga_regs+TGA_PIXELSHIFT_REG);
1362 wmb();
1363
1364 for (i = 0; i < height; ++i) {
1365 unsigned long j;
1366 void __iomem *sfb;
1367 void __iomem *dfb;
1368
1369 sfb = tga_fb + sepos;
1370 dfb = tga_fb + depos;
1371 if (mask_first) {
1372 __raw_writel(mask_first, sfb);
1373 wmb();
1374 __raw_writel(mask_first, dfb);
1375 wmb();
1376 }
1377
1378 for (j = 0; j < n32; ++j) {
1379 sfb -= 32;
1380 dfb -= 32;
1381 __raw_writel(0xffffffff, sfb);
1382 wmb();
1383 __raw_writel(0xffffffff, dfb);
1384 wmb();
1385 }
1386
1387 if (mask_last) {
1388 sfb -= 32;
1389 dfb -= 32;
1390 __raw_writel(mask_last, sfb);
1391 wmb();
1392 __raw_writel(mask_last, dfb);
1393 wmb();
1394 }
1395
1396 sepos += yincr;
1397 depos += yincr;
1398 }
1399
1400 /* Reset the MODE register to normal. */
1401 __raw_writel(TGA_MODE_SBM_8BPP|TGA_MODE_SIMPLE, tga_regs+TGA_MODE_REG);
1402}
1403
1404static void
1405tgafb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
1406{
1407 unsigned long dx, dy, width, height, sx, sy, vxres, vyres;
1408 unsigned long line_length, bpp;
1409
1410 dx = area->dx;
1411 dy = area->dy;
1412 width = area->width;
1413 height = area->height;
1414 sx = area->sx;
1415 sy = area->sy;
1416 vxres = info->var.xres_virtual;
1417 vyres = info->var.yres_virtual;
1418 line_length = info->fix.line_length;
1419
1420 /* The top left corners must be in the virtual screen. */
1421 if (dx > vxres || sx > vxres || dy > vyres || sy > vyres)
1422 return;
1423
1424 /* Clip the destination. */
1425 if (dx + width > vxres)
1426 width = vxres - dx;
1427 if (dy + height > vyres)
1428 height = vyres - dy;
1429
1430 /* The source must be completely inside the virtual screen. */
1431 if (sx + width > vxres || sy + height > vyres)
1432 return;
1433
1434 bpp = info->var.bits_per_pixel;
1435
1436 /* Detect copies of the entire line. */
1437 if (width * (bpp >> 3) == line_length) {
1438 if (bpp == 8)
1439 copyarea_line_8bpp(info, dy, sy, height, width);
1440 else
1441 copyarea_line_32bpp(info, dy, sy, height, width);
1442 }
1443
1444 /* ??? The documentation is unclear to me exactly how the pixelshift
1445 register works in 32bpp mode. Since I don't have hardware to test,
1446 give up for now and fall back on the generic routines. */
1447 else if (bpp == 32)
1448 cfb_copyarea(info, area);
1449
1450 /* Detect overlapping source and destination that requires
1451 a backward copy. */
1452 else if (dy == sy && dx > sx && dx < sx + width)
1453 copyarea_backward_8bpp(info, dx, dy, sx, sy, height,
1454 width, line_length, area);
1455 else
1456 copyarea_foreward_8bpp(info, dx, dy, sx, sy, height,
1457 width, line_length);
1458}
1459
1460
1461/*
1462 * Initialisation
1463 */
1464
1465static void
1466tgafb_init_fix(struct fb_info *info)
1467{
1468 struct tga_par *par = (struct tga_par *)info->par;
Yijing Wang3ca356b2013-12-05 19:25:18 +08001469 int tga_bus_pci = dev_is_pci(par->dev);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001470 int tga_bus_tc = TGA_BUS_TC(par->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 u8 tga_type = par->tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001472 const char *tga_type_name = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473
1474 switch (tga_type) {
1475 case TGA_TYPE_8PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001476 if (tga_bus_pci)
1477 tga_type_name = "Digital ZLXp-E1";
1478 if (tga_bus_tc)
1479 tga_type_name = "Digital ZLX-E1";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 break;
1481 case TGA_TYPE_24PLANE:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001482 if (tga_bus_pci)
1483 tga_type_name = "Digital ZLXp-E2";
1484 if (tga_bus_tc)
1485 tga_type_name = "Digital ZLX-E2";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 break;
1487 case TGA_TYPE_24PLUSZ:
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001488 if (tga_bus_pci)
1489 tga_type_name = "Digital ZLXp-E3";
1490 if (tga_bus_tc)
1491 tga_type_name = "Digital ZLX-E3";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 }
Dan Carpenter0ede5802013-12-16 12:49:23 +03001494 if (!tga_type_name)
1495 tga_type_name = "Unknown";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
1497 strlcpy(info->fix.id, tga_type_name, sizeof(info->fix.id));
1498
1499 info->fix.type = FB_TYPE_PACKED_PIXELS;
1500 info->fix.type_aux = 0;
1501 info->fix.visual = (tga_type == TGA_TYPE_8PLANE
1502 ? FB_VISUAL_PSEUDOCOLOR
Maciej W. Rozyckibe601182007-02-12 00:54:54 -08001503 : FB_VISUAL_DIRECTCOLOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504
1505 info->fix.line_length = par->xres * (par->bits_per_pixel >> 3);
1506 info->fix.smem_start = (size_t) par->tga_fb_base;
1507 info->fix.smem_len = info->fix.line_length * par->yres;
1508 info->fix.mmio_start = (size_t) par->tga_regs_base;
1509 info->fix.mmio_len = 512;
1510
1511 info->fix.xpanstep = 0;
1512 info->fix.ypanstep = 0;
1513 info->fix.ywrapstep = 0;
1514
1515 info->fix.accel = FB_ACCEL_DEC_TGA;
James Simmons28b230e2007-05-08 00:37:50 -07001516
1517 /*
1518 * These are needed by fb_set_logo_truepalette(), so we
1519 * set them here for 24-plane cards.
1520 */
1521 if (tga_type != TGA_TYPE_8PLANE) {
1522 info->var.red.length = 8;
1523 info->var.green.length = 8;
1524 info->var.blue.length = 8;
1525 info->var.red.offset = 16;
1526 info->var.green.offset = 8;
1527 info->var.blue.offset = 0;
1528 }
1529}
1530
1531static int tgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
1532{
1533 /* We just use this to catch switches out of graphics mode. */
1534 tgafb_set_par(info); /* A bit of overkill for BASE_ADDR reset. */
1535 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536}
1537
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001538static int tgafb_register(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001540 static const struct fb_videomode modedb_tc = {
1541 /* 1280x1024 @ 72 Hz, 76.8 kHz hsync */
1542 "1280x1024@72", 0, 1280, 1024, 7645, 224, 28, 33, 3, 160, 3,
1543 FB_SYNC_ON_GREEN, FB_VMODE_NONINTERLACED
1544 };
1545
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 static unsigned int const fb_offset_presets[4] = {
1547 TGA_8PLANE_FB_OFFSET,
1548 TGA_24PLANE_FB_OFFSET,
1549 0xffffffff,
1550 TGA_24PLUSZ_FB_OFFSET
1551 };
1552
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001553 const struct fb_videomode *modedb_tga = NULL;
1554 resource_size_t bar0_start = 0, bar0_len = 0;
1555 const char *mode_option_tga = NULL;
Yijing Wang3ca356b2013-12-05 19:25:18 +08001556 int tga_bus_pci = dev_is_pci(dev);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001557 int tga_bus_tc = TGA_BUS_TC(dev);
1558 unsigned int modedbsize_tga = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 void __iomem *mem_base;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001560 struct fb_info *info;
1561 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 u8 tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001563 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
1565 /* Enable device in PCI config. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001566 if (tga_bus_pci && pci_enable_device(to_pci_dev(dev))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 printk(KERN_ERR "tgafb: Cannot enable PCI device\n");
1568 return -ENODEV;
1569 }
1570
1571 /* Allocate the fb and par structures. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001572 info = framebuffer_alloc(sizeof(struct tga_par), dev);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001573 if (!info) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 printk(KERN_ERR "tgafb: Cannot allocate memory\n");
1575 return -ENOMEM;
1576 }
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001577
1578 par = info->par;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001579 dev_set_drvdata(dev, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
1581 /* Request the mem regions. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 ret = -ENODEV;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001583 if (tga_bus_pci) {
1584 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1585 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1586 }
1587 if (tga_bus_tc) {
1588 bar0_start = to_tc_dev(dev)->resource.start;
1589 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1590 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 if (!request_mem_region (bar0_start, bar0_len, "tgafb")) {
1592 printk(KERN_ERR "tgafb: cannot reserve FB region\n");
1593 goto err0;
1594 }
1595
1596 /* Map the framebuffer. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001597 mem_base = ioremap_nocache(bar0_start, bar0_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 if (!mem_base) {
1599 printk(KERN_ERR "tgafb: Cannot map MMIO\n");
1600 goto err1;
1601 }
1602
1603 /* Grab info about the card. */
1604 tga_type = (readl(mem_base) >> 12) & 0x0f;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001605 par->dev = dev;
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001606 par->tga_mem_base = mem_base;
1607 par->tga_fb_base = mem_base + fb_offset_presets[tga_type];
1608 par->tga_regs_base = mem_base + TGA_REGS_OFFSET;
1609 par->tga_type = tga_type;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001610 if (tga_bus_pci)
Auke Kok44c10132007-06-08 15:46:36 -07001611 par->tga_chip_rev = (to_pci_dev(dev))->revision;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001612 if (tga_bus_tc)
1613 par->tga_chip_rev = TGA_READ_REG(par, TGA_START_REG) & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614
1615 /* Setup framebuffer. */
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001616 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
1617 FBINFO_HWACCEL_IMAGEBLIT | FBINFO_HWACCEL_FILLRECT;
1618 info->fbops = &tgafb_ops;
1619 info->screen_base = par->tga_fb_base;
Antonino A. Daplaseb3daa82007-07-17 04:05:41 -07001620 info->pseudo_palette = par->palette;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
1622 /* This should give a reasonable default video mode. */
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001623 if (tga_bus_pci) {
1624 mode_option_tga = mode_option_pci;
1625 }
1626 if (tga_bus_tc) {
1627 mode_option_tga = mode_option_tc;
1628 modedb_tga = &modedb_tc;
1629 modedbsize_tga = 1;
1630 }
1631 ret = fb_find_mode(&info->var, info,
1632 mode_option ? mode_option : mode_option_tga,
1633 modedb_tga, modedbsize_tga, NULL,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 tga_type == TGA_TYPE_8PLANE ? 8 : 32);
1635 if (ret == 0 || ret == 4) {
1636 printk(KERN_ERR "tgafb: Could not find valid video mode\n");
1637 ret = -EINVAL;
1638 goto err1;
1639 }
1640
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001641 if (fb_alloc_cmap(&info->cmap, 256, 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 printk(KERN_ERR "tgafb: Could not allocate color map\n");
1643 ret = -ENOMEM;
1644 goto err1;
1645 }
1646
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001647 tgafb_set_par(info);
1648 tgafb_init_fix(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001650 if (register_framebuffer(info) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 printk(KERN_ERR "tgafb: Could not register framebuffer\n");
1652 ret = -EINVAL;
Andres Salomon327fc872009-03-31 15:25:28 -07001653 goto err2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 }
1655
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001656 if (tga_bus_pci) {
1657 pr_info("tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
1658 par->tga_chip_rev);
1659 pr_info("tgafb: at PCI bus %d, device %d, function %d\n",
1660 to_pci_dev(dev)->bus->number,
1661 PCI_SLOT(to_pci_dev(dev)->devfn),
1662 PCI_FUNC(to_pci_dev(dev)->devfn));
1663 }
1664 if (tga_bus_tc)
1665 pr_info("tgafb: SFB+ detected, rev=0x%02x\n",
1666 par->tga_chip_rev);
Joe Perches31b67802013-09-19 18:35:55 -07001667 fb_info(info, "%s frame buffer device at 0x%lx\n",
1668 info->fix.id, (long)bar0_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 return 0;
1671
Andres Salomon327fc872009-03-31 15:25:28 -07001672 err2:
1673 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 err1:
Amol Lade4bf0512006-12-08 02:40:04 -08001675 if (mem_base)
1676 iounmap(mem_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 release_mem_region(bar0_start, bar0_len);
1678 err0:
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001679 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 return ret;
1681}
1682
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001683static void tgafb_unregister(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001685 resource_size_t bar0_start = 0, bar0_len = 0;
Yijing Wang3ca356b2013-12-05 19:25:18 +08001686 int tga_bus_pci = dev_is_pci(dev);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001687 int tga_bus_tc = TGA_BUS_TC(dev);
1688 struct fb_info *info = NULL;
1689 struct tga_par *par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001691 info = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 if (!info)
1693 return;
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001694
1695 par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 unregister_framebuffer(info);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001697 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 iounmap(par->tga_mem_base);
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001699 if (tga_bus_pci) {
1700 bar0_start = pci_resource_start(to_pci_dev(dev), 0);
1701 bar0_len = pci_resource_len(to_pci_dev(dev), 0);
1702 }
1703 if (tga_bus_tc) {
1704 bar0_start = to_tc_dev(dev)->resource.start;
1705 bar0_len = to_tc_dev(dev)->resource.end - bar0_start + 1;
1706 }
1707 release_mem_region(bar0_start, bar0_len);
Maciej W. Rozyckiee9a25e2007-02-12 00:54:52 -08001708 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709}
1710
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001711static void tgafb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001713 tc_unregister_driver(&tgafb_tc_driver);
1714 pci_unregister_driver(&tgafb_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716
1717#ifndef MODULE
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001718static int tgafb_setup(char *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719{
1720 char *this_opt;
1721
1722 if (arg && *arg) {
1723 while ((this_opt = strsep(&arg, ","))) {
1724 if (!*this_opt)
1725 continue;
1726 if (!strncmp(this_opt, "mode:", 5))
1727 mode_option = this_opt+5;
1728 else
1729 printk(KERN_ERR
1730 "tgafb: unknown parameter %s\n",
1731 this_opt);
1732 }
1733 }
1734
1735 return 0;
1736}
1737#endif /* !MODULE */
1738
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001739static int tgafb_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740{
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001741 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742#ifndef MODULE
1743 char *option = NULL;
1744
1745 if (fb_get_options("tgafb", &option))
1746 return -ENODEV;
1747 tgafb_setup(option);
1748#endif
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001749 status = pci_register_driver(&tgafb_pci_driver);
1750 if (!status)
1751 status = tc_register_driver(&tgafb_tc_driver);
1752 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753}
1754
1755/*
1756 * Modularisation
1757 */
1758
1759module_init(tgafb_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760module_exit(tgafb_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
Maciej W. Rozycki86c6f7d2007-05-08 00:37:48 -07001762MODULE_DESCRIPTION("Framebuffer driver for TGA/SFB+ chipset");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763MODULE_LICENSE("GPL");