blob: 97db3ba8f2377d8cd66c677e68703d03255f76fe [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#include <asm/pgtable.h>
46
47#ifdef CONFIG_ZORRO
48#include <linux/zorro.h>
49#endif
50#ifdef CONFIG_PCI
51#include <linux/pci.h>
52#endif
53#ifdef CONFIG_AMIGA
54#include <asm/amigahw.h>
55#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070057#include <video/vga.h>
58#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Linus Torvalds1da177e2005-04-16 15:20:36 -070060/*****************************************************************
61 *
62 * debugging and utility macros
63 *
64 */
65
Linus Torvalds1da177e2005-04-16 15:20:36 -070066/* disable runtime assertions? */
67/* #define CIRRUSFB_NDEBUG */
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/* debugging assertions */
70#ifndef CIRRUSFB_NDEBUG
71#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070072 if (!(expr)) { \
73 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070074 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070075 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070076#else
77#define assert(expr)
78#endif
79
Krzysztof Helt8503df62007-10-16 01:29:08 -070080#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
Linus Torvalds1da177e2005-04-16 15:20:36 -070082/*****************************************************************
83 *
84 * chipset information
85 *
86 */
87
88/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070089enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070091 BT_SD64, /* GD5434 */
92 BT_PICCOLO, /* GD5426 */
93 BT_PICASSO, /* GD5426 or GD5428 */
94 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 BT_PICASSO4, /* GD5446 */
96 BT_ALPINE, /* GD543x/4x */
97 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -070098 BT_LAGUNA, /* GD5462/64 */
99 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700100};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*
103 * per-board-type information, used for enumerating and abstracting
104 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700105 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 * use direct indexing on this array
107 * NOTE: '__initdata' cannot be used as some of this info
108 * is required at runtime. Maybe separate into an init-only and
109 * a run-time table?
110 */
111static const struct cirrusfb_board_info_rec {
112 char *name; /* ASCII name of chipset */
113 long maxclock[5]; /* maximum video clock */
114 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700115 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
116 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700117 /* construct bit 19 of screen start address */
118 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119
120 /* initial SR07 value, then for each mode */
121 unsigned char sr07;
122 unsigned char sr07_1bpp;
123 unsigned char sr07_1bpp_mux;
124 unsigned char sr07_8bpp;
125 unsigned char sr07_8bpp_mux;
126
127 unsigned char sr1f; /* SR1F VGA initial register value */
128} cirrusfb_board_info[] = {
129 [BT_SD64] = {
130 .name = "CL SD64",
131 .maxclock = {
132 /* guess */
133 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700134 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700136 .init_sr07 = true,
137 .init_sr1f = true,
138 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 .sr07 = 0xF0,
140 .sr07_1bpp = 0xF0,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700141 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700143 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700144 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 },
146 [BT_PICCOLO] = {
147 .name = "CL Piccolo",
148 .maxclock = {
149 /* guess */
150 90000, 90000, 90000, 90000, 90000
151 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700152 .init_sr07 = true,
153 .init_sr1f = true,
154 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 .sr07 = 0x80,
156 .sr07_1bpp = 0x80,
157 .sr07_8bpp = 0x81,
158 .sr1f = 0x22
159 },
160 [BT_PICASSO] = {
161 .name = "CL Picasso",
162 .maxclock = {
163 /* guess */
164 90000, 90000, 90000, 90000, 90000
165 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700166 .init_sr07 = true,
167 .init_sr1f = true,
168 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 .sr07 = 0x20,
170 .sr07_1bpp = 0x20,
171 .sr07_8bpp = 0x21,
172 .sr1f = 0x22
173 },
174 [BT_SPECTRUM] = {
175 .name = "CL Spectrum",
176 .maxclock = {
177 /* guess */
178 90000, 90000, 90000, 90000, 90000
179 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700180 .init_sr07 = true,
181 .init_sr1f = true,
182 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 .sr07 = 0x80,
184 .sr07_1bpp = 0x80,
185 .sr07_8bpp = 0x81,
186 .sr1f = 0x22
187 },
188 [BT_PICASSO4] = {
189 .name = "CL Picasso4",
190 .maxclock = {
191 135100, 135100, 85500, 85500, 0
192 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700193 .init_sr07 = true,
194 .init_sr1f = false,
195 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700196 .sr07 = 0xA0,
197 .sr07_1bpp = 0xA0,
198 .sr07_1bpp_mux = 0xA6,
199 .sr07_8bpp = 0xA1,
200 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 .sr1f = 0
202 },
203 [BT_ALPINE] = {
204 .name = "CL Alpine",
205 .maxclock = {
206 /* for the GD5430. GD5446 can do more... */
207 85500, 85500, 50000, 28500, 0
208 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700209 .init_sr07 = true,
210 .init_sr1f = true,
211 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700213 .sr07_1bpp = 0xA0,
214 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .sr07_8bpp = 0xA1,
216 .sr07_8bpp_mux = 0xA7,
217 .sr1f = 0x1C
218 },
219 [BT_GD5480] = {
220 .name = "CL GD5480",
221 .maxclock = {
222 135100, 200000, 200000, 135100, 135100
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0x10,
228 .sr07_1bpp = 0x11,
229 .sr07_8bpp = 0x11,
230 .sr1f = 0x1C
231 },
232 [BT_LAGUNA] = {
233 .name = "CL Laguna",
234 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700235 /* taken from X11 code */
236 170000, 170000, 170000, 170000, 135100,
237 },
238 .init_sr07 = false,
239 .init_sr1f = false,
240 .scrn_start_bit19 = true,
241 },
242 [BT_LAGUNAB] = {
243 .name = "CL Laguna AGP",
244 .maxclock = {
245 /* taken from X11 code */
246 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700248 .init_sr07 = false,
249 .init_sr1f = false,
250 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 }
252};
253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254#ifdef CONFIG_PCI
255#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000256 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
258static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700259 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700260 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
261 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700262 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
263 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
264 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
266 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
267 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
268 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700269 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 { 0, }
271};
272MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
273#undef CHIP
274#endif /* CONFIG_PCI */
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276#ifdef CONFIG_ZORRO
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200277struct zorrocl {
278 enum cirrus_board type; /* Board type */
279 u32 regoffset; /* Offset of registers in first Zorro device */
280 u32 ramsize; /* Size of video RAM in first Zorro device */
281 /* If zero, use autoprobe on RAM device */
282 u32 ramoffset; /* Offset of video RAM in first Zorro device */
283 zorro_id ramid; /* Zorro ID of RAM device */
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200284 zorro_id ramid2; /* Zorro ID of optional second RAM device */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200285};
286
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800287static const struct zorrocl zcl_sd64 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200288 .type = BT_SD64,
289 .ramid = ZORRO_PROD_HELFRICH_SD64_RAM,
290};
291
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800292static const struct zorrocl zcl_piccolo = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200293 .type = BT_PICCOLO,
294 .ramid = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
295};
296
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800297static const struct zorrocl zcl_picasso = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200298 .type = BT_PICASSO,
299 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
300};
301
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800302static const struct zorrocl zcl_spectrum = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200303 .type = BT_SPECTRUM,
304 .ramid = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
305};
306
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800307static const struct zorrocl zcl_picasso4_z3 = {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200308 .type = BT_PICASSO4,
309 .regoffset = 0x00600000,
310 .ramsize = 4 * MB_,
Geert Uytterhoevene78bb882011-10-20 13:42:25 +0200311 .ramoffset = 0x01000000, /* 0x02000000 for 64 MiB boards */
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200312};
313
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800314static const struct zorrocl zcl_picasso4_z2 = {
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200315 .type = BT_PICASSO4,
316 .regoffset = 0x10000,
317 .ramid = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM1,
318 .ramid2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_RAM2,
319};
320
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200321
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800322static const struct zorro_device_id cirrusfb_zorro_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200324 .id = ZORRO_PROD_HELFRICH_SD64_REG,
325 .driver_data = (unsigned long)&zcl_sd64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200327 .id = ZORRO_PROD_HELFRICH_PICCOLO_REG,
328 .driver_data = (unsigned long)&zcl_piccolo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200330 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
331 .driver_data = (unsigned long)&zcl_picasso,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }, {
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200333 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
334 .driver_data = (unsigned long)&zcl_spectrum,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 }, {
336 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +0200337 .driver_data = (unsigned long)&zcl_picasso4_z3,
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +0200338 }, {
339 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z2_REG,
340 .driver_data = (unsigned long)&zcl_picasso4_z2,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 },
342 { 0 }
343};
Geert Uytterhoevenbf54a2b2008-11-18 21:13:53 +0100344MODULE_DEVICE_TABLE(zorro, cirrusfb_zorro_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345#endif /* CONFIG_ZORRO */
346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700348enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700349 CRT,
350 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700351};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700352#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354/* info about board */
355struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700357 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700358 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 unsigned char SFR; /* Shadow of special function register */
360
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700361 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700362 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700364 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700366 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367};
368
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -0800369static bool noaccel;
370static char *mode_option = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372/****************************************************************************/
373/**** BEGIN PROTOTYPES ******************************************************/
374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700376static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
377 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700380static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700381static void switch_monitor(struct cirrusfb_info *cinfo, int on);
382static void WGen(const struct cirrusfb_info *cinfo,
383 int regnum, unsigned char val);
384static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
385static void AttrOn(const struct cirrusfb_info *cinfo);
386static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
387static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
388static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
389static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
390 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700392static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
393 unsigned char *red, unsigned char *green,
394 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700396static void cirrusfb_WaitBLT(u8 __iomem *regbase);
397static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
398 u_short curx, u_short cury,
399 u_short destx, u_short desty,
400 u_short width, u_short height,
401 u_short line_length);
402static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
403 u_short x, u_short y,
404 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700405 u32 fg_color, u32 bg_color,
406 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700408static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409
410#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700411static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
412static void cirrusfb_dbg_print_regs(struct fb_info *info,
413 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700414 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415#endif /* CIRRUSFB_DEBUG */
416
417/*** END PROTOTYPES ********************************************************/
418/*****************************************************************************/
419/*** BEGIN Interface Used by the World ***************************************/
420
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700421static inline int is_laguna(const struct cirrusfb_info *cinfo)
422{
423 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
424}
425
Krzysztof Helt8503df62007-10-16 01:29:08 -0700426static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700429static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430{
431 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700432 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 return 0;
434}
435
436/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700437static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438{
439 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700440 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 return 0;
442}
443
444/**** END Interface used by the World *************************************/
445/****************************************************************************/
446/**** BEGIN Hardware specific Routines **************************************/
447
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700449static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700451 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700452 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 /* Read MCLK value */
455 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700456 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
458 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700459 * should divide it by to get VCLK
460 */
461
462 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700463 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700464 return 1;
465 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700466 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700467 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 }
469
Krzysztof Helt486ff382008-10-15 22:03:42 -0700470 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471}
472
Krzysztof Helt99a45842009-03-31 15:25:09 -0700473static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
474 struct fb_info *info)
475{
476 long freq;
477 long maxclock;
478 struct cirrusfb_info *cinfo = info->par;
479 unsigned maxclockidx = var->bits_per_pixel >> 3;
480
481 /* convert from ps to kHz */
482 freq = PICOS2KHZ(var->pixclock);
483
484 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
485
486 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
487 cinfo->multiplexing = 0;
488
489 /* If the frequency is greater than we can support, we might be able
490 * to use multiplexing for the video mode */
491 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700492 dev_err(info->device,
493 "Frequency greater than maxclock (%ld kHz)\n",
494 maxclock);
495 return -EINVAL;
496 }
497 /*
498 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
499 * pixel clock
500 */
501 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700502 switch (cinfo->btype) {
503 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700504 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700505 case BT_PICASSO4:
506 if (freq > 85500)
507 cinfo->multiplexing = 1;
508 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700509 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700510 if (freq > 135100)
511 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700512 break;
513
514 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700515 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700516 }
517 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700518
519 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700520 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700521 cinfo->doubleVCLK = 0;
522 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
523 var->bits_per_pixel == 16) {
524 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700525 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700526
Krzysztof Helt99a45842009-03-31 15:25:09 -0700527 return 0;
528}
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530static int cirrusfb_check_var(struct fb_var_screeninfo *var,
531 struct fb_info *info)
532{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700533 int yres;
534 /* memory size in pixels */
535 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700536 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700539 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 var->red.offset = 0;
541 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700542 var->green = var->red;
543 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 break;
545
546 case 8:
547 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700548 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700549 var->green = var->red;
550 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 break;
552
553 case 16:
Paul Bolle933ee712013-03-27 00:47:03 +0000554 var->red.offset = 11;
555 var->green.offset = 5;
556 var->blue.offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700558 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 var->blue.length = 5;
560 break;
561
Krzysztof Helt7cade312009-03-31 15:25:13 -0700562 case 24:
Paul Bolle933ee712013-03-27 00:47:03 +0000563 var->red.offset = 16;
564 var->green.offset = 8;
565 var->blue.offset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 var->red.length = 8;
567 var->green.length = 8;
568 var->blue.length = 8;
569 break;
570
571 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700572 dev_dbg(info->device,
573 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Krzysztof Helt0efb2a02009-04-13 14:39:55 -0700574 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700577 if (var->xres_virtual < var->xres)
578 var->xres_virtual = var->xres;
579 /* use highest possible virtual resolution */
580 if (var->yres_virtual == -1) {
581 var->yres_virtual = pixels / var->xres_virtual;
582
583 dev_info(info->device,
584 "virtual resolution set to maximum of %dx%d\n",
585 var->xres_virtual, var->yres_virtual);
586 }
587 if (var->yres_virtual < var->yres)
588 var->yres_virtual = var->yres;
589
590 if (var->xres_virtual * var->yres_virtual > pixels) {
591 dev_err(info->device, "mode %dx%dx%d rejected... "
592 "virtual resolution too high to fit into video memory!\n",
593 var->xres_virtual, var->yres_virtual,
594 var->bits_per_pixel);
595 return -EINVAL;
596 }
597
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700598 if (var->xoffset < 0)
599 var->xoffset = 0;
600 if (var->yoffset < 0)
601 var->yoffset = 0;
602
603 /* truncate xoffset and yoffset to maximum if too high */
604 if (var->xoffset > var->xres_virtual - var->xres)
605 var->xoffset = var->xres_virtual - var->xres - 1;
606 if (var->yoffset > var->yres_virtual - var->yres)
607 var->yoffset = var->yres_virtual - var->yres - 1;
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 var->red.msb_right =
610 var->green.msb_right =
611 var->blue.msb_right =
612 var->transp.offset =
613 var->transp.length =
614 var->transp.msb_right = 0;
615
616 yres = var->yres;
617 if (var->vmode & FB_VMODE_DOUBLE)
618 yres *= 2;
619 else if (var->vmode & FB_VMODE_INTERLACED)
620 yres = (yres + 1) / 2;
621
622 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700623 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700624 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 return -EINVAL;
626 }
627
Krzysztof Helt99a45842009-03-31 15:25:09 -0700628 if (cirrusfb_check_pixclock(var, info))
629 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700631 if (!is_laguna(cinfo))
632 var->accel_flags = FB_ACCELF_TEXT;
633
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 return 0;
635}
636
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700640 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700641
Krzysztof Helt8503df62007-10-16 01:29:08 -0700642 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Krzysztof Helt486ff382008-10-15 22:03:42 -0700645 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700646 dev_dbg(info->device, "Set %s as pixclock source.\n",
647 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700648 old1f |= 0x40;
649 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
650 if (div == 2)
651 old1e |= 1;
652
653 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700655 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
658/*************************************************************************
659 cirrusfb_set_par_foo()
660
661 actually writes the values for a new video mode into the hardware,
662**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700663static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 struct cirrusfb_info *cinfo = info->par;
666 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 u8 __iomem *regbase = cinfo->regbase;
668 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700669 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700671 int hdispend, hsyncstart, hsyncend, htotal;
672 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700673 long freq;
674 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700675 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700677 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700679
680 switch (var->bits_per_pixel) {
681 case 1:
682 info->fix.line_length = var->xres_virtual / 8;
683 info->fix.visual = FB_VISUAL_MONO10;
684 break;
685
686 case 8:
687 info->fix.line_length = var->xres_virtual;
688 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
689 break;
690
691 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700692 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700693 info->fix.line_length = var->xres_virtual *
694 var->bits_per_pixel >> 3;
695 info->fix.visual = FB_VISUAL_TRUECOLOR;
696 break;
697 }
698 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700700 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 bi = &cirrusfb_board_info[cinfo->btype];
703
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700704 hsyncstart = var->xres + var->right_margin;
705 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700706 htotal = (hsyncend + var->left_margin) / 8;
707 hdispend = var->xres / 8;
708 hsyncstart = hsyncstart / 8;
709 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700710
Krzysztof Helt8636a922009-03-31 15:25:17 -0700711 vdispend = var->yres;
712 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700713 vsyncend = vsyncstart + var->vsync_len;
714 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700715
716 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700717 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 vsyncstart *= 2;
719 vsyncend *= 2;
720 vtotal *= 2;
721 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700722 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700723 vsyncstart = (vsyncstart + 1) / 2;
724 vsyncend = (vsyncend + 1) / 2;
725 vtotal = (vtotal + 1) / 2;
726 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700727 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700728 if (yres >= 1024) {
729 vtotal /= 2;
730 vsyncstart /= 2;
731 vsyncend /= 2;
732 vdispend /= 2;
733 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700734
735 vdispend -= 1;
736 vsyncstart -= 1;
737 vsyncend -= 1;
738 vtotal -= 2;
739
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700740 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700741 htotal /= 2;
742 hsyncstart /= 2;
743 hsyncend /= 2;
744 hdispend /= 2;
745 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700746
747 htotal -= 5;
748 hdispend -= 1;
749 hsyncstart += 1;
750 hsyncend += 1;
751
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700753 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
755 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700756 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700759 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700762 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Krzysztof Helt8503df62007-10-16 01:29:08 -0700765 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700767 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700768 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700770 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700771 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700773 tmp = hsyncend % 32;
774 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700776 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700777 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
782 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700785 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700791 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700793 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700797 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700798 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700801 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 tmp |= 0x20;
803 if (var->vmode & FB_VMODE_DOUBLE)
804 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700805 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700806 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700808 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700811 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700812 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700815 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700817 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700818 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700820 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700821 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700823 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700824 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
826 tmp = 0;
827 if (var->vmode & FB_VMODE_INTERLACED)
828 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700829 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700833 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700835 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 tmp |= 128;
837
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700838 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700839 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700841 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700842 if (var->bits_per_pixel == 24)
843 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
844 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700845 if (cinfo->multiplexing)
846 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700847 if (cinfo->doubleVCLK)
848 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700849
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700850 bestclock(freq, &nom, &den, &div);
851
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700852 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
853 freq, nom, den, div);
854
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 /* set VCLK0 */
856 /* hardware RefClock: 14.31818 MHz */
857 /* formula: VClk = (OSC * N) / (D * (1+P)) */
858 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
859
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700860 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
861 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700862 /* if freq is close to mclk or mclk/2 select mclk
863 * as clock source
864 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700865 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700866 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700867 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700868 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700869 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700870 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700871 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
872 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
873 unsigned short tile_control;
874
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700875 if (cinfo->btype == BT_LAGUNAB) {
876 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
877 tile_control &= ~0x80;
878 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
879 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700880
881 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
882 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
883 control = fb_readw(cinfo->laguna_mmio + 0x402);
884 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
885 control &= ~0x6800;
886 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700887 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700888 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700889 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700890 tmp = den << 1;
891 if (div != 0)
892 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700893 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
894 if ((cinfo->btype == BT_SD64) ||
895 (cinfo->btype == BT_ALPINE) ||
896 (cinfo->btype == BT_GD5480))
897 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700899 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700900 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700901 vga_wseq(regbase, CL_SEQRE, tmp);
902 vga_wseq(regbase, CL_SEQR1E, nom);
903 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700904 vga_wseq(regbase, CL_SEQRE, nom);
905 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700906 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700907 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700909 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 else
913 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
914 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700915 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 /* don't know if it would hurt to also program this if no interlaced */
918 /* mode is used, but I feel better this way.. :-) */
919 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700920 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700924 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700926 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
928 tmp |= 0x40;
929 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
930 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Krzysztof Helt8503df62007-10-16 01:29:08 -0700933 /* text cursor on and start line */
934 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
935 /* text cursor end line */
936 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 /******************************************************
939 *
940 * 1 bpp
941 *
942 */
943
944 /* programming for different color depths */
945 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700946 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700947 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 /* SR07 */
950 switch (cinfo->btype) {
951 case BT_SD64:
952 case BT_PICCOLO:
953 case BT_PICASSO:
954 case BT_SPECTRUM:
955 case BT_PICASSO4:
956 case BT_ALPINE:
957 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700958 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700959 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 bi->sr07_1bpp_mux : bi->sr07_1bpp);
961 break;
962
963 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700964 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 vga_wseq(regbase, CL_SEQR7,
966 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 break;
968
969 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700970 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 break;
972 }
973
974 /* Extended Sequencer Mode */
975 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700978 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
980 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 break;
982
983 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 /* ## vorher d0 avoid FIFO underruns..? */
985 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 break;
987
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700988 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 case BT_PICASSO4:
990 case BT_ALPINE:
991 case BT_GD5480:
992 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700993 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 /* do nothing */
995 break;
996
997 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700998 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 break;
1000 }
1001
Krzysztof Helt8503df62007-10-16 01:29:08 -07001002 /* pixel mask: pass-through for first plane */
1003 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001004 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001005 /* hidden dac reg: 1280x1024 */
1006 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001008 /* hidden dac: nothing */
1009 WHDR(cinfo, 0);
1010 /* memory mode: odd/even, ext. memory */
1011 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1012 /* plane mask: only write to first plane */
1013 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 }
1015
1016 /******************************************************
1017 *
1018 * 8 bpp
1019 *
1020 */
1021
1022 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001023 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 switch (cinfo->btype) {
1025 case BT_SD64:
1026 case BT_PICCOLO:
1027 case BT_PICASSO:
1028 case BT_SPECTRUM:
1029 case BT_PICASSO4:
1030 case BT_ALPINE:
1031 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001033 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1035 break;
1036
1037 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001038 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 vga_wseq(regbase, CL_SEQR7,
1040 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001041 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 break;
1043
1044 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001045 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 break;
1047 }
1048
1049 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001053 /* Fast Page-Mode writes */
1054 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 break;
1056
1057 case BT_PICASSO4:
1058#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001059 /* ### INCOMPLETE!! */
1060 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001063 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 case BT_GD5480:
1065 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001066 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 /* do nothing */
1068 break;
1069
1070 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001071 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 break;
1073 }
1074
Krzysztof Helt8503df62007-10-16 01:29:08 -07001075 /* mode register: 256 color mode */
1076 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001077 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001078 /* hidden dac reg: 1280x1024 */
1079 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001081 /* hidden dac: nothing */
1082 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 }
1084
1085 /******************************************************
1086 *
1087 * 16 bpp
1088 *
1089 */
1090
1091 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001092 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001095 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001096 vga_wseq(regbase, CL_SEQR7, 0x87);
1097 /* Fast Page-Mode writes */
1098 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 break;
1100
1101 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001102 vga_wseq(regbase, CL_SEQR7, 0x27);
1103 /* Fast Page-Mode writes */
1104 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001107 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001110 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001111 vga_wseq(regbase, CL_SEQR7,
1112 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 break;
1114
1115 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 /* We already set SRF and SR1F */
1118 break;
1119
1120 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001121 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001122 vga_wseq(regbase, CL_SEQR7,
1123 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001124 control |= 0x2000;
1125 format |= 0x1400;
1126 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 break;
1128
1129 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001130 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132 }
1133
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 /* mode register: 256 color mode */
1135 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001137 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138#elif defined(CONFIG_ZORRO)
1139 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001140 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 }
1143
1144 /******************************************************
1145 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001146 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 *
1148 */
1149
Krzysztof Helt7cade312009-03-31 15:25:13 -07001150 else if (var->bits_per_pixel == 24) {
1151 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001154 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 vga_wseq(regbase, CL_SEQR7, 0x85);
1156 /* Fast Page-Mode writes */
1157 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 break;
1159
1160 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 vga_wseq(regbase, CL_SEQR7, 0x25);
1162 /* Fast Page-Mode writes */
1163 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001166 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001169 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001170 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 break;
1172
1173 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001174 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 /* We already set SRF and SR1F */
1176 break;
1177
1178 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001179 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 vga_wseq(regbase, CL_SEQR7,
1181 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001182 control |= 0x4000;
1183 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001184 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186
1187 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001188 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 break;
1190 }
1191
Krzysztof Helt8503df62007-10-16 01:29:08 -07001192 /* mode register: 256 color mode */
1193 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001194 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1195 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197
1198 /******************************************************
1199 *
1200 * unknown/unsupported bpp
1201 *
1202 */
1203
Krzysztof Helt8503df62007-10-16 01:29:08 -07001204 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001205 dev_err(info->device,
1206 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208
Krzysztof Helt6683e012009-03-31 15:25:06 -07001209 pitch = info->fix.line_length >> 3;
1210 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001212 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213 tmp |= 0x10; /* offset overflow bit */
1214
Krzysztof Helt8503df62007-10-16 01:29:08 -07001215 /* screen start addr #16-18, fastpagemode cycles */
1216 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001218 /* screen start address bit 19 */
1219 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001220 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001222 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001223 tmp = 0;
1224 if ((htotal + 5) & 256)
1225 tmp |= 128;
1226 if (hdispend & 256)
1227 tmp |= 64;
1228 if (hsyncstart & 256)
1229 tmp |= 48;
1230 if (vtotal & 1024)
1231 tmp |= 8;
1232 if (vdispend & 1024)
1233 tmp |= 4;
1234 if (vsyncstart & 1024)
1235 tmp |= 3;
1236
1237 vga_wcrt(regbase, CL_CRT1E, tmp);
1238 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1239 }
1240
Krzysztof Helt8503df62007-10-16 01:29:08 -07001241 /* pixel panning */
1242 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
1244 /* [ EGS: SetOffset(); ] */
1245 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001246 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001248 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001249 /* no tiles */
1250 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1251 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1252 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1253 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 /* finally, turn on everything - turn off "FullBandwidth" bit */
1255 /* also, set "DotClock%2" bit where requested */
1256 tmp = 0x01;
1257
1258/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1259 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1260 tmp |= 0x08;
1261*/
1262
Krzysztof Helt8503df62007-10-16 01:29:08 -07001263 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001264 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001267 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268#endif
1269
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 return 0;
1271}
1272
1273/* for some reason incomprehensible to me, cirrusfb requires that you write
1274 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277 cirrusfb_set_par_foo(info);
1278 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279}
1280
Krzysztof Helt8503df62007-10-16 01:29:08 -07001281static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1282 unsigned blue, unsigned transp,
1283 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284{
1285 struct cirrusfb_info *cinfo = info->par;
1286
1287 if (regno > 255)
1288 return -EINVAL;
1289
1290 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1291 u32 v;
1292 red >>= (16 - info->var.red.length);
1293 green >>= (16 - info->var.green.length);
1294 blue >>= (16 - info->var.blue.length);
1295
Krzysztof Helt8503df62007-10-16 01:29:08 -07001296 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 return 1;
1298 v = (red << info->var.red.offset) |
1299 (green << info->var.green.offset) |
1300 (blue << info->var.blue.offset);
1301
Krzysztof Helt060b6002007-10-16 01:29:13 -07001302 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 return 0;
1304 }
1305
Krzysztof Helt8503df62007-10-16 01:29:08 -07001306 if (info->var.bits_per_pixel == 8)
1307 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308
1309 return 0;
1310
1311}
1312
1313/*************************************************************************
1314 cirrusfb_pan_display()
1315
1316 performs display panning - provided hardware permits this
1317**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001318static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1319 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001321 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001323 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 struct cirrusfb_info *cinfo = info->par;
1325
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 /* no range checks for xoffset and yoffset, */
1327 /* as fb_pan_display has already done this */
1328 if (var->vmode & FB_VMODE_YWRAP)
1329 return -EINVAL;
1330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
Krzysztof Helt99a45842009-03-31 15:25:09 -07001333 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
1335 if (info->var.bits_per_pixel == 1) {
1336 /* base is already correct */
1337 xpix = (unsigned char) (var->xoffset % 8);
1338 } else {
1339 base /= 4;
1340 xpix = (unsigned char) ((xoffset % 4) * 2);
1341 }
1342
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001343 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001344 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
1346 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001347 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1348 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001350 /* 0xf2 is %11110010, exclude tmp bits */
1351 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 /* construct bits 16, 17 and 18 of screen start address */
1353 if (base & 0x10000)
1354 tmp |= 0x01;
1355 if (base & 0x20000)
1356 tmp |= 0x04;
1357 if (base & 0x40000)
1358 tmp |= 0x08;
1359
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001360 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001363 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001364 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1365 if (is_laguna(cinfo))
1366 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1367 else
1368 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001369 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1370 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Krzysztof Helt8503df62007-10-16 01:29:08 -07001372 /* write pixel panning value to AR33; this does not quite work in 8bpp
1373 *
1374 * ### Piccolo..? Will this work?
1375 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380}
1381
Krzysztof Helt8503df62007-10-16 01:29:08 -07001382static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383{
1384 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001385 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1386 * then the caller blanks by setting the CLUT (Color Look Up Table)
1387 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1388 * failed due to e.g. a video mode which doesn't support it.
1389 * Implements VESA suspend and powerdown modes on hardware that
1390 * supports disabling hsync/vsync:
1391 * blank_mode == 2: suspend vsync
1392 * blank_mode == 3: suspend hsync
1393 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 */
1395 unsigned char val;
1396 struct cirrusfb_info *cinfo = info->par;
1397 int current_mode = cinfo->blank_mode;
1398
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001399 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400
1401 if (info->state != FBINFO_STATE_RUNNING ||
1402 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001403 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404 return 0;
1405 }
1406
1407 /* Undo current */
1408 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001409 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001410 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001411 val = 0;
1412 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001413 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001414 val = 0x20;
1415
1416 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1417 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418
1419 switch (blank_mode) {
1420 case FB_BLANK_UNBLANK:
1421 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001422 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 break;
1424 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001425 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 break;
1427 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001428 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 break;
1430 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001431 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 break;
1433 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001434 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 return 1;
1436 }
1437
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001438 vga_wgfx(cinfo->regbase, CL_GRE, val);
1439
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001441 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 /* Let fbcon do a soft blank for us */
1444 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1445}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001446
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447/**** END Hardware specific Routines **************************************/
1448/****************************************************************************/
1449/**** BEGIN Internal Routines ***********************************************/
1450
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001451static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001453 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 const struct cirrusfb_board_info_rec *bi;
1455
Krzysztof Helt8503df62007-10-16 01:29:08 -07001456 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
1458 bi = &cirrusfb_board_info[cinfo->btype];
1459
1460 /* reset board globally */
1461 switch (cinfo->btype) {
1462 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001463 WSFR(cinfo, 0x01);
1464 udelay(500);
1465 WSFR(cinfo, 0x51);
1466 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 break;
1468 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001469 WSFR2(cinfo, 0xff);
1470 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471 break;
1472 case BT_SD64:
1473 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 WSFR(cinfo, 0x1f);
1475 udelay(500);
1476 WSFR(cinfo, 0x4f);
1477 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 break;
1479 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001480 /* disable flickerfixer */
1481 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1482 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001483 /* mode */
1484 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001485 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 /* from Klaus' NetBSD driver: */
1487 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001488 case BT_ALPINE: /* fall through */
1489 /* put blitter into 542x compat */
1490 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 break;
1492
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001493 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001494 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 /* Nothing to do to reset the board. */
1496 break;
1497
1498 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001499 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 break;
1501 }
1502
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001503 /* make sure RAM size set by this point */
1504 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 /* the P4 is not fully initialized here; I rely on it having been */
1507 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001508 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509
1510 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001511 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1512 WGen(cinfo, CL_POS102, 0x01);
1513 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
1515 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001519 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 /* FullBandwidth (video off) and 8/9 dot clock */
1522 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
Krzysztof Helt8503df62007-10-16 01:29:08 -07001524 /* "magic cookie" - doesn't make any sense to me.. */
1525/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1526 /* unlock all extension registers */
1527 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 switch (cinfo->btype) {
1530 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001531 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 break;
1533 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001534 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001535 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001538#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001540#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1544 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 break;
1546 }
1547 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001548 /* plane mask: nothing */
1549 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1550 /* character map select: doesn't even matter in gx mode */
1551 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001552 /* memory mode: chain4, ext. memory */
1553 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
1555 /* controller-internal base address of video memory */
1556 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1560 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1563 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1564 /* graphics cursor Y position (..."... ) */
1565 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1566 /* graphics cursor attributes */
1567 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1568 /* graphics cursor pattern address */
1569 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570
1571 /* writing these on a P4 might give problems.. */
1572 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001573 /* configuration readback and ext. color */
1574 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1575 /* signature generator */
1576 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 }
1578
Krzysztof Helt8503df62007-10-16 01:29:08 -07001579 /* Screen A preset row scan: none */
1580 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1581 /* Text cursor start: disable text cursor */
1582 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1583 /* Text cursor end: - */
1584 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 /* text cursor location high: 0 */
1586 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1587 /* text cursor location low: 0 */
1588 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 /* Underline Row scanline: - */
1591 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 /* ext. display controls: ext.adr. wrap */
1594 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
Masanari Iidaff0c2642012-07-22 00:23:15 +09001596 /* Set/Reset registers: - */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001597 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1598 /* Set/Reset enable: - */
1599 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1600 /* Color Compare: - */
1601 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1602 /* Data Rotate: - */
1603 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1604 /* Read Map Select: - */
1605 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1606 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1607 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1608 /* Miscellaneous: memory map base address, graphics mode */
1609 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1610 /* Color Don't care: involve all planes */
1611 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1612 /* Bit Mask: no mask at all */
1613 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001614
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001615 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1616 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001617 /* (5434 can't have bit 3 set for bitblt) */
1618 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001620 /* Graphics controller mode extensions: finer granularity,
1621 * 8byte data latches
1622 */
1623 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Krzysztof Helt8503df62007-10-16 01:29:08 -07001625 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1626 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1627 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1628 /* Background color byte 1: - */
1629 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1630 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
Krzysztof Helt8503df62007-10-16 01:29:08 -07001632 /* Attribute Controller palette registers: "identity mapping" */
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1645 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1646 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1647 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1648 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 /* Attribute Controller mode: graphics mode */
1651 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1652 /* Overscan color reg.: reg. 0 */
1653 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1654 /* Color Plane enable: Enable all 4 planes */
1655 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 /* Color Select: - */
1657 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660
Krzysztof Helt8503df62007-10-16 01:29:08 -07001661 /* BLT Start/status: Blitter reset */
1662 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1663 /* - " - : "end-of-reset" */
1664 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
1666 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 return;
1669}
1670
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672{
1673#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1674 static int IsOn = 0; /* XXX not ok for multiple boards */
1675
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 if (cinfo->btype == BT_PICASSO4)
1677 return; /* nothing to switch */
1678 if (cinfo->btype == BT_ALPINE)
1679 return; /* nothing to switch */
1680 if (cinfo->btype == BT_GD5480)
1681 return; /* nothing to switch */
1682 if (cinfo->btype == BT_PICASSO) {
1683 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 return;
1686 }
1687 if (on) {
1688 switch (cinfo->btype) {
1689 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 break;
1692 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
1695 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 break;
1698 default: /* do nothing */ break;
1699 }
1700 } else {
1701 switch (cinfo->btype) {
1702 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001703 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 break;
1705 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 break;
1708 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001711 default: /* do nothing */
1712 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 }
1714 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715#endif /* CONFIG_ZORRO */
1716}
1717
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718/******************************************/
1719/* Linux 2.6-style accelerated functions */
1720/******************************************/
1721
Krzysztof Helt8343c892009-03-31 15:25:11 -07001722static int cirrusfb_sync(struct fb_info *info)
1723{
1724 struct cirrusfb_info *cinfo = info->par;
1725
1726 if (!is_laguna(cinfo)) {
1727 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1728 cpu_relax();
1729 }
1730 return 0;
1731}
1732
Krzysztof Helt8503df62007-10-16 01:29:08 -07001733static void cirrusfb_fillrect(struct fb_info *info,
1734 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 struct fb_fillrect modded;
1737 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001738 struct cirrusfb_info *cinfo = info->par;
1739 int m = info->var.bits_per_pixel;
1740 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1741 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742
1743 if (info->state != FBINFO_STATE_RUNNING)
1744 return;
1745 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1746 cfb_fillrect(info, region);
1747 return;
1748 }
1749
1750 vxres = info->var.xres_virtual;
1751 vyres = info->var.yres_virtual;
1752
1753 memcpy(&modded, region, sizeof(struct fb_fillrect));
1754
Krzysztof Helt8503df62007-10-16 01:29:08 -07001755 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 modded.dx >= vxres || modded.dy >= vyres)
1757 return;
1758
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759 if (modded.dx + modded.width > vxres)
1760 modded.width = vxres - modded.dx;
1761 if (modded.dy + modded.height > vyres)
1762 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763
Krzysztof Helt060b6002007-10-16 01:29:13 -07001764 cirrusfb_RectFill(cinfo->regbase,
1765 info->var.bits_per_pixel,
1766 (region->dx * m) / 8, region->dy,
1767 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001768 color, color,
1769 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770}
1771
Krzysztof Helt8503df62007-10-16 01:29:08 -07001772static void cirrusfb_copyarea(struct fb_info *info,
1773 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 struct fb_copyarea modded;
1776 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001777 struct cirrusfb_info *cinfo = info->par;
1778 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779
1780 if (info->state != FBINFO_STATE_RUNNING)
1781 return;
1782 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1783 cfb_copyarea(info, area);
1784 return;
1785 }
1786
1787 vxres = info->var.xres_virtual;
1788 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001789 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
Krzysztof Helt8503df62007-10-16 01:29:08 -07001791 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 modded.sx >= vxres || modded.sy >= vyres ||
1793 modded.dx >= vxres || modded.dy >= vyres)
1794 return;
1795
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 if (modded.sx + modded.width > vxres)
1797 modded.width = vxres - modded.sx;
1798 if (modded.dx + modded.width > vxres)
1799 modded.width = vxres - modded.dx;
1800 if (modded.sy + modded.height > vyres)
1801 modded.height = vyres - modded.sy;
1802 if (modded.dy + modded.height > vyres)
1803 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
Krzysztof Helt060b6002007-10-16 01:29:13 -07001805 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1806 (area->sx * m) / 8, area->sy,
1807 (area->dx * m) / 8, area->dy,
1808 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001809 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001810
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811}
1812
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813static void cirrusfb_imageblit(struct fb_info *info,
1814 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815{
1816 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001817 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818
Krzysztof Helt9e848062009-03-31 15:25:11 -07001819 if (info->state != FBINFO_STATE_RUNNING)
1820 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001821 /* Alpine/SD64 does not work at 24bpp ??? */
1822 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1823 cfb_imageblit(info, image);
1824 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1825 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001826 cfb_imageblit(info, image);
1827 else {
1828 unsigned size = ((image->width + 7) >> 3) * image->height;
1829 int m = info->var.bits_per_pixel;
1830 u32 fg, bg;
1831
1832 if (info->var.bits_per_pixel == 8) {
1833 fg = image->fg_color;
1834 bg = image->bg_color;
1835 } else {
1836 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1837 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1838 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001839 if (info->var.bits_per_pixel == 24) {
1840 /* clear background first */
1841 cirrusfb_RectFill(cinfo->regbase,
1842 info->var.bits_per_pixel,
1843 (image->dx * m) / 8, image->dy,
1844 (image->width * m) / 8,
1845 image->height,
1846 bg, bg,
1847 info->fix.line_length, 0x40);
1848 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001849 cirrusfb_RectFill(cinfo->regbase,
1850 info->var.bits_per_pixel,
1851 (image->dx * m) / 8, image->dy,
1852 (image->width * m) / 8, image->height,
1853 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001854 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001855 memcpy(info->screen_base, image->data, size);
1856 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857}
1858
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
1862/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1863 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1864 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1865 * seem to have. */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001866static unsigned int cirrusfb_get_memsize(struct fb_info *info,
1867 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868{
1869 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001870 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001872 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001873 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1874
1875 mem = ((SR14 & 7) + 1) << 20;
1876 } else {
1877 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1878 switch ((SRF & 0x18)) {
1879 case 0x08:
1880 mem = 512 * 1024;
1881 break;
1882 case 0x10:
1883 mem = 1024 * 1024;
1884 break;
1885 /* 64-bit DRAM data bus width; assume 2MB.
1886 * Also indicates 2MB memory on the 5430.
1887 */
1888 case 0x18:
1889 mem = 2048 * 1024;
1890 break;
1891 default:
1892 dev_warn(info->device, "Unknown memory size!\n");
1893 mem = 1024 * 1024;
1894 }
1895 /* If DRAM bank switching is enabled, there must be
1896 * twice as much memory installed. (4MB on the 5434)
1897 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001898 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001899 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 return mem;
1904}
1905
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906static void get_pci_addrs(const struct pci_dev *pdev,
1907 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001909 assert(pdev != NULL);
1910 assert(display != NULL);
1911 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 *display = 0;
1914 *registers = 0;
1915
1916 /* This is a best-guess for now */
1917
1918 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1919 *display = pci_resource_start(pdev, 1);
1920 *registers = pci_resource_start(pdev, 0);
1921 } else {
1922 *display = pci_resource_start(pdev, 0);
1923 *registers = pci_resource_start(pdev, 1);
1924 }
1925
Krzysztof Helt8503df62007-10-16 01:29:08 -07001926 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927}
1928
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001929static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001931 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001932 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001934 if (cinfo->laguna_mmio == NULL)
1935 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001936 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937#if 0 /* if system didn't claim this region, we would... */
1938 release_mem_region(0xA0000, 65535);
1939#endif
1940 if (release_io_ports)
1941 release_region(0x3C0, 32);
1942 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943}
1944#endif /* CONFIG_PCI */
1945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001947static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Al Virod91f5bb2007-10-17 00:27:18 +01001949 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001950 struct zorro_dev *zdev = to_zorro_dev(info->device);
1951
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001952 if (info->fix.smem_start > 16 * MB_)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001953 iounmap(info->screen_base);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001954 if (info->fix.mmio_start > 16 * MB_)
1955 iounmap(cinfo->regbase);
1956
1957 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958}
1959#endif /* CONFIG_ZORRO */
1960
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001961/* function table of the above functions */
1962static struct fb_ops cirrusfb_ops = {
1963 .owner = THIS_MODULE,
1964 .fb_open = cirrusfb_open,
1965 .fb_release = cirrusfb_release,
1966 .fb_setcolreg = cirrusfb_setcolreg,
1967 .fb_check_var = cirrusfb_check_var,
1968 .fb_set_par = cirrusfb_set_par,
1969 .fb_pan_display = cirrusfb_pan_display,
1970 .fb_blank = cirrusfb_blank,
1971 .fb_fillrect = cirrusfb_fillrect,
1972 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001973 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001974 .fb_imageblit = cirrusfb_imageblit,
1975};
1976
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001977static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001979 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 struct fb_var_screeninfo *var = &info->var;
1981
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 info->pseudo_palette = cinfo->pseudo_palette;
1983 info->flags = FBINFO_DEFAULT
1984 | FBINFO_HWACCEL_XPAN
1985 | FBINFO_HWACCEL_YPAN
1986 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001987 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001989 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001991 info->fix.accel = FB_ACCEL_NONE;
1992 } else
1993 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
1994
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07001996
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 if (cinfo->btype == BT_GD5480) {
1998 if (var->bits_per_pixel == 16)
1999 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002000 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 info->screen_base += 2 * MB_;
2002 }
2003
2004 /* Fill fix common fields */
2005 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2006 sizeof(info->fix.id));
2007
2008 /* monochrome: only 1 memory plane */
2009 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002010 info->fix.smem_len = info->screen_size;
2011 if (var->bits_per_pixel == 1)
2012 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 info->fix.xpanstep = 1;
2015 info->fix.ypanstep = 1;
2016 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017
2018 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020
2021 fb_alloc_cmap(&info->cmap, 256, 0);
2022
2023 return 0;
2024}
2025
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002026static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002028 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
2031 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002032 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002034 /* set all the vital stuff */
2035 cirrusfb_set_fbinfo(info);
2036
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002037 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002039 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2040 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002041 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002042 err = -EINVAL;
2043 goto err_dealloc_cmap;
2044 }
2045
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 info->var.activate = FB_ACTIVATE_NOW;
2047
Krzysztof Helt99a45842009-03-31 15:25:09 -07002048 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049 if (err < 0) {
2050 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002051 dev_dbg(info->device,
2052 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002053 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 }
2055
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 err = register_framebuffer(info);
2057 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002058 dev_err(info->device,
2059 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 goto err_dealloc_cmap;
2061 }
2062
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 return 0;
2064
2065err_dealloc_cmap:
2066 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 return err;
2068}
2069
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002070static void cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071{
2072 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073
Krzysztof Helt8503df62007-10-16 01:29:08 -07002074 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002075 unregister_framebuffer(info);
2076 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002077 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002078 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002079 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080}
2081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082#ifdef CONFIG_PCI
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002083static int cirrusfb_pci_register(struct pci_dev *pdev,
2084 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085{
2086 struct cirrusfb_info *cinfo;
2087 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 unsigned long board_addr, board_size;
2089 int ret;
2090
2091 ret = pci_enable_device(pdev);
2092 if (ret < 0) {
2093 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2094 goto err_out;
2095 }
2096
2097 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2098 if (!info) {
2099 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2100 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002101 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 }
2103
2104 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002105 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002107 dev_dbg(info->device,
2108 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002109 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002110 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2111 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112
Paul Bolle933ee712013-03-27 00:47:03 +00002113 dev_dbg(info->device,
2114 "Attempt to get PCI info for Cirrus Graphics Card\n");
2115 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
2116 /* FIXME: this forces VGA. alternatives? */
2117 cinfo->regbase = NULL;
2118 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002120 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002121 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002123 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
2126 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002127 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002128 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2129 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 goto err_release_fb;
2131 }
2132#if 0 /* if the system didn't claim this region, we would... */
2133 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002134 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2135 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 ret = -EBUSY;
2137 goto err_release_regions;
2138 }
2139#endif
2140 if (request_region(0x3C0, 32, "cirrusfb"))
2141 release_io_ports = 1;
2142
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002143 info->screen_base = ioremap(board_addr, board_size);
2144 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 ret = -EIO;
2146 goto err_release_legacy;
2147 }
2148
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002149 info->fix.smem_start = board_addr;
2150 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 cinfo->unmap = cirrusfb_pci_unmap;
2152
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002153 dev_info(info->device,
2154 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2155 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 pci_set_drvdata(pdev, info);
2157
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002158 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002159 if (!ret)
2160 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002162 pci_set_drvdata(pdev, NULL);
2163 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164err_release_legacy:
2165 if (release_io_ports)
2166 release_region(0x3C0, 32);
2167#if 0
2168 release_mem_region(0xA0000, 65535);
2169err_release_regions:
2170#endif
2171 pci_release_regions(pdev);
2172err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002173 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002174 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176err_out:
2177 return ret;
2178}
2179
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002180static void cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181{
2182 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183
Krzysztof Helt8503df62007-10-16 01:29:08 -07002184 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185}
2186
2187static struct pci_driver cirrusfb_pci_driver = {
2188 .name = "cirrusfb",
2189 .id_table = cirrusfb_pci_table,
2190 .probe = cirrusfb_pci_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002191 .remove = cirrusfb_pci_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192#ifdef CONFIG_PM
2193#if 0
2194 .suspend = cirrusfb_pci_suspend,
2195 .resume = cirrusfb_pci_resume,
2196#endif
2197#endif
2198};
2199#endif /* CONFIG_PCI */
2200
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201#ifdef CONFIG_ZORRO
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002202static int cirrusfb_zorro_register(struct zorro_dev *z,
2203 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205 struct fb_info *info;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002206 int error;
2207 const struct zorrocl *zcl;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002208 enum cirrus_board btype;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002209 unsigned long regbase, ramsize, rambase;
2210 struct cirrusfb_info *cinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211
2212 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2213 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002214 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002215 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216 }
2217
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002218 zcl = (const struct zorrocl *)ent->driver_data;
2219 btype = zcl->type;
2220 regbase = zorro_resource_start(z) + zcl->regoffset;
2221 ramsize = zcl->ramsize;
2222 if (ramsize) {
2223 rambase = zorro_resource_start(z) + zcl->ramoffset;
Geert Uytterhoevene78bb882011-10-20 13:42:25 +02002224 if (zorro_resource_len(z) == 64 * MB_) {
2225 /* Quirk for 64 MiB Picasso IV */
2226 rambase += zcl->ramoffset;
2227 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002228 } else {
2229 struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
2230 if (!ram || !zorro_resource_len(ram)) {
2231 dev_err(info->device, "No video RAM found\n");
2232 error = -ENODEV;
2233 goto err_release_fb;
2234 }
2235 rambase = zorro_resource_start(ram);
2236 ramsize = zorro_resource_len(ram);
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +02002237 if (zcl->ramid2 &&
2238 (ram = zorro_find_device(zcl->ramid2, NULL))) {
2239 if (zorro_resource_start(ram) != rambase + ramsize) {
2240 dev_warn(info->device,
2241 "Skipping non-contiguous RAM at %pR\n",
2242 &ram->resource);
2243 } else {
2244 ramsize += zorro_resource_len(ram);
2245 }
2246 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002247 }
2248
2249 dev_info(info->device,
2250 "%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
2251 cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
2252 rambase);
2253
2254 if (!zorro_request_device(z, "cirrusfb")) {
2255 dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
2256 error = -EBUSY;
2257 goto err_release_fb;
2258 }
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002259
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 cinfo->btype = btype;
2262
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002263 info->fix.mmio_start = regbase;
2264 cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
2265 : (caddr_t)ZTWO_VADDR(regbase);
2266 if (!cinfo->regbase) {
2267 dev_err(info->device, "Cannot map registers\n");
2268 error = -EIO;
2269 goto err_release_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 }
2271
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002272 info->fix.smem_start = rambase;
2273 info->screen_size = ramsize;
2274 info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
2275 : (caddr_t)ZTWO_VADDR(rambase);
2276 if (!info->screen_base) {
2277 dev_err(info->device, "Cannot map video RAM\n");
2278 error = -EIO;
2279 goto err_unmap_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002281
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 cinfo->unmap = cirrusfb_zorro_unmap;
2283
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002284 dev_info(info->device,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002285 "Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
2286 ramsize / MB_, rambase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002288 /* MCLK select etc. */
2289 if (cirrusfb_board_info[btype].init_sr1f)
2290 vga_wseq(cinfo->regbase, CL_SEQR1F,
2291 cirrusfb_board_info[btype].sr1f);
2292
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002293 error = cirrusfb_register(info);
2294 if (error) {
2295 dev_err(info->device, "Failed to register device, error %d\n",
2296 error);
2297 goto err_unmap_ram;
2298 }
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002299
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002300 zorro_set_drvdata(z, info);
2301 return 0;
2302
2303err_unmap_ram:
2304 if (rambase > 16 * MB_)
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002305 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002307err_unmap_reg:
2308 if (regbase > 16 * MB_)
2309 iounmap(cinfo->regbase);
2310err_release_dev:
2311 zorro_release_device(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312err_release_fb:
2313 framebuffer_release(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002314 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315}
2316
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002317void cirrusfb_zorro_unregister(struct zorro_dev *z)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318{
2319 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320
Krzysztof Helt8503df62007-10-16 01:29:08 -07002321 cirrusfb_cleanup(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002322 zorro_set_drvdata(z, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323}
2324
2325static struct zorro_driver cirrusfb_zorro_driver = {
2326 .name = "cirrusfb",
2327 .id_table = cirrusfb_zorro_table,
2328 .probe = cirrusfb_zorro_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002329 .remove = cirrusfb_zorro_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330};
2331#endif /* CONFIG_ZORRO */
2332
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002334static int __init cirrusfb_setup(char *options)
2335{
Vlada Pericee119402008-11-19 15:36:45 -08002336 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 if (!options || !*options)
2339 return 0;
2340
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002342 if (!*this_opt)
2343 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 if (!strcmp(this_opt, "noaccel"))
2346 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002347 else if (!strncmp(this_opt, "mode:", 5))
2348 mode_option = this_opt + 5;
2349 else
2350 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 }
2352 return 0;
2353}
2354#endif
2355
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 /*
2357 * Modularization
2358 */
2359
2360MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2361MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2362MODULE_LICENSE("GPL");
2363
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002364static int __init cirrusfb_init(void)
2365{
2366 int error = 0;
2367
2368#ifndef MODULE
2369 char *option = NULL;
2370
2371 if (fb_get_options("cirrusfb", &option))
2372 return -ENODEV;
2373 cirrusfb_setup(option);
2374#endif
2375
2376#ifdef CONFIG_ZORRO
2377 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2378#endif
2379#ifdef CONFIG_PCI
2380 error |= pci_register_driver(&cirrusfb_pci_driver);
2381#endif
2382 return error;
2383}
2384
Krzysztof Helt8503df62007-10-16 01:29:08 -07002385static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386{
2387#ifdef CONFIG_PCI
2388 pci_unregister_driver(&cirrusfb_pci_driver);
2389#endif
2390#ifdef CONFIG_ZORRO
2391 zorro_unregister_driver(&cirrusfb_zorro_driver);
2392#endif
2393}
2394
2395module_init(cirrusfb_init);
2396
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002397module_param(mode_option, charp, 0);
2398MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002399module_param(noaccel, bool, 0);
2400MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002401
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402#ifdef MODULE
2403module_exit(cirrusfb_exit);
2404#endif
2405
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406/**********************************************************************/
2407/* about the following functions - I have used the same names for the */
2408/* functions as Markus Wild did in his Retina driver for NetBSD as */
2409/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002410/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411/**********************************************************************/
2412
2413/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002414static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 int regnum, unsigned char val)
2416{
2417 unsigned long regofs = 0;
2418
2419 if (cinfo->btype == BT_PICASSO) {
2420 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002421/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2422 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2424 regofs = 0xfff;
2425 }
2426
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428}
2429
2430/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002431static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432{
2433 unsigned long regofs = 0;
2434
2435 if (cinfo->btype == BT_PICASSO) {
2436 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002437/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2438 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2440 regofs = 0xfff;
2441 }
2442
Krzysztof Helt8503df62007-10-16 01:29:08 -07002443 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444}
2445
2446/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002449 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 /* if we're just in "write value" mode, write back the */
2453 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454 vga_w(cinfo->regbase, VGA_ATT_IW,
2455 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 }
2457 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002458/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2459 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460
2461 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463}
2464
2465/*** WHDR() - write into the Hidden DAC register ***/
2466/* as the HDR is the only extension register that requires special treatment
2467 * (the other extension registers are accessible just like the "ordinary"
2468 * registers of their functional group) here is a specialized routine for
2469 * accessing the HDR
2470 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472{
2473 unsigned char dummy;
2474
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002475 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002476 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 if (cinfo->btype == BT_PICASSO) {
2478 /* Klaus' hint for correct access to HDR on some boards */
2479 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480 WGen(cinfo, VGA_PEL_MSK, 0x00);
2481 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002483 dummy = RGen(cinfo, VGA_PEL_IW);
2484 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 }
2486 /* now do the usual stuff to access the HDR */
2487
Krzysztof Helt8503df62007-10-16 01:29:08 -07002488 dummy = RGen(cinfo, VGA_PEL_MSK);
2489 udelay(200);
2490 dummy = RGen(cinfo, VGA_PEL_MSK);
2491 udelay(200);
2492 dummy = RGen(cinfo, VGA_PEL_MSK);
2493 udelay(200);
2494 dummy = RGen(cinfo, VGA_PEL_MSK);
2495 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496
Krzysztof Helt8503df62007-10-16 01:29:08 -07002497 WGen(cinfo, VGA_PEL_MSK, val);
2498 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499
2500 if (cinfo->btype == BT_PICASSO) {
2501 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002502 dummy = RGen(cinfo, VGA_PEL_IW);
2503 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504
2505 /* and at the end, restore the mask value */
2506 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002507 WGen(cinfo, VGA_PEL_MSK, 0xff);
2508 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509 }
2510}
2511
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514{
2515#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519#endif
2520}
2521
2522/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002523static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524{
2525#ifdef CONFIG_ZORRO
2526 /* writing an arbitrary value to this one causes the monitor switcher */
2527 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531#endif
2532}
2533
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 unsigned char green, unsigned char blue)
2537{
2538 unsigned int data = VGA_PEL_D;
2539
2540 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542
2543 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002544 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002545 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002546 /* but DAC data register IS, at least for Picasso II */
2547 if (cinfo->btype == BT_PICASSO)
2548 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 vga_w(cinfo->regbase, data, red);
2550 vga_w(cinfo->regbase, data, green);
2551 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553 vga_w(cinfo->regbase, data, blue);
2554 vga_w(cinfo->regbase, data, green);
2555 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 }
2557}
2558
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559#if 0
2560/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002561static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 unsigned char *green, unsigned char *blue)
2563{
2564 unsigned int data = VGA_PEL_D;
2565
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567
2568 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2569 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2570 if (cinfo->btype == BT_PICASSO)
2571 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 *red = vga_r(cinfo->regbase, data);
2573 *green = vga_r(cinfo->regbase, data);
2574 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002576 *blue = vga_r(cinfo->regbase, data);
2577 *green = vga_r(cinfo->regbase, data);
2578 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 }
2580}
2581#endif
2582
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583/*******************************************************************
2584 cirrusfb_WaitBLT()
2585
2586 Wait for the BitBLT engine to complete a possible earlier job
2587*********************************************************************/
2588
2589/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002593 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594}
2595
2596/*******************************************************************
2597 cirrusfb_BitBLT()
2598
2599 perform accelerated "scrolling"
2600********************************************************************/
2601
Krzysztof Helt8343c892009-03-31 15:25:11 -07002602static void cirrusfb_set_blitter(u8 __iomem *regbase,
2603 u_short nwidth, u_short nheight,
2604 u_long nsrc, u_long ndest,
2605 u_short bltmode, u_short line_length)
2606
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 /* dest pitch low */
2610 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2611 /* dest pitch hi */
2612 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2613 /* source pitch low */
2614 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2615 /* source pitch hi */
2616 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617
2618 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002619 /* BLT width low */
2620 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2621 /* BLT width hi */
2622 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623
2624 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 /* BLT height low */
2626 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2627 /* BLT width hi */
2628 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
2630 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002631 /* BLT dest low */
2632 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2633 /* BLT dest mid */
2634 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2635 /* BLT dest hi */
2636 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639 /* BLT src low */
2640 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2641 /* BLT src mid */
2642 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2643 /* BLT src hi */
2644 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645
2646 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002647 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
2652 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002653 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002654}
2655
2656/*******************************************************************
2657 cirrusfb_BitBLT()
2658
2659 perform accelerated "scrolling"
2660********************************************************************/
2661
2662static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2663 u_short curx, u_short cury,
2664 u_short destx, u_short desty,
2665 u_short width, u_short height,
2666 u_short line_length)
2667{
2668 u_short nwidth = width - 1;
2669 u_short nheight = height - 1;
2670 u_long nsrc, ndest;
2671 u_char bltmode;
2672
2673 bltmode = 0x00;
2674 /* if source adr < dest addr, do the Blt backwards */
2675 if (cury <= desty) {
2676 if (cury == desty) {
2677 /* if src and dest are on the same line, check x */
2678 if (curx < destx)
2679 bltmode |= 0x01;
2680 } else
2681 bltmode |= 0x01;
2682 }
2683 /* standard case: forward blitting */
2684 nsrc = (cury * line_length) + curx;
2685 ndest = (desty * line_length) + destx;
2686 if (bltmode) {
2687 /* this means start addresses are at the end,
2688 * counting backwards
2689 */
2690 nsrc += nheight * line_length + nwidth;
2691 ndest += nheight * line_length + nwidth;
2692 }
2693
2694 cirrusfb_WaitBLT(regbase);
2695
2696 cirrusfb_set_blitter(regbase, nwidth, nheight,
2697 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698}
2699
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700/*******************************************************************
2701 cirrusfb_RectFill()
2702
2703 perform accelerated rectangle fill
2704********************************************************************/
2705
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002708 u32 fg_color, u32 bg_color, u_short line_length,
2709 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002711 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002712 u_char op;
2713
Krzysztof Helt8503df62007-10-16 01:29:08 -07002714 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 /* This is a ColorExpand Blt, using the */
2717 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002718 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2719 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
Krzysztof Helt9e848062009-03-31 15:25:11 -07002721 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002722 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002723 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2724 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2725 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002726 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002727 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002728 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2729 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002730 op = 0xa0;
2731 }
2732 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002733 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2734 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2735 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002737 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002738 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739}
2740
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741/**************************************************************************
2742 * bestclock() - determine closest possible clock lower(?) than the
2743 * desired pixel clock
2744 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002745static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002747 int n, d;
2748 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749
Krzysztof Helt8503df62007-10-16 01:29:08 -07002750 assert(nom != NULL);
2751 assert(den != NULL);
2752 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753
2754 *nom = 0;
2755 *den = 0;
2756 *div = 0;
2757
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 if (freq < 8000)
2759 freq = 8000;
2760
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002761 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
2763 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002764 int s = 0;
2765
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002766 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002768 int temp = d;
2769
2770 if (temp > 31) {
2771 s = 1;
2772 temp >>= 1;
2773 }
2774 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002775 h = h > freq ? h - freq : freq - h;
2776 if (h < diff) {
2777 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002779 *den = temp;
2780 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 }
2782 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002783 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002785 if (d > 31) {
2786 s = 1;
2787 d >>= 1;
2788 }
2789 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002790 h = h > freq ? h - freq : freq - h;
2791 if (h < diff) {
2792 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002794 *den = d;
2795 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 }
2797 }
2798 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799}
2800
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801/* -------------------------------------------------------------------------
2802 *
2803 * debugging functions
2804 *
2805 * -------------------------------------------------------------------------
2806 */
2807
2808#ifdef CIRRUSFB_DEBUG
2809
2810/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 * cirrusfb_dbg_print_regs
2812 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2813 * @reg_class: type of registers to read: %CRT, or %SEQ
2814 *
2815 * DESCRIPTION:
2816 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2817 * old-style I/O ports are queried for information, otherwise MMIO is
2818 * used at the given @base address to query the information.
2819 */
2820
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002821static void cirrusfb_dbg_print_regs(struct fb_info *info,
2822 caddr_t regbase,
2823 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824{
2825 va_list list;
2826 unsigned char val = 0;
2827 unsigned reg;
2828 char *name;
2829
Krzysztof Helt8503df62007-10-16 01:29:08 -07002830 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831
Krzysztof Helt8503df62007-10-16 01:29:08 -07002832 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002834 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835
2836 switch (reg_class) {
2837 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002838 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 break;
2840 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002841 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 break;
2843 default:
2844 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002845 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 break;
2847 }
2848
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002849 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850
Krzysztof Helt8503df62007-10-16 01:29:08 -07002851 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 }
2853
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855}
2856
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 * cirrusfb_dbg_reg_dump
2859 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2860 *
2861 * DESCRIPTION:
2862 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2863 * old-style I/O ports are queried for information, otherwise MMIO is
2864 * used at the given @base address to query the information.
2865 */
2866
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002867static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002869 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002871 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 "CR00", 0x00,
2873 "CR01", 0x01,
2874 "CR02", 0x02,
2875 "CR03", 0x03,
2876 "CR04", 0x04,
2877 "CR05", 0x05,
2878 "CR06", 0x06,
2879 "CR07", 0x07,
2880 "CR08", 0x08,
2881 "CR09", 0x09,
2882 "CR0A", 0x0A,
2883 "CR0B", 0x0B,
2884 "CR0C", 0x0C,
2885 "CR0D", 0x0D,
2886 "CR0E", 0x0E,
2887 "CR0F", 0x0F,
2888 "CR10", 0x10,
2889 "CR11", 0x11,
2890 "CR12", 0x12,
2891 "CR13", 0x13,
2892 "CR14", 0x14,
2893 "CR15", 0x15,
2894 "CR16", 0x16,
2895 "CR17", 0x17,
2896 "CR18", 0x18,
2897 "CR22", 0x22,
2898 "CR24", 0x24,
2899 "CR26", 0x26,
2900 "CR2D", 0x2D,
2901 "CR2E", 0x2E,
2902 "CR2F", 0x2F,
2903 "CR30", 0x30,
2904 "CR31", 0x31,
2905 "CR32", 0x32,
2906 "CR33", 0x33,
2907 "CR34", 0x34,
2908 "CR35", 0x35,
2909 "CR36", 0x36,
2910 "CR37", 0x37,
2911 "CR38", 0x38,
2912 "CR39", 0x39,
2913 "CR3A", 0x3A,
2914 "CR3B", 0x3B,
2915 "CR3C", 0x3C,
2916 "CR3D", 0x3D,
2917 "CR3E", 0x3E,
2918 "CR3F", 0x3F,
2919 NULL);
2920
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002921 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002923 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002925 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 "SR00", 0x00,
2927 "SR01", 0x01,
2928 "SR02", 0x02,
2929 "SR03", 0x03,
2930 "SR04", 0x04,
2931 "SR08", 0x08,
2932 "SR09", 0x09,
2933 "SR0A", 0x0A,
2934 "SR0B", 0x0B,
2935 "SR0D", 0x0D,
2936 "SR10", 0x10,
2937 "SR11", 0x11,
2938 "SR12", 0x12,
2939 "SR13", 0x13,
2940 "SR14", 0x14,
2941 "SR15", 0x15,
2942 "SR16", 0x16,
2943 "SR17", 0x17,
2944 "SR18", 0x18,
2945 "SR19", 0x19,
2946 "SR1A", 0x1A,
2947 "SR1B", 0x1B,
2948 "SR1C", 0x1C,
2949 "SR1D", 0x1D,
2950 "SR1E", 0x1E,
2951 "SR1F", 0x1F,
2952 NULL);
2953
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002954 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955}
2956
2957#endif /* CIRRUSFB_DEBUG */
2958