blob: 15a9ee7cd734d3fa1147a9cacbe65cb06bf5b3e4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/delay.h>
43#include <linux/fb.h>
44#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
46#ifdef CONFIG_ZORRO
47#include <linux/zorro.h>
48#endif
49#ifdef CONFIG_PCI
50#include <linux/pci.h>
51#endif
52#ifdef CONFIG_AMIGA
53#include <asm/amigahw.h>
54#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070056#include <video/vga.h>
57#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Linus Torvalds1da177e2005-04-16 15:20:36 -070059/*****************************************************************
60 *
61 * debugging and utility macros
62 *
63 */
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065/* disable runtime assertions? */
66/* #define CIRRUSFB_NDEBUG */
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068/* debugging assertions */
69#ifndef CIRRUSFB_NDEBUG
70#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070071 if (!(expr)) { \
72 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070073 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070074 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070075#else
76#define assert(expr)
77#endif
78
Krzysztof Helt8503df62007-10-16 01:29:08 -070079#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Linus Torvalds1da177e2005-04-16 15:20:36 -070081/*****************************************************************
82 *
83 * chipset information
84 *
85 */
86
87/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070088enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070090 BT_SD64, /* GD5434 */
91 BT_PICCOLO, /* GD5426 */
92 BT_PICASSO, /* GD5426 or GD5428 */
93 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 BT_PICASSO4, /* GD5446 */
95 BT_ALPINE, /* GD543x/4x */
96 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -070097 BT_LAGUNA, /* GD5462/64 */
98 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -070099};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101/*
102 * per-board-type information, used for enumerating and abstracting
103 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700104 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 * use direct indexing on this array
106 * NOTE: '__initdata' cannot be used as some of this info
107 * is required at runtime. Maybe separate into an init-only and
108 * a run-time table?
109 */
110static const struct cirrusfb_board_info_rec {
111 char *name; /* ASCII name of chipset */
112 long maxclock[5]; /* maximum video clock */
113 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700114 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
115 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700116 /* construct bit 19 of screen start address */
117 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118
119 /* initial SR07 value, then for each mode */
120 unsigned char sr07;
121 unsigned char sr07_1bpp;
122 unsigned char sr07_1bpp_mux;
123 unsigned char sr07_8bpp;
124 unsigned char sr07_8bpp_mux;
125
126 unsigned char sr1f; /* SR1F VGA initial register value */
127} cirrusfb_board_info[] = {
128 [BT_SD64] = {
129 .name = "CL SD64",
130 .maxclock = {
131 /* guess */
132 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700133 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700135 .init_sr07 = true,
136 .init_sr1f = true,
137 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 .sr07 = 0xF0,
139 .sr07_1bpp = 0xF0,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700140 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700142 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700143 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 },
145 [BT_PICCOLO] = {
146 .name = "CL Piccolo",
147 .maxclock = {
148 /* guess */
149 90000, 90000, 90000, 90000, 90000
150 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700151 .init_sr07 = true,
152 .init_sr1f = true,
153 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 .sr07 = 0x80,
155 .sr07_1bpp = 0x80,
156 .sr07_8bpp = 0x81,
157 .sr1f = 0x22
158 },
159 [BT_PICASSO] = {
160 .name = "CL Picasso",
161 .maxclock = {
162 /* guess */
163 90000, 90000, 90000, 90000, 90000
164 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700165 .init_sr07 = true,
166 .init_sr1f = true,
167 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 .sr07 = 0x20,
169 .sr07_1bpp = 0x20,
170 .sr07_8bpp = 0x21,
171 .sr1f = 0x22
172 },
173 [BT_SPECTRUM] = {
174 .name = "CL Spectrum",
175 .maxclock = {
176 /* guess */
177 90000, 90000, 90000, 90000, 90000
178 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700179 .init_sr07 = true,
180 .init_sr1f = true,
181 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 .sr07 = 0x80,
183 .sr07_1bpp = 0x80,
184 .sr07_8bpp = 0x81,
185 .sr1f = 0x22
186 },
187 [BT_PICASSO4] = {
188 .name = "CL Picasso4",
189 .maxclock = {
190 135100, 135100, 85500, 85500, 0
191 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700192 .init_sr07 = true,
193 .init_sr1f = false,
194 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700195 .sr07 = 0xA0,
196 .sr07_1bpp = 0xA0,
197 .sr07_1bpp_mux = 0xA6,
198 .sr07_8bpp = 0xA1,
199 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr1f = 0
201 },
202 [BT_ALPINE] = {
203 .name = "CL Alpine",
204 .maxclock = {
205 /* for the GD5430. GD5446 can do more... */
206 85500, 85500, 50000, 28500, 0
207 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700208 .init_sr07 = true,
209 .init_sr1f = true,
210 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700212 .sr07_1bpp = 0xA0,
213 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 .sr07_8bpp = 0xA1,
215 .sr07_8bpp_mux = 0xA7,
216 .sr1f = 0x1C
217 },
218 [BT_GD5480] = {
219 .name = "CL GD5480",
220 .maxclock = {
221 135100, 200000, 200000, 135100, 135100
222 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700223 .init_sr07 = true,
224 .init_sr1f = true,
225 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 .sr07 = 0x10,
227 .sr07_1bpp = 0x11,
228 .sr07_8bpp = 0x11,
229 .sr1f = 0x1C
230 },
231 [BT_LAGUNA] = {
232 .name = "CL Laguna",
233 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700234 /* taken from X11 code */
235 170000, 170000, 170000, 170000, 135100,
236 },
237 .init_sr07 = false,
238 .init_sr1f = false,
239 .scrn_start_bit19 = true,
240 },
241 [BT_LAGUNAB] = {
242 .name = "CL Laguna AGP",
243 .maxclock = {
244 /* taken from X11 code */
245 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700247 .init_sr07 = false,
248 .init_sr1f = false,
249 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251};
252
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253#ifdef CONFIG_PCI
254#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000255 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700258 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700259 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
260 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700261 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
262 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
263 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
264 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
265 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
266 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
267 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700268 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 { 0, }
270};
271MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
272#undef CHIP
273#endif /* CONFIG_PCI */
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275#ifdef CONFIG_ZORRO
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200276struct zorrocl {
277 enum cirrus_board type; /* Board type */
278 u32 regoffset; /* Offset of registers in first Zorro device */
279 u32 ramsize; /* Size of video RAM in first Zorro device */
280 /* If zero, use autoprobe on RAM device */
281 u32 ramoffset; /* Offset of video RAM in first Zorro device */
282 zorro_id ramid; /* Zorro ID of RAM device */
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200283 zorro_id ramid2; /* Zorro ID of optional second RAM device */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200284};
285
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800286static const struct zorrocl zcl_sd64 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200287 .type = BT_SD64,
288 .ramid = ZORRO_PROD_HELFRICH_SD64_RAM,
289};
290
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800291static const struct zorrocl zcl_piccolo = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200292 .type = BT_PICCOLO,
293 .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
294};
295
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800296static const struct zorrocl zcl_picasso = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200297 .type = BT_PICASSO,
298 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
299};
300
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800301static const struct zorrocl zcl_spectrum = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200302 .type = BT_SPECTRUM,
303 .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
304};
305
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800306static const struct zorrocl zcl_picasso4_z3 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200307 .type = BT_PICASSO4,
308 .regoffset = 0x00600000,
309 .ramsize = 4 * MB_,
Geert Uytterhoevene78bb882011-10-20 13:42:25 +0200310 .ramoffset = 0x01000000, /* 0x02000000 for 64 MiB boards */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200311};
312
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800313static const struct zorrocl zcl_picasso4_z2 = {
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200314 .type = BT_PICASSO4,
315 .regoffset = 0x10000,
316 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1,
317 .ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2,
318};
319
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200320
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800321static const struct zorro_device_id cirrusfb_zorro_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200323 .id = ZORRO_PROD_HELFRICH_SD64_REG,
324 .driver_data = (unsigned long)&zcl_sd64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200326 .id = ZORRO_PROD_HELFRICH_PICCOLO_REG,
327 .driver_data = (unsigned long)&zcl_piccolo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200329 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
330 .driver_data = (unsigned long)&zcl_picasso,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200332 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
333 .driver_data = (unsigned long)&zcl_spectrum,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 }, {
335 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200336 .driver_data = (unsigned long)&zcl_picasso4_z3,
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200337 }, {
338 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG,
339 .driver_data = (unsigned long)&zcl_picasso4_z2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 },
341 { 0 }
342};
Geert Uytterhoevenbf54a2b2008-11-18 21:13:53 +0100343MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344#endif /* CONFIG_ZORRO */
345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700347enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700348 CRT,
349 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700350};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700351#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353/* info about board */
354struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700356 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700357 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 unsigned char SFR; /* Shadow of special function register */
359
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700360 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700361 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700363 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700365 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366};
367
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800368static bool noaccel;
369static char *mode_option = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371/****************************************************************************/
372/**** BEGIN PROTOTYPES ******************************************************/
373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
376 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700379static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700380static void switch_monitor(struct cirrusfb_info *cinfo, int on);
381static void WGen(const struct cirrusfb_info *cinfo,
382 int regnum, unsigned char val);
383static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
384static void AttrOn(const struct cirrusfb_info *cinfo);
385static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
386static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
387static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
388static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
389 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700391static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
392 unsigned char *red, unsigned char *green,
393 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700395static void cirrusfb_WaitBLT(u8 __iomem *regbase);
396static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
397 u_short curx, u_short cury,
398 u_short destx, u_short desty,
399 u_short width, u_short height,
400 u_short line_length);
401static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
402 u_short x, u_short y,
403 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700404 u32 fg_color, u32 bg_color,
405 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700407static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700410static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
411static void cirrusfb_dbg_print_regs(struct fb_info *info,
412 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700413 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414#endif /* CIRRUSFB_DEBUG */
415
416/*** END PROTOTYPES ********************************************************/
417/*****************************************************************************/
418/*** BEGIN Interface Used by the World ***************************************/
419
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700420static inline int is_laguna(const struct cirrusfb_info *cinfo)
421{
422 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
423}
424
Krzysztof Helt8503df62007-10-16 01:29:08 -0700425static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426
427/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700428static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
430 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700431 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 return 0;
433}
434
435/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700436static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437{
438 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700439 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 return 0;
441}
442
443/**** END Interface used by the World *************************************/
444/****************************************************************************/
445/**** BEGIN Hardware specific Routines **************************************/
446
Krzysztof Helt486ff382008-10-15 22:03:42 -0700447/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700448static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700450 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Krzysztof Helt486ff382008-10-15 22:03:42 -0700453 /* Read MCLK value */
454 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700455 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456
457 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700458 * should divide it by to get VCLK
459 */
460
461 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700462 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700463 return 1;
464 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700465 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700466 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
468
Krzysztof Helt486ff382008-10-15 22:03:42 -0700469 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470}
471
Krzysztof Helt99a45842009-03-31 15:25:09 -0700472static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
473 struct fb_info *info)
474{
475 long freq;
476 long maxclock;
477 struct cirrusfb_info *cinfo = info->par;
478 unsigned maxclockidx = var->bits_per_pixel >> 3;
479
480 /* convert from ps to kHz */
481 freq = PICOS2KHZ(var->pixclock);
482
483 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
484
485 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
486 cinfo->multiplexing = 0;
487
488 /* If the frequency is greater than we can support, we might be able
489 * to use multiplexing for the video mode */
490 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700491 dev_err(info->device,
492 "Frequency greater than maxclock (%ld kHz)\n",
493 maxclock);
494 return -EINVAL;
495 }
496 /*
497 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
498 * pixel clock
499 */
500 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700501 switch (cinfo->btype) {
502 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700503 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700504 case BT_PICASSO4:
505 if (freq > 85500)
506 cinfo->multiplexing = 1;
507 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700508 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700509 if (freq > 135100)
510 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700511 break;
512
513 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700514 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700515 }
516 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700517
518 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700519 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700520 cinfo->doubleVCLK = 0;
521 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
522 var->bits_per_pixel == 16) {
523 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700524 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700525
Krzysztof Helt99a45842009-03-31 15:25:09 -0700526 return 0;
527}
528
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529static int cirrusfb_check_var(struct fb_var_screeninfo *var,
530 struct fb_info *info)
531{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700532 int yres;
533 /* memory size in pixels */
534 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700535 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700538 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 var->red.offset = 0;
540 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700541 var->green = var->red;
542 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 break;
544
545 case 8:
546 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700547 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700548 var->green = var->red;
549 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 break;
551
552 case 16:
Paul Bolle933ee712013-03-27 00:47:03 +0000553 var->red.offset = 11;
554 var->green.offset = 5;
555 var->blue.offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700557 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 var->blue.length = 5;
559 break;
560
Krzysztof Helt7cade312009-03-31 15:25:13 -0700561 case 24:
Paul Bolle933ee712013-03-27 00:47:03 +0000562 var->red.offset = 16;
563 var->green.offset = 8;
564 var->blue.offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 var->red.length = 8;
566 var->green.length = 8;
567 var->blue.length = 8;
568 break;
569
570 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700571 dev_dbg(info->device,
572 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700573 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
575
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700576 if (var->xres_virtual < var->xres)
577 var->xres_virtual = var->xres;
578 /* use highest possible virtual resolution */
579 if (var->yres_virtual == -1) {
580 var->yres_virtual = pixels / var->xres_virtual;
581
582 dev_info(info->device,
583 "virtual resolution set to maximum of %dx%d\n",
584 var->xres_virtual, var->yres_virtual);
585 }
586 if (var->yres_virtual < var->yres)
587 var->yres_virtual = var->yres;
588
589 if (var->xres_virtual * var->yres_virtual > pixels) {
590 dev_err(info->device, "mode %dx%dx%d rejected... "
591 "virtual resolution too high to fit into video memory!\n",
592 var->xres_virtual, var->yres_virtual,
593 var->bits_per_pixel);
594 return -EINVAL;
595 }
596
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700597 /* truncate xoffset and yoffset to maximum if too high */
598 if (var->xoffset > var->xres_virtual - var->xres)
599 var->xoffset = var->xres_virtual - var->xres - 1;
600 if (var->yoffset > var->yres_virtual - var->yres)
601 var->yoffset = var->yres_virtual - var->yres - 1;
602
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 var->red.msb_right =
604 var->green.msb_right =
605 var->blue.msb_right =
606 var->transp.offset =
607 var->transp.length =
608 var->transp.msb_right = 0;
609
610 yres = var->yres;
611 if (var->vmode & FB_VMODE_DOUBLE)
612 yres *= 2;
613 else if (var->vmode & FB_VMODE_INTERLACED)
614 yres = (yres + 1) / 2;
615
616 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700617 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700618 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 return -EINVAL;
620 }
621
Krzysztof Helt99a45842009-03-31 15:25:09 -0700622 if (cirrusfb_check_pixclock(var, info))
623 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700625 if (!is_laguna(cinfo))
626 var->accel_flags = FB_ACCELF_TEXT;
627
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 return 0;
629}
630
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700631static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700633 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700634 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635
Krzysztof Helt8503df62007-10-16 01:29:08 -0700636 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700637 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638
Krzysztof Helt486ff382008-10-15 22:03:42 -0700639 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700640 dev_dbg(info->device, "Set %s as pixclock source.\n",
641 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700642 old1f |= 0x40;
643 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
644 if (div == 2)
645 old1e |= 1;
646
647 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700649 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650}
651
652/*************************************************************************
653 cirrusfb_set_par_foo()
654
655 actually writes the values for a new video mode into the hardware,
656**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700657static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658{
659 struct cirrusfb_info *cinfo = info->par;
660 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 u8 __iomem *regbase = cinfo->regbase;
662 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700663 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700665 int hdispend, hsyncstart, hsyncend, htotal;
666 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700667 long freq;
668 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700669 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700671 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700673
674 switch (var->bits_per_pixel) {
675 case 1:
676 info->fix.line_length = var->xres_virtual / 8;
677 info->fix.visual = FB_VISUAL_MONO10;
678 break;
679
680 case 8:
681 info->fix.line_length = var->xres_virtual;
682 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
683 break;
684
685 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700686 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700687 info->fix.line_length = var->xres_virtual *
688 var->bits_per_pixel >> 3;
689 info->fix.visual = FB_VISUAL_TRUECOLOR;
690 break;
691 }
692 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700694 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 bi = &cirrusfb_board_info[cinfo->btype];
697
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700698 hsyncstart = var->xres + var->right_margin;
699 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700700 htotal = (hsyncend + var->left_margin) / 8;
701 hdispend = var->xres / 8;
702 hsyncstart = hsyncstart / 8;
703 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700704
Krzysztof Helt8636a922009-03-31 15:25:17 -0700705 vdispend = var->yres;
706 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700707 vsyncend = vsyncstart + var->vsync_len;
708 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700709
710 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700711 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700712 vsyncstart *= 2;
713 vsyncend *= 2;
714 vtotal *= 2;
715 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700716 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700717 vsyncstart = (vsyncstart + 1) / 2;
718 vsyncend = (vsyncend + 1) / 2;
719 vtotal = (vtotal + 1) / 2;
720 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700721 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700722 if (yres >= 1024) {
723 vtotal /= 2;
724 vsyncstart /= 2;
725 vsyncend /= 2;
726 vdispend /= 2;
727 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700728
729 vdispend -= 1;
730 vsyncstart -= 1;
731 vsyncend -= 1;
732 vtotal -= 2;
733
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700734 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700735 htotal /= 2;
736 hsyncstart /= 2;
737 hsyncend /= 2;
738 hdispend /= 2;
739 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700740
741 htotal -= 5;
742 hdispend -= 1;
743 hsyncstart += 1;
744 hsyncend += 1;
745
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700747 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
749 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700751 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700753 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700756 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Krzysztof Helt8503df62007-10-16 01:29:08 -0700759 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700760 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700761 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700764 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700765 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 tmp = hsyncend % 32;
768 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700770 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700771 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700773 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700774 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775
776 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700777 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700779 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700785 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700791 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700792 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 tmp |= 0x20;
797 if (var->vmode & FB_VMODE_DOUBLE)
798 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700799 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700800 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700802 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700805 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700808 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700811 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700812 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700815 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700817 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700818 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
820 tmp = 0;
821 if (var->vmode & FB_VMODE_INTERLACED)
822 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700823 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700825 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700829 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 tmp |= 128;
831
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700832 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700833 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700835 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700836 if (var->bits_per_pixel == 24)
837 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
838 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700839 if (cinfo->multiplexing)
840 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700841 if (cinfo->doubleVCLK)
842 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700843
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700844 bestclock(freq, &nom, &den, &div);
845
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700846 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
847 freq, nom, den, div);
848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 /* set VCLK0 */
850 /* hardware RefClock: 14.31818 MHz */
851 /* formula: VClk = (OSC * N) / (D * (1+P)) */
852 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
853
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700854 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
855 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700856 /* if freq is close to mclk or mclk/2 select mclk
857 * as clock source
858 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700859 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700860 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700861 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700862 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700863 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700864 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700865 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
866 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
867 unsigned short tile_control;
868
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700869 if (cinfo->btype == BT_LAGUNAB) {
870 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
871 tile_control &= ~0x80;
872 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
873 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700874
875 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
876 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
877 control = fb_readw(cinfo->laguna_mmio + 0x402);
878 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
879 control &= ~0x6800;
880 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700881 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700882 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700883 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700884 tmp = den << 1;
885 if (div != 0)
886 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700887 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
888 if ((cinfo->btype == BT_SD64) ||
889 (cinfo->btype == BT_ALPINE) ||
890 (cinfo->btype == BT_GD5480))
891 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700893 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700894 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700895 vga_wseq(regbase, CL_SEQRE, tmp);
896 vga_wseq(regbase, CL_SEQR1E, nom);
897 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700898 vga_wseq(regbase, CL_SEQRE, nom);
899 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700900 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700901 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700903 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 else
907 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
908 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700909 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 /* don't know if it would hurt to also program this if no interlaced */
912 /* mode is used, but I feel better this way.. :-) */
913 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700914 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700918 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700919 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700920 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
922 tmp |= 0x40;
923 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
924 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 /* text cursor on and start line */
928 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
929 /* text cursor end line */
930 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 /******************************************************
933 *
934 * 1 bpp
935 *
936 */
937
938 /* programming for different color depths */
939 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700940 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700941 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 /* SR07 */
944 switch (cinfo->btype) {
945 case BT_SD64:
946 case BT_PICCOLO:
947 case BT_PICASSO:
948 case BT_SPECTRUM:
949 case BT_PICASSO4:
950 case BT_ALPINE:
951 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700953 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 bi->sr07_1bpp_mux : bi->sr07_1bpp);
955 break;
956
957 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700958 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700959 vga_wseq(regbase, CL_SEQR7,
960 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 break;
962
963 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700964 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 break;
966 }
967
968 /* Extended Sequencer Mode */
969 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700972 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700973 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
974 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 break;
976
977 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700978 /* ## vorher d0 avoid FIFO underruns..? */
979 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 break;
981
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700982 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 case BT_PICASSO4:
984 case BT_ALPINE:
985 case BT_GD5480:
986 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700987 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 /* do nothing */
989 break;
990
991 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700992 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 break;
994 }
995
Krzysztof Helt8503df62007-10-16 01:29:08 -0700996 /* pixel mask: pass-through for first plane */
997 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700998 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700999 /* hidden dac reg: 1280x1024 */
1000 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 /* hidden dac: nothing */
1003 WHDR(cinfo, 0);
1004 /* memory mode: odd/even, ext. memory */
1005 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1006 /* plane mask: only write to first plane */
1007 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 }
1009
1010 /******************************************************
1011 *
1012 * 8 bpp
1013 *
1014 */
1015
1016 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001017 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 switch (cinfo->btype) {
1019 case BT_SD64:
1020 case BT_PICCOLO:
1021 case BT_PICASSO:
1022 case BT_SPECTRUM:
1023 case BT_PICASSO4:
1024 case BT_ALPINE:
1025 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001026 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001027 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1029 break;
1030
1031 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001032 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001033 vga_wseq(regbase, CL_SEQR7,
1034 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001035 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 break;
1037
1038 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001039 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 break;
1041 }
1042
1043 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001047 /* Fast Page-Mode writes */
1048 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 break;
1050
1051 case BT_PICASSO4:
1052#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001053 /* ### INCOMPLETE!! */
1054 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001057 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 case BT_GD5480:
1059 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001060 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 /* do nothing */
1062 break;
1063
1064 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001065 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 break;
1067 }
1068
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 /* mode register: 256 color mode */
1070 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001071 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001072 /* hidden dac reg: 1280x1024 */
1073 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001075 /* hidden dac: nothing */
1076 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 }
1078
1079 /******************************************************
1080 *
1081 * 16 bpp
1082 *
1083 */
1084
1085 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001086 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001089 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001090 vga_wseq(regbase, CL_SEQR7, 0x87);
1091 /* Fast Page-Mode writes */
1092 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 break;
1094
1095 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001096 vga_wseq(regbase, CL_SEQR7, 0x27);
1097 /* Fast Page-Mode writes */
1098 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 break;
1100
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001101 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001104 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001105 vga_wseq(regbase, CL_SEQR7,
1106 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 break;
1108
1109 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001110 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 /* We already set SRF and SR1F */
1112 break;
1113
1114 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001115 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 vga_wseq(regbase, CL_SEQR7,
1117 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001118 control |= 0x2000;
1119 format |= 0x1400;
1120 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122
1123 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001124 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126 }
1127
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 /* mode register: 256 color mode */
1129 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001131 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132#elif defined(CONFIG_ZORRO)
1133 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 }
1137
1138 /******************************************************
1139 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001140 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 *
1142 */
1143
Krzysztof Helt7cade312009-03-31 15:25:13 -07001144 else if (var->bits_per_pixel == 24) {
1145 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001146 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001148 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001149 vga_wseq(regbase, CL_SEQR7, 0x85);
1150 /* Fast Page-Mode writes */
1151 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 break;
1153
1154 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 vga_wseq(regbase, CL_SEQR7, 0x25);
1156 /* Fast Page-Mode writes */
1157 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 break;
1159
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001160 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001163 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001164 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 break;
1166
1167 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001168 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 /* We already set SRF and SR1F */
1170 break;
1171
1172 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001173 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001174 vga_wseq(regbase, CL_SEQR7,
1175 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001176 control |= 0x4000;
1177 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001178 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 break;
1180
1181 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001182 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184 }
1185
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 /* mode register: 256 color mode */
1187 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1189 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 }
1191
1192 /******************************************************
1193 *
1194 * unknown/unsupported bpp
1195 *
1196 */
1197
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001199 dev_err(info->device,
1200 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Krzysztof Helt6683e012009-03-31 15:25:06 -07001203 pitch = info->fix.line_length >> 3;
1204 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001206 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 tmp |= 0x10; /* offset overflow bit */
1208
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 /* screen start addr #16-18, fastpagemode cycles */
1210 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001212 /* screen start address bit 19 */
1213 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001214 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001216 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001217 tmp = 0;
1218 if ((htotal + 5) & 256)
1219 tmp |= 128;
1220 if (hdispend & 256)
1221 tmp |= 64;
1222 if (hsyncstart & 256)
1223 tmp |= 48;
1224 if (vtotal & 1024)
1225 tmp |= 8;
1226 if (vdispend & 1024)
1227 tmp |= 4;
1228 if (vsyncstart & 1024)
1229 tmp |= 3;
1230
1231 vga_wcrt(regbase, CL_CRT1E, tmp);
1232 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1233 }
1234
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 /* pixel panning */
1236 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
1238 /* [ EGS: SetOffset(); ] */
1239 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001240 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001242 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001243 /* no tiles */
1244 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1245 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1246 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1247 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 /* finally, turn on everything - turn off "FullBandwidth" bit */
1249 /* also, set "DotClock%2" bit where requested */
1250 tmp = 0x01;
1251
1252/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1253 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1254 tmp |= 0x08;
1255*/
1256
Krzysztof Helt8503df62007-10-16 01:29:08 -07001257 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001258 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001261 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262#endif
1263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 return 0;
1265}
1266
1267/* for some reason incomprehensible to me, cirrusfb requires that you write
1268 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001269static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001271 cirrusfb_set_par_foo(info);
1272 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273}
1274
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1276 unsigned blue, unsigned transp,
1277 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278{
1279 struct cirrusfb_info *cinfo = info->par;
1280
1281 if (regno > 255)
1282 return -EINVAL;
1283
1284 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1285 u32 v;
1286 red >>= (16 - info->var.red.length);
1287 green >>= (16 - info->var.green.length);
1288 blue >>= (16 - info->var.blue.length);
1289
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 return 1;
1292 v = (red << info->var.red.offset) |
1293 (green << info->var.green.offset) |
1294 (blue << info->var.blue.offset);
1295
Krzysztof Helt060b6002007-10-16 01:29:13 -07001296 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 return 0;
1298 }
1299
Krzysztof Helt8503df62007-10-16 01:29:08 -07001300 if (info->var.bits_per_pixel == 8)
1301 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 return 0;
1304
1305}
1306
1307/*************************************************************************
1308 cirrusfb_pan_display()
1309
1310 performs display panning - provided hardware permits this
1311**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001312static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1313 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001315 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001317 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 struct cirrusfb_info *cinfo = info->par;
1319
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 /* no range checks for xoffset and yoffset, */
1321 /* as fb_pan_display has already done this */
1322 if (var->vmode & FB_VMODE_YWRAP)
1323 return -EINVAL;
1324
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Krzysztof Helt99a45842009-03-31 15:25:09 -07001327 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
1329 if (info->var.bits_per_pixel == 1) {
1330 /* base is already correct */
1331 xpix = (unsigned char) (var->xoffset % 8);
1332 } else {
1333 base /= 4;
1334 xpix = (unsigned char) ((xoffset % 4) * 2);
1335 }
1336
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001337 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001338 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001341 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1342 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001344 /* 0xf2 is %11110010, exclude tmp bits */
1345 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 /* construct bits 16, 17 and 18 of screen start address */
1347 if (base & 0x10000)
1348 tmp |= 0x01;
1349 if (base & 0x20000)
1350 tmp |= 0x04;
1351 if (base & 0x40000)
1352 tmp |= 0x08;
1353
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001354 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001357 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001358 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1359 if (is_laguna(cinfo))
1360 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1361 else
1362 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001363 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 /* write pixel panning value to AR33; this does not quite work in 8bpp
1367 *
1368 * ### Piccolo..? Will this work?
1369 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
Krzysztof Helt8503df62007-10-16 01:29:08 -07001373 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374}
1375
Krzysztof Helt8503df62007-10-16 01:29:08 -07001376static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377{
1378 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1380 * then the caller blanks by setting the CLUT (Color Look Up Table)
1381 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1382 * failed due to e.g. a video mode which doesn't support it.
1383 * Implements VESA suspend and powerdown modes on hardware that
1384 * supports disabling hsync/vsync:
1385 * blank_mode == 2: suspend vsync
1386 * blank_mode == 3: suspend hsync
1387 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 */
1389 unsigned char val;
1390 struct cirrusfb_info *cinfo = info->par;
1391 int current_mode = cinfo->blank_mode;
1392
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001393 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394
1395 if (info->state != FBINFO_STATE_RUNNING ||
1396 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001397 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 return 0;
1399 }
1400
1401 /* Undo current */
1402 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001403 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001404 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001405 val = 0;
1406 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001407 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001408 val = 0x20;
1409
1410 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1411 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413 switch (blank_mode) {
1414 case FB_BLANK_UNBLANK:
1415 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001416 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 break;
1418 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001419 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 break;
1421 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001422 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 break;
1424 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001425 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 break;
1427 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001428 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 return 1;
1430 }
1431
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 vga_wgfx(cinfo->regbase, CL_GRE, val);
1433
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001435 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
1437 /* Let fbcon do a soft blank for us */
1438 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1439}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441/**** END Hardware specific Routines **************************************/
1442/****************************************************************************/
1443/**** BEGIN Internal Routines ***********************************************/
1444
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001445static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001447 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 const struct cirrusfb_board_info_rec *bi;
1449
Krzysztof Helt8503df62007-10-16 01:29:08 -07001450 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451
1452 bi = &cirrusfb_board_info[cinfo->btype];
1453
1454 /* reset board globally */
1455 switch (cinfo->btype) {
1456 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001457 WSFR(cinfo, 0x01);
1458 udelay(500);
1459 WSFR(cinfo, 0x51);
1460 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 break;
1462 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 WSFR2(cinfo, 0xff);
1464 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 break;
1466 case BT_SD64:
1467 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468 WSFR(cinfo, 0x1f);
1469 udelay(500);
1470 WSFR(cinfo, 0x4f);
1471 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 break;
1473 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 /* disable flickerfixer */
1475 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1476 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 /* mode */
1478 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05001479 fallthrough;
Gustavo A. R. Silva8c152202017-11-09 18:09:32 +01001480 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 /* from Klaus' NetBSD driver: */
1482 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05001483 fallthrough;
Gustavo A. R. Silva8c152202017-11-09 18:09:32 +01001484 case BT_ALPINE:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001485 /* put blitter into 542x compat */
1486 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 break;
1488
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001489 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001490 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 /* Nothing to do to reset the board. */
1492 break;
1493
1494 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001495 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 break;
1497 }
1498
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001499 /* make sure RAM size set by this point */
1500 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
1502 /* the P4 is not fully initialized here; I rely on it having been */
1503 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1508 WGen(cinfo, CL_POS102, 0x01);
1509 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510
1511 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
Krzysztof Helt8503df62007-10-16 01:29:08 -07001514 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001515 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Krzysztof Helt8503df62007-10-16 01:29:08 -07001517 /* FullBandwidth (video off) and 8/9 dot clock */
1518 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
Krzysztof Helt8503df62007-10-16 01:29:08 -07001520 /* "magic cookie" - doesn't make any sense to me.. */
1521/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1522 /* unlock all extension registers */
1523 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 switch (cinfo->btype) {
1526 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001527 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 break;
1529 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001530 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001531 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 break;
1533 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001534#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001536#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 break;
1538 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1540 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 }
1543 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001544 /* plane mask: nothing */
1545 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1546 /* character map select: doesn't even matter in gx mode */
1547 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001548 /* memory mode: chain4, ext. memory */
1549 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550
1551 /* controller-internal base address of video memory */
1552 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1556 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
Krzysztof Helt8503df62007-10-16 01:29:08 -07001558 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1559 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1560 /* graphics cursor Y position (..."... ) */
1561 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1562 /* graphics cursor attributes */
1563 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1564 /* graphics cursor pattern address */
1565 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566
1567 /* writing these on a P4 might give problems.. */
1568 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001569 /* configuration readback and ext. color */
1570 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1571 /* signature generator */
1572 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 }
1574
Krzysztof Helt8503df62007-10-16 01:29:08 -07001575 /* Screen A preset row scan: none */
1576 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1577 /* Text cursor start: disable text cursor */
1578 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1579 /* Text cursor end: - */
1580 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 /* text cursor location high: 0 */
1582 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1583 /* text cursor location low: 0 */
1584 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585
Krzysztof Helt8503df62007-10-16 01:29:08 -07001586 /* Underline Row scanline: - */
1587 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001589 /* ext. display controls: ext.adr. wrap */
1590 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
Masanari Iidaff0c2642012-07-22 00:23:15 +09001592 /* Set/Reset registers: - */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1594 /* Set/Reset enable: - */
1595 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1596 /* Color Compare: - */
1597 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1598 /* Data Rotate: - */
1599 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1600 /* Read Map Select: - */
1601 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1602 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1603 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1604 /* Miscellaneous: memory map base address, graphics mode */
1605 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1606 /* Color Don't care: involve all planes */
1607 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1608 /* Bit Mask: no mask at all */
1609 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001610
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001611 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1612 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001613 /* (5434 can't have bit 3 set for bitblt) */
1614 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* Graphics controller mode extensions: finer granularity,
1617 * 8byte data latches
1618 */
1619 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620
Krzysztof Helt8503df62007-10-16 01:29:08 -07001621 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1622 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1623 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1624 /* Background color byte 1: - */
1625 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1626 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
Krzysztof Helt8503df62007-10-16 01:29:08 -07001628 /* Attribute Controller palette registers: "identity mapping" */
1629 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Krzysztof Helt8503df62007-10-16 01:29:08 -07001646 /* Attribute Controller mode: graphics mode */
1647 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1648 /* Overscan color reg.: reg. 0 */
1649 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1650 /* Color Plane enable: Enable all 4 planes */
1651 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 /* Color Select: - */
1653 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 /* BLT Start/status: Blitter reset */
1658 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1659 /* - " - : "end-of-reset" */
1660 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
1662 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001663 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 return;
1665}
1666
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668{
1669#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1670 static int IsOn = 0; /* XXX not ok for multiple boards */
1671
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 if (cinfo->btype == BT_PICASSO4)
1673 return; /* nothing to switch */
1674 if (cinfo->btype == BT_ALPINE)
1675 return; /* nothing to switch */
1676 if (cinfo->btype == BT_GD5480)
1677 return; /* nothing to switch */
1678 if (cinfo->btype == BT_PICASSO) {
1679 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 return;
1682 }
1683 if (on) {
1684 switch (cinfo->btype) {
1685 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001686 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 break;
1688 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 break;
1691 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001692 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 break;
1694 default: /* do nothing */ break;
1695 }
1696 } else {
1697 switch (cinfo->btype) {
1698 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001699 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 break;
1701 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703 break;
1704 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001707 default: /* do nothing */
1708 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 }
1710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711#endif /* CONFIG_ZORRO */
1712}
1713
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714/******************************************/
1715/* Linux 2.6-style accelerated functions */
1716/******************************************/
1717
Krzysztof Helt8343c892009-03-31 15:25:11 -07001718static int cirrusfb_sync(struct fb_info *info)
1719{
1720 struct cirrusfb_info *cinfo = info->par;
1721
1722 if (!is_laguna(cinfo)) {
1723 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1724 cpu_relax();
1725 }
1726 return 0;
1727}
1728
Krzysztof Helt8503df62007-10-16 01:29:08 -07001729static void cirrusfb_fillrect(struct fb_info *info,
1730 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 struct fb_fillrect modded;
1733 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001734 struct cirrusfb_info *cinfo = info->par;
1735 int m = info->var.bits_per_pixel;
1736 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1737 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738
1739 if (info->state != FBINFO_STATE_RUNNING)
1740 return;
1741 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1742 cfb_fillrect(info, region);
1743 return;
1744 }
1745
1746 vxres = info->var.xres_virtual;
1747 vyres = info->var.yres_virtual;
1748
1749 memcpy(&modded, region, sizeof(struct fb_fillrect));
1750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 modded.dx >= vxres || modded.dy >= vyres)
1753 return;
1754
Krzysztof Helt8503df62007-10-16 01:29:08 -07001755 if (modded.dx + modded.width > vxres)
1756 modded.width = vxres - modded.dx;
1757 if (modded.dy + modded.height > vyres)
1758 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
Krzysztof Helt060b6002007-10-16 01:29:13 -07001760 cirrusfb_RectFill(cinfo->regbase,
1761 info->var.bits_per_pixel,
1762 (region->dx * m) / 8, region->dy,
1763 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001764 color, color,
1765 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766}
1767
Krzysztof Helt8503df62007-10-16 01:29:08 -07001768static void cirrusfb_copyarea(struct fb_info *info,
1769 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771 struct fb_copyarea modded;
1772 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001773 struct cirrusfb_info *cinfo = info->par;
1774 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
1776 if (info->state != FBINFO_STATE_RUNNING)
1777 return;
1778 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1779 cfb_copyarea(info, area);
1780 return;
1781 }
1782
1783 vxres = info->var.xres_virtual;
1784 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001785 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786
Krzysztof Helt8503df62007-10-16 01:29:08 -07001787 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 modded.sx >= vxres || modded.sy >= vyres ||
1789 modded.dx >= vxres || modded.dy >= vyres)
1790 return;
1791
Krzysztof Helt8503df62007-10-16 01:29:08 -07001792 if (modded.sx + modded.width > vxres)
1793 modded.width = vxres - modded.sx;
1794 if (modded.dx + modded.width > vxres)
1795 modded.width = vxres - modded.dx;
1796 if (modded.sy + modded.height > vyres)
1797 modded.height = vyres - modded.sy;
1798 if (modded.dy + modded.height > vyres)
1799 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Krzysztof Helt060b6002007-10-16 01:29:13 -07001801 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1802 (area->sx * m) / 8, area->sy,
1803 (area->dx * m) / 8, area->dy,
1804 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001805 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001806
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807}
1808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809static void cirrusfb_imageblit(struct fb_info *info,
1810 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811{
1812 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001813 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Krzysztof Helt9e848062009-03-31 15:25:11 -07001815 if (info->state != FBINFO_STATE_RUNNING)
1816 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001817 /* Alpine/SD64 does not work at 24bpp ??? */
1818 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1819 cfb_imageblit(info, image);
1820 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1821 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001822 cfb_imageblit(info, image);
1823 else {
1824 unsigned size = ((image->width + 7) >> 3) * image->height;
1825 int m = info->var.bits_per_pixel;
1826 u32 fg, bg;
1827
1828 if (info->var.bits_per_pixel == 8) {
1829 fg = image->fg_color;
1830 bg = image->bg_color;
1831 } else {
1832 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1833 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1834 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001835 if (info->var.bits_per_pixel == 24) {
1836 /* clear background first */
1837 cirrusfb_RectFill(cinfo->regbase,
1838 info->var.bits_per_pixel,
1839 (image->dx * m) / 8, image->dy,
1840 (image->width * m) / 8,
1841 image->height,
1842 bg, bg,
1843 info->fix.line_length, 0x40);
1844 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001845 cirrusfb_RectFill(cinfo->regbase,
1846 info->var.bits_per_pixel,
1847 (image->dx * m) / 8, image->dy,
1848 (image->width * m) / 8, image->height,
1849 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001850 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001851 memcpy(info->screen_base, image->data, size);
1852 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853}
1854
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001856static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857
1858/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1859 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1860 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1861 * seem to have. */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001862static unsigned int cirrusfb_get_memsize(struct fb_info *info,
1863 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864{
1865 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001866 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001868 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001869 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1870
1871 mem = ((SR14 & 7) + 1) << 20;
1872 } else {
1873 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1874 switch ((SRF & 0x18)) {
1875 case 0x08:
1876 mem = 512 * 1024;
1877 break;
1878 case 0x10:
1879 mem = 1024 * 1024;
1880 break;
1881 /* 64-bit DRAM data bus width; assume 2MB.
1882 * Also indicates 2MB memory on the 5430.
1883 */
1884 case 0x18:
1885 mem = 2048 * 1024;
1886 break;
1887 default:
1888 dev_warn(info->device, "Unknown memory size!\n");
1889 mem = 1024 * 1024;
1890 }
1891 /* If DRAM bank switching is enabled, there must be
1892 * twice as much memory installed. (4MB on the 5434)
1893 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001894 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001895 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 return mem;
1900}
1901
Krzysztof Helt8503df62007-10-16 01:29:08 -07001902static void get_pci_addrs(const struct pci_dev *pdev,
1903 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001905 assert(pdev != NULL);
1906 assert(display != NULL);
1907 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 *display = 0;
1910 *registers = 0;
1911
1912 /* This is a best-guess for now */
1913
1914 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1915 *display = pci_resource_start(pdev, 1);
1916 *registers = pci_resource_start(pdev, 0);
1917 } else {
1918 *display = pci_resource_start(pdev, 0);
1919 *registers = pci_resource_start(pdev, 1);
1920 }
1921
Krzysztof Helt8503df62007-10-16 01:29:08 -07001922 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001923}
1924
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001925static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001927 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001928 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001930 if (cinfo->laguna_mmio == NULL)
1931 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001932 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933#if 0 /* if system didn't claim this region, we would... */
1934 release_mem_region(0xA0000, 65535);
1935#endif
1936 if (release_io_ports)
1937 release_region(0x3C0, 32);
1938 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939}
1940#endif /* CONFIG_PCI */
1941
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001943static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
Al Virod91f5bb2007-10-17 00:27:18 +01001945 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001946 struct zorro_dev *zdev = to_zorro_dev(info->device);
1947
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001948 if (info->fix.smem_start > 16 * MB_)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001949 iounmap(info->screen_base);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001950 if (info->fix.mmio_start > 16 * MB_)
1951 iounmap(cinfo->regbase);
1952
1953 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954}
1955#endif /* CONFIG_ZORRO */
1956
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001957/* function table of the above functions */
Jani Nikula8a48ac332019-12-03 18:38:50 +02001958static const struct fb_ops cirrusfb_ops = {
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001959 .owner = THIS_MODULE,
1960 .fb_open = cirrusfb_open,
1961 .fb_release = cirrusfb_release,
1962 .fb_setcolreg = cirrusfb_setcolreg,
1963 .fb_check_var = cirrusfb_check_var,
1964 .fb_set_par = cirrusfb_set_par,
1965 .fb_pan_display = cirrusfb_pan_display,
1966 .fb_blank = cirrusfb_blank,
1967 .fb_fillrect = cirrusfb_fillrect,
1968 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001969 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001970 .fb_imageblit = cirrusfb_imageblit,
1971};
1972
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001973static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001975 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001976 struct fb_var_screeninfo *var = &info->var;
1977
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978 info->pseudo_palette = cinfo->pseudo_palette;
1979 info->flags = FBINFO_DEFAULT
1980 | FBINFO_HWACCEL_XPAN
1981 | FBINFO_HWACCEL_YPAN
1982 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001983 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001985 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001987 info->fix.accel = FB_ACCEL_NONE;
1988 } else
1989 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
1990
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07001992
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 if (cinfo->btype == BT_GD5480) {
1994 if (var->bits_per_pixel == 16)
1995 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001996 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 info->screen_base += 2 * MB_;
1998 }
1999
2000 /* Fill fix common fields */
2001 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2002 sizeof(info->fix.id));
2003
2004 /* monochrome: only 1 memory plane */
2005 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002006 info->fix.smem_len = info->screen_size;
2007 if (var->bits_per_pixel == 1)
2008 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 info->fix.xpanstep = 1;
2011 info->fix.ypanstep = 1;
2012 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013
2014 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
2017 fb_alloc_cmap(&info->cmap, 256, 0);
2018
2019 return 0;
2020}
2021
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002022static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002024 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
2027 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002028 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002030 /* set all the vital stuff */
2031 cirrusfb_set_fbinfo(info);
2032
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002033 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002035 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2036 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002037 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002038 err = -EINVAL;
2039 goto err_dealloc_cmap;
2040 }
2041
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 info->var.activate = FB_ACTIVATE_NOW;
2043
Krzysztof Helt99a45842009-03-31 15:25:09 -07002044 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 if (err < 0) {
2046 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002047 dev_dbg(info->device,
2048 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002049 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 }
2051
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 err = register_framebuffer(info);
2053 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002054 dev_err(info->device,
2055 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 goto err_dealloc_cmap;
2057 }
2058
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return 0;
2060
2061err_dealloc_cmap:
2062 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 return err;
2064}
2065
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002066static void cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
2068 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069
Krzysztof Helt8503df62007-10-16 01:29:08 -07002070 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002071 unregister_framebuffer(info);
2072 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002073 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002074 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002075 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076}
2077
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078#ifdef CONFIG_PCI
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002079static int cirrusfb_pci_register(struct pci_dev *pdev,
2080 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081{
2082 struct cirrusfb_info *cinfo;
2083 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 unsigned long board_addr, board_size;
2085 int ret;
2086
2087 ret = pci_enable_device(pdev);
2088 if (ret < 0) {
2089 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2090 goto err_out;
2091 }
2092
2093 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2094 if (!info) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002096 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 }
2098
2099 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002100 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002102 dev_dbg(info->device,
2103 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002104 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002105 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2106 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
Paul Bolle933ee712013-03-27 00:47:03 +00002108 dev_dbg(info->device,
2109 "Attempt to get PCI info for Cirrus Graphics Card\n");
2110 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
2111 /* FIXME: this forces VGA. alternatives? */
2112 cinfo->regbase = NULL;
2113 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002115 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002116 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002118 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002119 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120
2121 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002123 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2124 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 goto err_release_fb;
2126 }
2127#if 0 /* if the system didn't claim this region, we would... */
2128 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002129 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2130 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 ret = -EBUSY;
2132 goto err_release_regions;
2133 }
2134#endif
2135 if (request_region(0x3C0, 32, "cirrusfb"))
2136 release_io_ports = 1;
2137
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002138 info->screen_base = ioremap(board_addr, board_size);
2139 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 ret = -EIO;
2141 goto err_release_legacy;
2142 }
2143
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002144 info->fix.smem_start = board_addr;
2145 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 cinfo->unmap = cirrusfb_pci_unmap;
2147
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002148 dev_info(info->device,
2149 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2150 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 pci_set_drvdata(pdev, info);
2152
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002153 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002154 if (!ret)
2155 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002157 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158err_release_legacy:
2159 if (release_io_ports)
2160 release_region(0x3C0, 32);
2161#if 0
2162 release_mem_region(0xA0000, 65535);
2163err_release_regions:
2164#endif
2165 pci_release_regions(pdev);
2166err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002167 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002168 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170err_out:
2171 return ret;
2172}
2173
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002174static void cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175{
2176 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177
Krzysztof Helt8503df62007-10-16 01:29:08 -07002178 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179}
2180
2181static struct pci_driver cirrusfb_pci_driver = {
2182 .name = "cirrusfb",
2183 .id_table = cirrusfb_pci_table,
2184 .probe = cirrusfb_pci_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002185 .remove = cirrusfb_pci_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186#ifdef CONFIG_PM
2187#if 0
2188 .suspend = cirrusfb_pci_suspend,
2189 .resume = cirrusfb_pci_resume,
2190#endif
2191#endif
2192};
2193#endif /* CONFIG_PCI */
2194
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195#ifdef CONFIG_ZORRO
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002196static int cirrusfb_zorro_register(struct zorro_dev *z,
2197 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199 struct fb_info *info;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002200 int error;
2201 const struct zorrocl *zcl;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002202 enum cirrus_board btype;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002203 unsigned long regbase, ramsize, rambase;
2204 struct cirrusfb_info *cinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205
2206 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
Bartlomiej Zolnierkiewicz0adcdbc2019-06-28 12:30:08 +02002207 if (!info)
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002208 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002210 zcl = (const struct zorrocl *)ent->driver_data;
2211 btype = zcl->type;
2212 regbase = zorro_resource_start(z) + zcl->regoffset;
2213 ramsize = zcl->ramsize;
2214 if (ramsize) {
2215 rambase = zorro_resource_start(z) + zcl->ramoffset;
Geert Uytterhoevene78bb882011-10-20 13:42:25 +02002216 if (zorro_resource_len(z) == 64 * MB_) {
2217 /* Quirk for 64 MiB Picasso IV */
2218 rambase += zcl->ramoffset;
2219 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002220 } else {
2221 struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
2222 if (!ram || !zorro_resource_len(ram)) {
2223 dev_err(info->device, "No video RAM found\n");
2224 error = -ENODEV;
2225 goto err_release_fb;
2226 }
2227 rambase = zorro_resource_start(ram);
2228 ramsize = zorro_resource_len(ram);
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +02002229 if (zcl->ramid2 &&
2230 (ram = zorro_find_device(zcl->ramid2, NULL))) {
2231 if (zorro_resource_start(ram) != rambase + ramsize) {
2232 dev_warn(info->device,
2233 "Skipping non-contiguous RAM at %pR\n",
2234 &ram->resource);
2235 } else {
2236 ramsize += zorro_resource_len(ram);
2237 }
2238 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002239 }
2240
2241 dev_info(info->device,
2242 "%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
2243 cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
2244 rambase);
2245
2246 if (!zorro_request_device(z, "cirrusfb")) {
2247 dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
2248 error = -EBUSY;
2249 goto err_release_fb;
2250 }
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002251
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 cinfo->btype = btype;
2254
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002255 info->fix.mmio_start = regbase;
2256 cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
Geert Uytterhoeven6112ea02011-01-09 11:03:43 +01002257 : ZTWO_VADDR(regbase);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002258 if (!cinfo->regbase) {
2259 dev_err(info->device, "Cannot map registers\n");
2260 error = -EIO;
2261 goto err_release_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 }
2263
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002264 info->fix.smem_start = rambase;
2265 info->screen_size = ramsize;
2266 info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
Geert Uytterhoeven6112ea02011-01-09 11:03:43 +01002267 : ZTWO_VADDR(rambase);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002268 if (!info->screen_base) {
2269 dev_err(info->device, "Cannot map video RAM\n");
2270 error = -EIO;
2271 goto err_unmap_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002273
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 cinfo->unmap = cirrusfb_zorro_unmap;
2275
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002276 dev_info(info->device,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002277 "Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
2278 ramsize / MB_, rambase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002280 /* MCLK select etc. */
2281 if (cirrusfb_board_info[btype].init_sr1f)
2282 vga_wseq(cinfo->regbase, CL_SEQR1F,
2283 cirrusfb_board_info[btype].sr1f);
2284
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002285 error = cirrusfb_register(info);
2286 if (error) {
2287 dev_err(info->device, "Failed to register device, error %d\n",
2288 error);
2289 goto err_unmap_ram;
2290 }
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002291
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002292 zorro_set_drvdata(z, info);
2293 return 0;
2294
2295err_unmap_ram:
2296 if (rambase > 16 * MB_)
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002297 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002299err_unmap_reg:
2300 if (regbase > 16 * MB_)
2301 iounmap(cinfo->regbase);
2302err_release_dev:
2303 zorro_release_device(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304err_release_fb:
2305 framebuffer_release(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002306 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307}
2308
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002309void cirrusfb_zorro_unregister(struct zorro_dev *z)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310{
2311 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312
Krzysztof Helt8503df62007-10-16 01:29:08 -07002313 cirrusfb_cleanup(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002314 zorro_set_drvdata(z, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315}
2316
2317static struct zorro_driver cirrusfb_zorro_driver = {
2318 .name = "cirrusfb",
2319 .id_table = cirrusfb_zorro_table,
2320 .probe = cirrusfb_zorro_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002321 .remove = cirrusfb_zorro_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322};
2323#endif /* CONFIG_ZORRO */
2324
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002326static int __init cirrusfb_setup(char *options)
2327{
Vlada Pericee119402008-11-19 15:36:45 -08002328 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330 if (!options || !*options)
2331 return 0;
2332
Krzysztof Helt8503df62007-10-16 01:29:08 -07002333 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002334 if (!*this_opt)
2335 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 if (!strcmp(this_opt, "noaccel"))
2338 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002339 else if (!strncmp(this_opt, "mode:", 5))
2340 mode_option = this_opt + 5;
2341 else
2342 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 }
2344 return 0;
2345}
2346#endif
2347
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 /*
2349 * Modularization
2350 */
2351
2352MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2353MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2354MODULE_LICENSE("GPL");
2355
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002356static int __init cirrusfb_init(void)
2357{
2358 int error = 0;
2359
2360#ifndef MODULE
2361 char *option = NULL;
2362
2363 if (fb_get_options("cirrusfb", &option))
2364 return -ENODEV;
2365 cirrusfb_setup(option);
2366#endif
2367
2368#ifdef CONFIG_ZORRO
2369 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2370#endif
2371#ifdef CONFIG_PCI
2372 error |= pci_register_driver(&cirrusfb_pci_driver);
2373#endif
2374 return error;
2375}
2376
Krzysztof Helt8503df62007-10-16 01:29:08 -07002377static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378{
2379#ifdef CONFIG_PCI
2380 pci_unregister_driver(&cirrusfb_pci_driver);
2381#endif
2382#ifdef CONFIG_ZORRO
2383 zorro_unregister_driver(&cirrusfb_zorro_driver);
2384#endif
2385}
2386
2387module_init(cirrusfb_init);
2388
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002389module_param(mode_option, charp, 0);
2390MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002391module_param(noaccel, bool, 0);
2392MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002393
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394#ifdef MODULE
2395module_exit(cirrusfb_exit);
2396#endif
2397
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398/**********************************************************************/
2399/* about the following functions - I have used the same names for the */
2400/* functions as Markus Wild did in his Retina driver for NetBSD as */
2401/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002402/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403/**********************************************************************/
2404
2405/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002406static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407 int regnum, unsigned char val)
2408{
2409 unsigned long regofs = 0;
2410
2411 if (cinfo->btype == BT_PICASSO) {
2412 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002413/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2414 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2416 regofs = 0xfff;
2417 }
2418
Krzysztof Helt8503df62007-10-16 01:29:08 -07002419 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420}
2421
2422/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002423static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424{
2425 unsigned long regofs = 0;
2426
2427 if (cinfo->btype == BT_PICASSO) {
2428 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002429/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2430 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2432 regofs = 0xfff;
2433 }
2434
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436}
2437
2438/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002439static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002441 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442
Krzysztof Helt8503df62007-10-16 01:29:08 -07002443 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444 /* if we're just in "write value" mode, write back the */
2445 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446 vga_w(cinfo->regbase, VGA_ATT_IW,
2447 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 }
2449 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2451 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452
2453 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455}
2456
2457/*** WHDR() - write into the Hidden DAC register ***/
2458/* as the HDR is the only extension register that requires special treatment
2459 * (the other extension registers are accessible just like the "ordinary"
2460 * registers of their functional group) here is a specialized routine for
2461 * accessing the HDR
2462 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464{
2465 unsigned char dummy;
2466
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002467 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002468 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 if (cinfo->btype == BT_PICASSO) {
2470 /* Klaus' hint for correct access to HDR on some boards */
2471 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002472 WGen(cinfo, VGA_PEL_MSK, 0x00);
2473 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475 dummy = RGen(cinfo, VGA_PEL_IW);
2476 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 }
2478 /* now do the usual stuff to access the HDR */
2479
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480 dummy = RGen(cinfo, VGA_PEL_MSK);
2481 udelay(200);
2482 dummy = RGen(cinfo, VGA_PEL_MSK);
2483 udelay(200);
2484 dummy = RGen(cinfo, VGA_PEL_MSK);
2485 udelay(200);
2486 dummy = RGen(cinfo, VGA_PEL_MSK);
2487 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489 WGen(cinfo, VGA_PEL_MSK, val);
2490 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491
2492 if (cinfo->btype == BT_PICASSO) {
2493 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 dummy = RGen(cinfo, VGA_PEL_IW);
2495 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496
2497 /* and at the end, restore the mask value */
2498 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002499 WGen(cinfo, VGA_PEL_MSK, 0xff);
2500 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 }
2502}
2503
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506{
2507#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511#endif
2512}
2513
2514/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516{
2517#ifdef CONFIG_ZORRO
2518 /* writing an arbitrary value to this one causes the monitor switcher */
2519 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523#endif
2524}
2525
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 unsigned char green, unsigned char blue)
2529{
2530 unsigned int data = VGA_PEL_D;
2531
2532 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002533 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534
2535 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002536 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002537 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 /* but DAC data register IS, at least for Picasso II */
2539 if (cinfo->btype == BT_PICASSO)
2540 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 vga_w(cinfo->regbase, data, red);
2542 vga_w(cinfo->regbase, data, green);
2543 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002545 vga_w(cinfo->regbase, data, blue);
2546 vga_w(cinfo->regbase, data, green);
2547 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 }
2549}
2550
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551#if 0
2552/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 unsigned char *green, unsigned char *blue)
2555{
2556 unsigned int data = VGA_PEL_D;
2557
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559
2560 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2561 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2562 if (cinfo->btype == BT_PICASSO)
2563 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564 *red = vga_r(cinfo->regbase, data);
2565 *green = vga_r(cinfo->regbase, data);
2566 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568 *blue = vga_r(cinfo->regbase, data);
2569 *green = vga_r(cinfo->regbase, data);
2570 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 }
2572}
2573#endif
2574
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575/*******************************************************************
2576 cirrusfb_WaitBLT()
2577
2578 Wait for the BitBLT engine to complete a possible earlier job
2579*********************************************************************/
2580
2581/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002584 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002585 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002586}
2587
2588/*******************************************************************
2589 cirrusfb_BitBLT()
2590
2591 perform accelerated "scrolling"
2592********************************************************************/
2593
Krzysztof Helt8343c892009-03-31 15:25:11 -07002594static void cirrusfb_set_blitter(u8 __iomem *regbase,
2595 u_short nwidth, u_short nheight,
2596 u_long nsrc, u_long ndest,
2597 u_short bltmode, u_short line_length)
2598
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601 /* dest pitch low */
2602 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2603 /* dest pitch hi */
2604 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2605 /* source pitch low */
2606 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2607 /* source pitch hi */
2608 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609
2610 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002611 /* BLT width low */
2612 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2613 /* BLT width hi */
2614 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615
2616 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 /* BLT height low */
2618 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2619 /* BLT width hi */
2620 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621
2622 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 /* BLT dest low */
2624 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2625 /* BLT dest mid */
2626 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2627 /* BLT dest hi */
2628 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
2630 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002631 /* BLT src low */
2632 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2633 /* BLT src mid */
2634 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2635 /* BLT src hi */
2636 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640
2641 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643
2644 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002645 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002646}
2647
2648/*******************************************************************
2649 cirrusfb_BitBLT()
2650
2651 perform accelerated "scrolling"
2652********************************************************************/
2653
2654static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2655 u_short curx, u_short cury,
2656 u_short destx, u_short desty,
2657 u_short width, u_short height,
2658 u_short line_length)
2659{
2660 u_short nwidth = width - 1;
2661 u_short nheight = height - 1;
2662 u_long nsrc, ndest;
2663 u_char bltmode;
2664
2665 bltmode = 0x00;
2666 /* if source adr < dest addr, do the Blt backwards */
2667 if (cury <= desty) {
2668 if (cury == desty) {
2669 /* if src and dest are on the same line, check x */
2670 if (curx < destx)
2671 bltmode |= 0x01;
2672 } else
2673 bltmode |= 0x01;
2674 }
2675 /* standard case: forward blitting */
2676 nsrc = (cury * line_length) + curx;
2677 ndest = (desty * line_length) + destx;
2678 if (bltmode) {
2679 /* this means start addresses are at the end,
2680 * counting backwards
2681 */
2682 nsrc += nheight * line_length + nwidth;
2683 ndest += nheight * line_length + nwidth;
2684 }
2685
2686 cirrusfb_WaitBLT(regbase);
2687
2688 cirrusfb_set_blitter(regbase, nwidth, nheight,
2689 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690}
2691
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692/*******************************************************************
2693 cirrusfb_RectFill()
2694
2695 perform accelerated rectangle fill
2696********************************************************************/
2697
Krzysztof Helt8503df62007-10-16 01:29:08 -07002698static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002700 u32 fg_color, u32 bg_color, u_short line_length,
2701 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002703 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704 u_char op;
2705
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 /* This is a ColorExpand Blt, using the */
2709 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002710 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2711 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712
Krzysztof Helt9e848062009-03-31 15:25:11 -07002713 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002714 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002715 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2716 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2717 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002718 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002719 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002720 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2721 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002722 op = 0xa0;
2723 }
2724 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002725 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2726 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2727 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002729 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002730 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731}
2732
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733/**************************************************************************
2734 * bestclock() - determine closest possible clock lower(?) than the
2735 * desired pixel clock
2736 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002737static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002738{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002739 int n, d;
2740 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741
Krzysztof Helt8503df62007-10-16 01:29:08 -07002742 assert(nom != NULL);
2743 assert(den != NULL);
2744 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745
2746 *nom = 0;
2747 *den = 0;
2748 *div = 0;
2749
Linus Torvalds1da177e2005-04-16 15:20:36 -07002750 if (freq < 8000)
2751 freq = 8000;
2752
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002753 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754
2755 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002756 int s = 0;
2757
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002758 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002760 int temp = d;
2761
2762 if (temp > 31) {
2763 s = 1;
2764 temp >>= 1;
2765 }
2766 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002767 h = h > freq ? h - freq : freq - h;
2768 if (h < diff) {
2769 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002771 *den = temp;
2772 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 }
2774 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002775 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002777 if (d > 31) {
2778 s = 1;
2779 d >>= 1;
2780 }
2781 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002782 h = h > freq ? h - freq : freq - h;
2783 if (h < diff) {
2784 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002786 *den = d;
2787 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 }
2789 }
2790 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791}
2792
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793/* -------------------------------------------------------------------------
2794 *
2795 * debugging functions
2796 *
2797 * -------------------------------------------------------------------------
2798 */
2799
2800#ifdef CIRRUSFB_DEBUG
2801
2802/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 * cirrusfb_dbg_print_regs
2804 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2805 * @reg_class: type of registers to read: %CRT, or %SEQ
2806 *
2807 * DESCRIPTION:
2808 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2809 * old-style I/O ports are queried for information, otherwise MMIO is
2810 * used at the given @base address to query the information.
2811 */
2812
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002813static void cirrusfb_dbg_print_regs(struct fb_info *info,
2814 caddr_t regbase,
2815 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816{
2817 va_list list;
2818 unsigned char val = 0;
2819 unsigned reg;
2820 char *name;
2821
Krzysztof Helt8503df62007-10-16 01:29:08 -07002822 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823
Krzysztof Helt8503df62007-10-16 01:29:08 -07002824 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002826 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827
2828 switch (reg_class) {
2829 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002830 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 break;
2832 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002833 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 break;
2835 default:
2836 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002837 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 break;
2839 }
2840
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002841 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 }
2845
Krzysztof Helt8503df62007-10-16 01:29:08 -07002846 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847}
2848
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850 * cirrusfb_dbg_reg_dump
2851 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2852 *
2853 * DESCRIPTION:
2854 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2855 * old-style I/O ports are queried for information, otherwise MMIO is
2856 * used at the given @base address to query the information.
2857 */
2858
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002859static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002861 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002863 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 "CR00", 0x00,
2865 "CR01", 0x01,
2866 "CR02", 0x02,
2867 "CR03", 0x03,
2868 "CR04", 0x04,
2869 "CR05", 0x05,
2870 "CR06", 0x06,
2871 "CR07", 0x07,
2872 "CR08", 0x08,
2873 "CR09", 0x09,
2874 "CR0A", 0x0A,
2875 "CR0B", 0x0B,
2876 "CR0C", 0x0C,
2877 "CR0D", 0x0D,
2878 "CR0E", 0x0E,
2879 "CR0F", 0x0F,
2880 "CR10", 0x10,
2881 "CR11", 0x11,
2882 "CR12", 0x12,
2883 "CR13", 0x13,
2884 "CR14", 0x14,
2885 "CR15", 0x15,
2886 "CR16", 0x16,
2887 "CR17", 0x17,
2888 "CR18", 0x18,
2889 "CR22", 0x22,
2890 "CR24", 0x24,
2891 "CR26", 0x26,
2892 "CR2D", 0x2D,
2893 "CR2E", 0x2E,
2894 "CR2F", 0x2F,
2895 "CR30", 0x30,
2896 "CR31", 0x31,
2897 "CR32", 0x32,
2898 "CR33", 0x33,
2899 "CR34", 0x34,
2900 "CR35", 0x35,
2901 "CR36", 0x36,
2902 "CR37", 0x37,
2903 "CR38", 0x38,
2904 "CR39", 0x39,
2905 "CR3A", 0x3A,
2906 "CR3B", 0x3B,
2907 "CR3C", 0x3C,
2908 "CR3D", 0x3D,
2909 "CR3E", 0x3E,
2910 "CR3F", 0x3F,
2911 NULL);
2912
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002913 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002915 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002917 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918 "SR00", 0x00,
2919 "SR01", 0x01,
2920 "SR02", 0x02,
2921 "SR03", 0x03,
2922 "SR04", 0x04,
2923 "SR08", 0x08,
2924 "SR09", 0x09,
2925 "SR0A", 0x0A,
2926 "SR0B", 0x0B,
2927 "SR0D", 0x0D,
2928 "SR10", 0x10,
2929 "SR11", 0x11,
2930 "SR12", 0x12,
2931 "SR13", 0x13,
2932 "SR14", 0x14,
2933 "SR15", 0x15,
2934 "SR16", 0x16,
2935 "SR17", 0x17,
2936 "SR18", 0x18,
2937 "SR19", 0x19,
2938 "SR1A", 0x1A,
2939 "SR1B", 0x1B,
2940 "SR1C", 0x1C,
2941 "SR1D", 0x1D,
2942 "SR1E", 0x1E,
2943 "SR1F", 0x1F,
2944 NULL);
2945
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002946 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947}
2948
2949#endif /* CIRRUSFB_DEBUG */
2950