blob: c3a3e344cee3ae6facc075d556739bf340e34a57 [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 /* truncate xoffset and yoffset to maximum if too high */
599 if (var->xoffset > var->xres_virtual - var->xres)
600 var->xoffset = var->xres_virtual - var->xres - 1;
601 if (var->yoffset > var->yres_virtual - var->yres)
602 var->yoffset = var->yres_virtual - var->yres - 1;
603
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 var->red.msb_right =
605 var->green.msb_right =
606 var->blue.msb_right =
607 var->transp.offset =
608 var->transp.length =
609 var->transp.msb_right = 0;
610
611 yres = var->yres;
612 if (var->vmode & FB_VMODE_DOUBLE)
613 yres *= 2;
614 else if (var->vmode & FB_VMODE_INTERLACED)
615 yres = (yres + 1) / 2;
616
617 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700618 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700619 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 return -EINVAL;
621 }
622
Krzysztof Helt99a45842009-03-31 15:25:09 -0700623 if (cirrusfb_check_pixclock(var, info))
624 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700626 if (!is_laguna(cinfo))
627 var->accel_flags = FB_ACCELF_TEXT;
628
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 return 0;
630}
631
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700632static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700634 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700635 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700636
Krzysztof Helt8503df62007-10-16 01:29:08 -0700637 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700638 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Krzysztof Helt486ff382008-10-15 22:03:42 -0700640 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700641 dev_dbg(info->device, "Set %s as pixclock source.\n",
642 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 old1f |= 0x40;
644 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
645 if (div == 2)
646 old1e |= 1;
647
648 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700650 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651}
652
653/*************************************************************************
654 cirrusfb_set_par_foo()
655
656 actually writes the values for a new video mode into the hardware,
657**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700658static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
660 struct cirrusfb_info *cinfo = info->par;
661 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 u8 __iomem *regbase = cinfo->regbase;
663 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700664 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700666 int hdispend, hsyncstart, hsyncend, htotal;
667 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700668 long freq;
669 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700670 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700672 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700674
675 switch (var->bits_per_pixel) {
676 case 1:
677 info->fix.line_length = var->xres_virtual / 8;
678 info->fix.visual = FB_VISUAL_MONO10;
679 break;
680
681 case 8:
682 info->fix.line_length = var->xres_virtual;
683 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
684 break;
685
686 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700687 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700688 info->fix.line_length = var->xres_virtual *
689 var->bits_per_pixel >> 3;
690 info->fix.visual = FB_VISUAL_TRUECOLOR;
691 break;
692 }
693 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700695 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 bi = &cirrusfb_board_info[cinfo->btype];
698
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700699 hsyncstart = var->xres + var->right_margin;
700 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700701 htotal = (hsyncend + var->left_margin) / 8;
702 hdispend = var->xres / 8;
703 hsyncstart = hsyncstart / 8;
704 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700705
Krzysztof Helt8636a922009-03-31 15:25:17 -0700706 vdispend = var->yres;
707 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700708 vsyncend = vsyncstart + var->vsync_len;
709 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700710
711 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700712 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700713 vsyncstart *= 2;
714 vsyncend *= 2;
715 vtotal *= 2;
716 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700717 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 vsyncstart = (vsyncstart + 1) / 2;
719 vsyncend = (vsyncend + 1) / 2;
720 vtotal = (vtotal + 1) / 2;
721 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700722 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700723 if (yres >= 1024) {
724 vtotal /= 2;
725 vsyncstart /= 2;
726 vsyncend /= 2;
727 vdispend /= 2;
728 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700729
730 vdispend -= 1;
731 vsyncstart -= 1;
732 vsyncend -= 1;
733 vtotal -= 2;
734
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700735 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700736 htotal /= 2;
737 hsyncstart /= 2;
738 hsyncend /= 2;
739 hdispend /= 2;
740 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700741
742 htotal -= 5;
743 hdispend -= 1;
744 hsyncstart += 1;
745 hsyncend += 1;
746
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700748 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
750 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700751 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700754 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700755 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700757 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Krzysztof Helt8503df62007-10-16 01:29:08 -0700760 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700761 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700762 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700765 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700768 tmp = hsyncend % 32;
769 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700771 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700772 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
777 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700788 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700792 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700793 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700796 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 tmp |= 0x20;
798 if (var->vmode & FB_VMODE_DOUBLE)
799 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700800 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700801 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700803 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700806 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700810 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700813 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700816 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700819 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 tmp = 0;
822 if (var->vmode & FB_VMODE_INTERLACED)
823 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700830 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 tmp |= 128;
832
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700833 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700836 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700837 if (var->bits_per_pixel == 24)
838 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
839 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700840 if (cinfo->multiplexing)
841 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700842 if (cinfo->doubleVCLK)
843 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700844
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700845 bestclock(freq, &nom, &den, &div);
846
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700847 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
848 freq, nom, den, div);
849
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 /* set VCLK0 */
851 /* hardware RefClock: 14.31818 MHz */
852 /* formula: VClk = (OSC * N) / (D * (1+P)) */
853 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
854
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700855 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
856 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700857 /* if freq is close to mclk or mclk/2 select mclk
858 * as clock source
859 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700860 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700861 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700862 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700863 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700864 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700865 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700866 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
867 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
868 unsigned short tile_control;
869
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700870 if (cinfo->btype == BT_LAGUNAB) {
871 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
872 tile_control &= ~0x80;
873 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
874 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700875
876 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
877 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
878 control = fb_readw(cinfo->laguna_mmio + 0x402);
879 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
880 control &= ~0x6800;
881 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700882 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700883 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700884 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700885 tmp = den << 1;
886 if (div != 0)
887 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700888 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
889 if ((cinfo->btype == BT_SD64) ||
890 (cinfo->btype == BT_ALPINE) ||
891 (cinfo->btype == BT_GD5480))
892 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700894 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700895 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700896 vga_wseq(regbase, CL_SEQRE, tmp);
897 vga_wseq(regbase, CL_SEQR1E, nom);
898 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700899 vga_wseq(regbase, CL_SEQRE, nom);
900 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700901 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700902 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700904 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 else
908 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
909 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700910 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 /* don't know if it would hurt to also program this if no interlaced */
913 /* mode is used, but I feel better this way.. :-) */
914 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700915 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700917 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700919 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700920 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700921 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
923 tmp |= 0x40;
924 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
925 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Krzysztof Helt8503df62007-10-16 01:29:08 -0700928 /* text cursor on and start line */
929 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
930 /* text cursor end line */
931 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
933 /******************************************************
934 *
935 * 1 bpp
936 *
937 */
938
939 /* programming for different color depths */
940 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700941 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700942 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
944 /* SR07 */
945 switch (cinfo->btype) {
946 case BT_SD64:
947 case BT_PICCOLO:
948 case BT_PICASSO:
949 case BT_SPECTRUM:
950 case BT_PICASSO4:
951 case BT_ALPINE:
952 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700953 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700954 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 bi->sr07_1bpp_mux : bi->sr07_1bpp);
956 break;
957
958 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700959 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700960 vga_wseq(regbase, CL_SEQR7,
961 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 break;
963
964 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700965 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 break;
967 }
968
969 /* Extended Sequencer Mode */
970 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971
972 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700973 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700974 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
975 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 break;
977
978 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* ## vorher d0 avoid FIFO underruns..? */
980 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 break;
982
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700983 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 case BT_PICASSO4:
985 case BT_ALPINE:
986 case BT_GD5480:
987 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700988 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 /* do nothing */
990 break;
991
992 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700993 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 break;
995 }
996
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* pixel mask: pass-through for first plane */
998 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700999 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* hidden dac reg: 1280x1024 */
1001 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001003 /* hidden dac: nothing */
1004 WHDR(cinfo, 0);
1005 /* memory mode: odd/even, ext. memory */
1006 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1007 /* plane mask: only write to first plane */
1008 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 }
1010
1011 /******************************************************
1012 *
1013 * 8 bpp
1014 *
1015 */
1016
1017 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001018 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 switch (cinfo->btype) {
1020 case BT_SD64:
1021 case BT_PICCOLO:
1022 case BT_PICASSO:
1023 case BT_SPECTRUM:
1024 case BT_PICASSO4:
1025 case BT_ALPINE:
1026 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001028 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1030 break;
1031
1032 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001033 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001034 vga_wseq(regbase, CL_SEQR7,
1035 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001036 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038
1039 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001040 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 break;
1042 }
1043
1044 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001048 /* Fast Page-Mode writes */
1049 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 break;
1051
1052 case BT_PICASSO4:
1053#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001054 /* ### INCOMPLETE!! */
1055 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001058 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 case BT_GD5480:
1060 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001061 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 /* do nothing */
1063 break;
1064
1065 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001066 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 break;
1068 }
1069
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* mode register: 256 color mode */
1071 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001072 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* hidden dac reg: 1280x1024 */
1074 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* hidden dac: nothing */
1077 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 }
1079
1080 /******************************************************
1081 *
1082 * 16 bpp
1083 *
1084 */
1085
1086 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001087 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001090 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001091 vga_wseq(regbase, CL_SEQR7, 0x87);
1092 /* Fast Page-Mode writes */
1093 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 break;
1095
1096 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 vga_wseq(regbase, CL_SEQR7, 0x27);
1098 /* Fast Page-Mode writes */
1099 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 break;
1101
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001102 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001105 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001106 vga_wseq(regbase, CL_SEQR7,
1107 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
1110 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001111 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112 /* We already set SRF and SR1F */
1113 break;
1114
1115 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001116 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 vga_wseq(regbase, CL_SEQR7,
1118 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001119 control |= 0x2000;
1120 format |= 0x1400;
1121 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123
1124 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001125 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 break;
1127 }
1128
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 /* mode register: 256 color mode */
1130 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001132 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133#elif defined(CONFIG_ZORRO)
1134 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 }
1138
1139 /******************************************************
1140 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001141 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 *
1143 */
1144
Krzysztof Helt7cade312009-03-31 15:25:13 -07001145 else if (var->bits_per_pixel == 24) {
1146 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001149 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001150 vga_wseq(regbase, CL_SEQR7, 0x85);
1151 /* Fast Page-Mode writes */
1152 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 break;
1154
1155 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 vga_wseq(regbase, CL_SEQR7, 0x25);
1157 /* Fast Page-Mode writes */
1158 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001161 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001164 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001165 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 break;
1167
1168 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001169 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 /* We already set SRF and SR1F */
1171 break;
1172
1173 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001174 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 vga_wseq(regbase, CL_SEQR7,
1176 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001177 control |= 0x4000;
1178 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001179 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 break;
1181
1182 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001183 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 break;
1185 }
1186
Krzysztof Helt8503df62007-10-16 01:29:08 -07001187 /* mode register: 256 color mode */
1188 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1190 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 }
1192
1193 /******************************************************
1194 *
1195 * unknown/unsupported bpp
1196 *
1197 */
1198
Krzysztof Helt8503df62007-10-16 01:29:08 -07001199 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001200 dev_err(info->device,
1201 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203
Krzysztof Helt6683e012009-03-31 15:25:06 -07001204 pitch = info->fix.line_length >> 3;
1205 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001207 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 tmp |= 0x10; /* offset overflow bit */
1209
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 /* screen start addr #16-18, fastpagemode cycles */
1211 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001213 /* screen start address bit 19 */
1214 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001215 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001217 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001218 tmp = 0;
1219 if ((htotal + 5) & 256)
1220 tmp |= 128;
1221 if (hdispend & 256)
1222 tmp |= 64;
1223 if (hsyncstart & 256)
1224 tmp |= 48;
1225 if (vtotal & 1024)
1226 tmp |= 8;
1227 if (vdispend & 1024)
1228 tmp |= 4;
1229 if (vsyncstart & 1024)
1230 tmp |= 3;
1231
1232 vga_wcrt(regbase, CL_CRT1E, tmp);
1233 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1234 }
1235
Krzysztof Helt8503df62007-10-16 01:29:08 -07001236 /* pixel panning */
1237 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
1239 /* [ EGS: SetOffset(); ] */
1240 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001241 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001243 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001244 /* no tiles */
1245 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1246 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1247 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1248 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 /* finally, turn on everything - turn off "FullBandwidth" bit */
1250 /* also, set "DotClock%2" bit where requested */
1251 tmp = 0x01;
1252
1253/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1254 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1255 tmp |= 0x08;
1256*/
1257
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001259 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001262 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263#endif
1264
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 return 0;
1266}
1267
1268/* for some reason incomprehensible to me, cirrusfb requires that you write
1269 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 cirrusfb_set_par_foo(info);
1273 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274}
1275
Krzysztof Helt8503df62007-10-16 01:29:08 -07001276static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1277 unsigned blue, unsigned transp,
1278 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279{
1280 struct cirrusfb_info *cinfo = info->par;
1281
1282 if (regno > 255)
1283 return -EINVAL;
1284
1285 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1286 u32 v;
1287 red >>= (16 - info->var.red.length);
1288 green >>= (16 - info->var.green.length);
1289 blue >>= (16 - info->var.blue.length);
1290
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 return 1;
1293 v = (red << info->var.red.offset) |
1294 (green << info->var.green.offset) |
1295 (blue << info->var.blue.offset);
1296
Krzysztof Helt060b6002007-10-16 01:29:13 -07001297 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298 return 0;
1299 }
1300
Krzysztof Helt8503df62007-10-16 01:29:08 -07001301 if (info->var.bits_per_pixel == 8)
1302 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
1304 return 0;
1305
1306}
1307
1308/*************************************************************************
1309 cirrusfb_pan_display()
1310
1311 performs display panning - provided hardware permits this
1312**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1314 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001316 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001318 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 struct cirrusfb_info *cinfo = info->par;
1320
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 /* no range checks for xoffset and yoffset, */
1322 /* as fb_pan_display has already done this */
1323 if (var->vmode & FB_VMODE_YWRAP)
1324 return -EINVAL;
1325
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Krzysztof Helt99a45842009-03-31 15:25:09 -07001328 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 if (info->var.bits_per_pixel == 1) {
1331 /* base is already correct */
1332 xpix = (unsigned char) (var->xoffset % 8);
1333 } else {
1334 base /= 4;
1335 xpix = (unsigned char) ((xoffset % 4) * 2);
1336 }
1337
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001338 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001339 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
1341 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001342 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1343 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001345 /* 0xf2 is %11110010, exclude tmp bits */
1346 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 /* construct bits 16, 17 and 18 of screen start address */
1348 if (base & 0x10000)
1349 tmp |= 0x01;
1350 if (base & 0x20000)
1351 tmp |= 0x04;
1352 if (base & 0x40000)
1353 tmp |= 0x08;
1354
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001355 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
1357 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001358 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001359 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1360 if (is_laguna(cinfo))
1361 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1362 else
1363 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001364 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Krzysztof Helt8503df62007-10-16 01:29:08 -07001367 /* write pixel panning value to AR33; this does not quite work in 8bpp
1368 *
1369 * ### Piccolo..? Will this work?
1370 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001372 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
Krzysztof Helt8503df62007-10-16 01:29:08 -07001374 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375}
1376
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378{
1379 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001380 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1381 * then the caller blanks by setting the CLUT (Color Look Up Table)
1382 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1383 * failed due to e.g. a video mode which doesn't support it.
1384 * Implements VESA suspend and powerdown modes on hardware that
1385 * supports disabling hsync/vsync:
1386 * blank_mode == 2: suspend vsync
1387 * blank_mode == 3: suspend hsync
1388 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 */
1390 unsigned char val;
1391 struct cirrusfb_info *cinfo = info->par;
1392 int current_mode = cinfo->blank_mode;
1393
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001394 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
1396 if (info->state != FBINFO_STATE_RUNNING ||
1397 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001398 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 return 0;
1400 }
1401
1402 /* Undo current */
1403 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001404 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001406 val = 0;
1407 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001408 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001409 val = 0x20;
1410
1411 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1412 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413
1414 switch (blank_mode) {
1415 case FB_BLANK_UNBLANK:
1416 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001417 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 break;
1419 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001420 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 break;
1425 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001426 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 break;
1428 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001429 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 return 1;
1431 }
1432
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001433 vga_wgfx(cinfo->regbase, CL_GRE, val);
1434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001436 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437
1438 /* Let fbcon do a soft blank for us */
1439 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1440}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001441
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442/**** END Hardware specific Routines **************************************/
1443/****************************************************************************/
1444/**** BEGIN Internal Routines ***********************************************/
1445
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001446static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001448 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 const struct cirrusfb_board_info_rec *bi;
1450
Krzysztof Helt8503df62007-10-16 01:29:08 -07001451 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453 bi = &cirrusfb_board_info[cinfo->btype];
1454
1455 /* reset board globally */
1456 switch (cinfo->btype) {
1457 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001458 WSFR(cinfo, 0x01);
1459 udelay(500);
1460 WSFR(cinfo, 0x51);
1461 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 break;
1463 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464 WSFR2(cinfo, 0xff);
1465 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 break;
1467 case BT_SD64:
1468 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001469 WSFR(cinfo, 0x1f);
1470 udelay(500);
1471 WSFR(cinfo, 0x4f);
1472 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 break;
1474 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 /* disable flickerfixer */
1476 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1477 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 /* mode */
1479 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Gustavo A. R. Silva8c152202017-11-09 18:09:32 +01001480 /* fall through */
1481 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001482 /* from Klaus' NetBSD driver: */
1483 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Gustavo A. R. Silva8c152202017-11-09 18:09:32 +01001484 /* fall through */
1485 case BT_ALPINE:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001486 /* put blitter into 542x compat */
1487 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 break;
1489
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001490 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001491 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 /* Nothing to do to reset the board. */
1493 break;
1494
1495 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001496 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 break;
1498 }
1499
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001500 /* make sure RAM size set by this point */
1501 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 /* the P4 is not fully initialized here; I rely on it having been */
1504 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001505 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001508 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1509 WGen(cinfo, CL_POS102, 0x01);
1510 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
1512 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Krzysztof Helt8503df62007-10-16 01:29:08 -07001515 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001516 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 /* FullBandwidth (video off) and 8/9 dot clock */
1519 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 /* "magic cookie" - doesn't make any sense to me.. */
1522/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1523 /* unlock all extension registers */
1524 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 switch (cinfo->btype) {
1527 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529 break;
1530 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001531 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001532 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 break;
1534 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001535#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001537#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 break;
1539 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1541 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 break;
1543 }
1544 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 /* plane mask: nothing */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1547 /* character map select: doesn't even matter in gx mode */
1548 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001549 /* memory mode: chain4, ext. memory */
1550 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
1552 /* controller-internal base address of video memory */
1553 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001554 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
Krzysztof Helt8503df62007-10-16 01:29:08 -07001556 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1557 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1560 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1561 /* graphics cursor Y position (..."... ) */
1562 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1563 /* graphics cursor attributes */
1564 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1565 /* graphics cursor pattern address */
1566 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567
1568 /* writing these on a P4 might give problems.. */
1569 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001570 /* configuration readback and ext. color */
1571 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1572 /* signature generator */
1573 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574 }
1575
Krzysztof Helt8503df62007-10-16 01:29:08 -07001576 /* Screen A preset row scan: none */
1577 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1578 /* Text cursor start: disable text cursor */
1579 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1580 /* Text cursor end: - */
1581 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001582 /* text cursor location high: 0 */
1583 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1584 /* text cursor location low: 0 */
1585 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 /* Underline Row scanline: - */
1588 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 /* ext. display controls: ext.adr. wrap */
1591 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
Masanari Iidaff0c2642012-07-22 00:23:15 +09001593 /* Set/Reset registers: - */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001594 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1595 /* Set/Reset enable: - */
1596 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1597 /* Color Compare: - */
1598 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1599 /* Data Rotate: - */
1600 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1601 /* Read Map Select: - */
1602 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1603 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1604 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1605 /* Miscellaneous: memory map base address, graphics mode */
1606 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1607 /* Color Don't care: involve all planes */
1608 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1609 /* Bit Mask: no mask at all */
1610 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001611
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001612 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1613 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* (5434 can't have bit 3 set for bitblt) */
1615 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001617 /* Graphics controller mode extensions: finer granularity,
1618 * 8byte data latches
1619 */
1620 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Krzysztof Helt8503df62007-10-16 01:29:08 -07001622 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1623 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1624 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1625 /* Background color byte 1: - */
1626 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1627 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628
Krzysztof Helt8503df62007-10-16 01:29:08 -07001629 /* Attribute Controller palette registers: "identity mapping" */
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1645 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* Attribute Controller mode: graphics mode */
1648 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1649 /* Overscan color reg.: reg. 0 */
1650 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1651 /* Color Plane enable: Enable all 4 planes */
1652 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 /* Color Select: - */
1654 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 /* BLT Start/status: Blitter reset */
1659 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1660 /* - " - : "end-of-reset" */
1661 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
1663 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 return;
1666}
1667
Krzysztof Helt8503df62007-10-16 01:29:08 -07001668static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669{
1670#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1671 static int IsOn = 0; /* XXX not ok for multiple boards */
1672
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 if (cinfo->btype == BT_PICASSO4)
1674 return; /* nothing to switch */
1675 if (cinfo->btype == BT_ALPINE)
1676 return; /* nothing to switch */
1677 if (cinfo->btype == BT_GD5480)
1678 return; /* nothing to switch */
1679 if (cinfo->btype == BT_PICASSO) {
1680 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001681 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 return;
1683 }
1684 if (on) {
1685 switch (cinfo->btype) {
1686 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 break;
1689 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001690 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 break;
1692 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
1695 default: /* do nothing */ break;
1696 }
1697 } else {
1698 switch (cinfo->btype) {
1699 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 break;
1702 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001703 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 break;
1705 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001708 default: /* do nothing */
1709 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 }
1711 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001712#endif /* CONFIG_ZORRO */
1713}
1714
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715/******************************************/
1716/* Linux 2.6-style accelerated functions */
1717/******************************************/
1718
Krzysztof Helt8343c892009-03-31 15:25:11 -07001719static int cirrusfb_sync(struct fb_info *info)
1720{
1721 struct cirrusfb_info *cinfo = info->par;
1722
1723 if (!is_laguna(cinfo)) {
1724 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1725 cpu_relax();
1726 }
1727 return 0;
1728}
1729
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730static void cirrusfb_fillrect(struct fb_info *info,
1731 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 struct fb_fillrect modded;
1734 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001735 struct cirrusfb_info *cinfo = info->par;
1736 int m = info->var.bits_per_pixel;
1737 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1738 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
1740 if (info->state != FBINFO_STATE_RUNNING)
1741 return;
1742 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1743 cfb_fillrect(info, region);
1744 return;
1745 }
1746
1747 vxres = info->var.xres_virtual;
1748 vyres = info->var.yres_virtual;
1749
1750 memcpy(&modded, region, sizeof(struct fb_fillrect));
1751
Krzysztof Helt8503df62007-10-16 01:29:08 -07001752 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 modded.dx >= vxres || modded.dy >= vyres)
1754 return;
1755
Krzysztof Helt8503df62007-10-16 01:29:08 -07001756 if (modded.dx + modded.width > vxres)
1757 modded.width = vxres - modded.dx;
1758 if (modded.dy + modded.height > vyres)
1759 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760
Krzysztof Helt060b6002007-10-16 01:29:13 -07001761 cirrusfb_RectFill(cinfo->regbase,
1762 info->var.bits_per_pixel,
1763 (region->dx * m) / 8, region->dy,
1764 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001765 color, color,
1766 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767}
1768
Krzysztof Helt8503df62007-10-16 01:29:08 -07001769static void cirrusfb_copyarea(struct fb_info *info,
1770 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 struct fb_copyarea modded;
1773 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001774 struct cirrusfb_info *cinfo = info->par;
1775 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
1777 if (info->state != FBINFO_STATE_RUNNING)
1778 return;
1779 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1780 cfb_copyarea(info, area);
1781 return;
1782 }
1783
1784 vxres = info->var.xres_virtual;
1785 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001786 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
Krzysztof Helt8503df62007-10-16 01:29:08 -07001788 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789 modded.sx >= vxres || modded.sy >= vyres ||
1790 modded.dx >= vxres || modded.dy >= vyres)
1791 return;
1792
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 if (modded.sx + modded.width > vxres)
1794 modded.width = vxres - modded.sx;
1795 if (modded.dx + modded.width > vxres)
1796 modded.width = vxres - modded.dx;
1797 if (modded.sy + modded.height > vyres)
1798 modded.height = vyres - modded.sy;
1799 if (modded.dy + modded.height > vyres)
1800 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
Krzysztof Helt060b6002007-10-16 01:29:13 -07001802 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1803 (area->sx * m) / 8, area->sy,
1804 (area->dx * m) / 8, area->dy,
1805 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001806 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001807
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808}
1809
Krzysztof Helt8503df62007-10-16 01:29:08 -07001810static void cirrusfb_imageblit(struct fb_info *info,
1811 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812{
1813 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001814 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815
Krzysztof Helt9e848062009-03-31 15:25:11 -07001816 if (info->state != FBINFO_STATE_RUNNING)
1817 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001818 /* Alpine/SD64 does not work at 24bpp ??? */
1819 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1820 cfb_imageblit(info, image);
1821 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1822 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001823 cfb_imageblit(info, image);
1824 else {
1825 unsigned size = ((image->width + 7) >> 3) * image->height;
1826 int m = info->var.bits_per_pixel;
1827 u32 fg, bg;
1828
1829 if (info->var.bits_per_pixel == 8) {
1830 fg = image->fg_color;
1831 bg = image->bg_color;
1832 } else {
1833 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1834 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1835 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001836 if (info->var.bits_per_pixel == 24) {
1837 /* clear background first */
1838 cirrusfb_RectFill(cinfo->regbase,
1839 info->var.bits_per_pixel,
1840 (image->dx * m) / 8, image->dy,
1841 (image->width * m) / 8,
1842 image->height,
1843 bg, bg,
1844 info->fix.line_length, 0x40);
1845 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001846 cirrusfb_RectFill(cinfo->regbase,
1847 info->var.bits_per_pixel,
1848 (image->dx * m) / 8, image->dy,
1849 (image->width * m) / 8, image->height,
1850 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001851 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001852 memcpy(info->screen_base, image->data, size);
1853 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854}
1855
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001857static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858
1859/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1860 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1861 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1862 * seem to have. */
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001863static unsigned int cirrusfb_get_memsize(struct fb_info *info,
1864 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865{
1866 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001867 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001869 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001870 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1871
1872 mem = ((SR14 & 7) + 1) << 20;
1873 } else {
1874 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1875 switch ((SRF & 0x18)) {
1876 case 0x08:
1877 mem = 512 * 1024;
1878 break;
1879 case 0x10:
1880 mem = 1024 * 1024;
1881 break;
1882 /* 64-bit DRAM data bus width; assume 2MB.
1883 * Also indicates 2MB memory on the 5430.
1884 */
1885 case 0x18:
1886 mem = 2048 * 1024;
1887 break;
1888 default:
1889 dev_warn(info->device, "Unknown memory size!\n");
1890 mem = 1024 * 1024;
1891 }
1892 /* If DRAM bank switching is enabled, there must be
1893 * twice as much memory installed. (4MB on the 5434)
1894 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001895 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001896 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001898
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 return mem;
1901}
1902
Krzysztof Helt8503df62007-10-16 01:29:08 -07001903static void get_pci_addrs(const struct pci_dev *pdev,
1904 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906 assert(pdev != NULL);
1907 assert(display != NULL);
1908 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 *display = 0;
1911 *registers = 0;
1912
1913 /* This is a best-guess for now */
1914
1915 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1916 *display = pci_resource_start(pdev, 1);
1917 *registers = pci_resource_start(pdev, 0);
1918 } else {
1919 *display = pci_resource_start(pdev, 0);
1920 *registers = pci_resource_start(pdev, 1);
1921 }
1922
Krzysztof Helt8503df62007-10-16 01:29:08 -07001923 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924}
1925
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001926static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001928 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001929 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001931 if (cinfo->laguna_mmio == NULL)
1932 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001933 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934#if 0 /* if system didn't claim this region, we would... */
1935 release_mem_region(0xA0000, 65535);
1936#endif
1937 if (release_io_ports)
1938 release_region(0x3C0, 32);
1939 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940}
1941#endif /* CONFIG_PCI */
1942
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001944static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945{
Al Virod91f5bb2007-10-17 00:27:18 +01001946 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001947 struct zorro_dev *zdev = to_zorro_dev(info->device);
1948
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001949 if (info->fix.smem_start > 16 * MB_)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001950 iounmap(info->screen_base);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02001951 if (info->fix.mmio_start > 16 * MB_)
1952 iounmap(cinfo->regbase);
1953
1954 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955}
1956#endif /* CONFIG_ZORRO */
1957
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001958/* function table of the above functions */
Jani Nikula8a48ac332019-12-03 18:38:50 +02001959static const struct fb_ops cirrusfb_ops = {
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001960 .owner = THIS_MODULE,
1961 .fb_open = cirrusfb_open,
1962 .fb_release = cirrusfb_release,
1963 .fb_setcolreg = cirrusfb_setcolreg,
1964 .fb_check_var = cirrusfb_check_var,
1965 .fb_set_par = cirrusfb_set_par,
1966 .fb_pan_display = cirrusfb_pan_display,
1967 .fb_blank = cirrusfb_blank,
1968 .fb_fillrect = cirrusfb_fillrect,
1969 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001970 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001971 .fb_imageblit = cirrusfb_imageblit,
1972};
1973
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08001974static int cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001976 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 struct fb_var_screeninfo *var = &info->var;
1978
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979 info->pseudo_palette = cinfo->pseudo_palette;
1980 info->flags = FBINFO_DEFAULT
1981 | FBINFO_HWACCEL_XPAN
1982 | FBINFO_HWACCEL_YPAN
1983 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001984 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001986 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001988 info->fix.accel = FB_ACCEL_NONE;
1989 } else
1990 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
1991
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07001993
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 if (cinfo->btype == BT_GD5480) {
1995 if (var->bits_per_pixel == 16)
1996 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001997 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 info->screen_base += 2 * MB_;
1999 }
2000
2001 /* Fill fix common fields */
2002 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2003 sizeof(info->fix.id));
2004
2005 /* monochrome: only 1 memory plane */
2006 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002007 info->fix.smem_len = info->screen_size;
2008 if (var->bits_per_pixel == 1)
2009 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->fix.xpanstep = 1;
2012 info->fix.ypanstep = 1;
2013 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014
2015 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017
2018 fb_alloc_cmap(&info->cmap, 256, 0);
2019
2020 return 0;
2021}
2022
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002023static int cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002025 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027
2028 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002029 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002031 /* set all the vital stuff */
2032 cirrusfb_set_fbinfo(info);
2033
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002034 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002036 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2037 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002038 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002039 err = -EINVAL;
2040 goto err_dealloc_cmap;
2041 }
2042
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043 info->var.activate = FB_ACTIVATE_NOW;
2044
Krzysztof Helt99a45842009-03-31 15:25:09 -07002045 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 if (err < 0) {
2047 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002048 dev_dbg(info->device,
2049 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002050 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 }
2052
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 err = register_framebuffer(info);
2054 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002055 dev_err(info->device,
2056 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002057 goto err_dealloc_cmap;
2058 }
2059
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 return 0;
2061
2062err_dealloc_cmap:
2063 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 return err;
2065}
2066
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002067static void cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068{
2069 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070
Krzysztof Helt8503df62007-10-16 01:29:08 -07002071 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002072 unregister_framebuffer(info);
2073 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002074 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002075 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002076 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077}
2078
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079#ifdef CONFIG_PCI
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002080static int cirrusfb_pci_register(struct pci_dev *pdev,
2081 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082{
2083 struct cirrusfb_info *cinfo;
2084 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 unsigned long board_addr, board_size;
2086 int ret;
2087
2088 ret = pci_enable_device(pdev);
2089 if (ret < 0) {
2090 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2091 goto err_out;
2092 }
2093
2094 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2095 if (!info) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002097 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 }
2099
2100 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002101 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002103 dev_dbg(info->device,
2104 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002105 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002106 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2107 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108
Paul Bolle933ee712013-03-27 00:47:03 +00002109 dev_dbg(info->device,
2110 "Attempt to get PCI info for Cirrus Graphics Card\n");
2111 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
2112 /* FIXME: this forces VGA. alternatives? */
2113 cinfo->regbase = NULL;
2114 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002116 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002117 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002119 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002120 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121
2122 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002123 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2125 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 goto err_release_fb;
2127 }
2128#if 0 /* if the system didn't claim this region, we would... */
2129 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002130 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2131 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132 ret = -EBUSY;
2133 goto err_release_regions;
2134 }
2135#endif
2136 if (request_region(0x3C0, 32, "cirrusfb"))
2137 release_io_ports = 1;
2138
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002139 info->screen_base = ioremap(board_addr, board_size);
2140 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 ret = -EIO;
2142 goto err_release_legacy;
2143 }
2144
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002145 info->fix.smem_start = board_addr;
2146 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 cinfo->unmap = cirrusfb_pci_unmap;
2148
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002149 dev_info(info->device,
2150 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2151 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 pci_set_drvdata(pdev, info);
2153
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002154 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002155 if (!ret)
2156 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002158 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159err_release_legacy:
2160 if (release_io_ports)
2161 release_region(0x3C0, 32);
2162#if 0
2163 release_mem_region(0xA0000, 65535);
2164err_release_regions:
2165#endif
2166 pci_release_regions(pdev);
2167err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002168 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002169 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171err_out:
2172 return ret;
2173}
2174
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002175static void cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176{
2177 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178
Krzysztof Helt8503df62007-10-16 01:29:08 -07002179 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180}
2181
2182static struct pci_driver cirrusfb_pci_driver = {
2183 .name = "cirrusfb",
2184 .id_table = cirrusfb_pci_table,
2185 .probe = cirrusfb_pci_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002186 .remove = cirrusfb_pci_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187#ifdef CONFIG_PM
2188#if 0
2189 .suspend = cirrusfb_pci_suspend,
2190 .resume = cirrusfb_pci_resume,
2191#endif
2192#endif
2193};
2194#endif /* CONFIG_PCI */
2195
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196#ifdef CONFIG_ZORRO
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002197static int cirrusfb_zorro_register(struct zorro_dev *z,
2198 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 struct fb_info *info;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002201 int error;
2202 const struct zorrocl *zcl;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002203 enum cirrus_board btype;
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002204 unsigned long regbase, ramsize, rambase;
2205 struct cirrusfb_info *cinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206
2207 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
Bartlomiej Zolnierkiewicz0adcdbc2019-06-28 12:30:08 +02002208 if (!info)
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002209 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002211 zcl = (const struct zorrocl *)ent->driver_data;
2212 btype = zcl->type;
2213 regbase = zorro_resource_start(z) + zcl->regoffset;
2214 ramsize = zcl->ramsize;
2215 if (ramsize) {
2216 rambase = zorro_resource_start(z) + zcl->ramoffset;
Geert Uytterhoevene78bb882011-10-20 13:42:25 +02002217 if (zorro_resource_len(z) == 64 * MB_) {
2218 /* Quirk for 64 MiB Picasso IV */
2219 rambase += zcl->ramoffset;
2220 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002221 } else {
2222 struct zorro_dev *ram = zorro_find_device(zcl->ramid, NULL);
2223 if (!ram || !zorro_resource_len(ram)) {
2224 dev_err(info->device, "No video RAM found\n");
2225 error = -ENODEV;
2226 goto err_release_fb;
2227 }
2228 rambase = zorro_resource_start(ram);
2229 ramsize = zorro_resource_len(ram);
Geert Uytterhoeven17bdf482011-10-20 13:42:24 +02002230 if (zcl->ramid2 &&
2231 (ram = zorro_find_device(zcl->ramid2, NULL))) {
2232 if (zorro_resource_start(ram) != rambase + ramsize) {
2233 dev_warn(info->device,
2234 "Skipping non-contiguous RAM at %pR\n",
2235 &ram->resource);
2236 } else {
2237 ramsize += zorro_resource_len(ram);
2238 }
2239 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002240 }
2241
2242 dev_info(info->device,
2243 "%s board detected, REG at 0x%lx, %lu MiB RAM at 0x%lx\n",
2244 cirrusfb_board_info[btype].name, regbase, ramsize / MB_,
2245 rambase);
2246
2247 if (!zorro_request_device(z, "cirrusfb")) {
2248 dev_err(info->device, "Cannot reserve %pR\n", &z->resource);
2249 error = -EBUSY;
2250 goto err_release_fb;
2251 }
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002252
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 cinfo->btype = btype;
2255
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002256 info->fix.mmio_start = regbase;
2257 cinfo->regbase = regbase > 16 * MB_ ? ioremap(regbase, 64 * 1024)
Geert Uytterhoeven6112ea02011-01-09 11:03:43 +01002258 : ZTWO_VADDR(regbase);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002259 if (!cinfo->regbase) {
2260 dev_err(info->device, "Cannot map registers\n");
2261 error = -EIO;
2262 goto err_release_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263 }
2264
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002265 info->fix.smem_start = rambase;
2266 info->screen_size = ramsize;
2267 info->screen_base = rambase > 16 * MB_ ? ioremap(rambase, ramsize)
Geert Uytterhoeven6112ea02011-01-09 11:03:43 +01002268 : ZTWO_VADDR(rambase);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002269 if (!info->screen_base) {
2270 dev_err(info->device, "Cannot map video RAM\n");
2271 error = -EIO;
2272 goto err_unmap_reg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 }
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002274
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 cinfo->unmap = cirrusfb_zorro_unmap;
2276
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002277 dev_info(info->device,
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002278 "Cirrus Logic chipset on Zorro bus, RAM (%lu MiB) at 0x%lx\n",
2279 ramsize / MB_, rambase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002281 /* MCLK select etc. */
2282 if (cirrusfb_board_info[btype].init_sr1f)
2283 vga_wseq(cinfo->regbase, CL_SEQR1F,
2284 cirrusfb_board_info[btype].sr1f);
2285
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002286 error = cirrusfb_register(info);
2287 if (error) {
2288 dev_err(info->device, "Failed to register device, error %d\n",
2289 error);
2290 goto err_unmap_ram;
2291 }
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002292
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002293 zorro_set_drvdata(z, info);
2294 return 0;
2295
2296err_unmap_ram:
2297 if (rambase > 16 * MB_)
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002298 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002300err_unmap_reg:
2301 if (regbase > 16 * MB_)
2302 iounmap(cinfo->regbase);
2303err_release_dev:
2304 zorro_release_device(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305err_release_fb:
2306 framebuffer_release(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002307 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308}
2309
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002310void cirrusfb_zorro_unregister(struct zorro_dev *z)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002311{
2312 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313
Krzysztof Helt8503df62007-10-16 01:29:08 -07002314 cirrusfb_cleanup(info);
Geert Uytterhoeven0e0d1332011-10-20 13:42:23 +02002315 zorro_set_drvdata(z, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316}
2317
2318static struct zorro_driver cirrusfb_zorro_driver = {
2319 .name = "cirrusfb",
2320 .id_table = cirrusfb_zorro_table,
2321 .probe = cirrusfb_zorro_register,
Greg Kroah-Hartman48c68c42012-12-21 13:07:39 -08002322 .remove = cirrusfb_zorro_unregister,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323};
2324#endif /* CONFIG_ZORRO */
2325
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002327static int __init cirrusfb_setup(char *options)
2328{
Vlada Pericee119402008-11-19 15:36:45 -08002329 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 if (!options || !*options)
2332 return 0;
2333
Krzysztof Helt8503df62007-10-16 01:29:08 -07002334 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002335 if (!*this_opt)
2336 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 if (!strcmp(this_opt, "noaccel"))
2339 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002340 else if (!strncmp(this_opt, "mode:", 5))
2341 mode_option = this_opt + 5;
2342 else
2343 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 }
2345 return 0;
2346}
2347#endif
2348
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 /*
2350 * Modularization
2351 */
2352
2353MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2354MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2355MODULE_LICENSE("GPL");
2356
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002357static int __init cirrusfb_init(void)
2358{
2359 int error = 0;
2360
2361#ifndef MODULE
2362 char *option = NULL;
2363
2364 if (fb_get_options("cirrusfb", &option))
2365 return -ENODEV;
2366 cirrusfb_setup(option);
2367#endif
2368
2369#ifdef CONFIG_ZORRO
2370 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2371#endif
2372#ifdef CONFIG_PCI
2373 error |= pci_register_driver(&cirrusfb_pci_driver);
2374#endif
2375 return error;
2376}
2377
Krzysztof Helt8503df62007-10-16 01:29:08 -07002378static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002379{
2380#ifdef CONFIG_PCI
2381 pci_unregister_driver(&cirrusfb_pci_driver);
2382#endif
2383#ifdef CONFIG_ZORRO
2384 zorro_unregister_driver(&cirrusfb_zorro_driver);
2385#endif
2386}
2387
2388module_init(cirrusfb_init);
2389
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002390module_param(mode_option, charp, 0);
2391MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002392module_param(noaccel, bool, 0);
2393MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002394
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395#ifdef MODULE
2396module_exit(cirrusfb_exit);
2397#endif
2398
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399/**********************************************************************/
2400/* about the following functions - I have used the same names for the */
2401/* functions as Markus Wild did in his Retina driver for NetBSD as */
2402/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002403/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404/**********************************************************************/
2405
2406/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002407static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 int regnum, unsigned char val)
2409{
2410 unsigned long regofs = 0;
2411
2412 if (cinfo->btype == BT_PICASSO) {
2413 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002414/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2415 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2417 regofs = 0xfff;
2418 }
2419
Krzysztof Helt8503df62007-10-16 01:29:08 -07002420 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421}
2422
2423/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002424static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425{
2426 unsigned long regofs = 0;
2427
2428 if (cinfo->btype == BT_PICASSO) {
2429 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002430/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2431 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2433 regofs = 0xfff;
2434 }
2435
Krzysztof Helt8503df62007-10-16 01:29:08 -07002436 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437}
2438
2439/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 /* if we're just in "write value" mode, write back the */
2446 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447 vga_w(cinfo->regbase, VGA_ATT_IW,
2448 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 }
2450 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2452 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453
2454 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002455 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456}
2457
2458/*** WHDR() - write into the Hidden DAC register ***/
2459/* as the HDR is the only extension register that requires special treatment
2460 * (the other extension registers are accessible just like the "ordinary"
2461 * registers of their functional group) here is a specialized routine for
2462 * accessing the HDR
2463 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465{
2466 unsigned char dummy;
2467
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002468 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002469 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 if (cinfo->btype == BT_PICASSO) {
2471 /* Klaus' hint for correct access to HDR on some boards */
2472 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473 WGen(cinfo, VGA_PEL_MSK, 0x00);
2474 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 dummy = RGen(cinfo, VGA_PEL_IW);
2477 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478 }
2479 /* now do the usual stuff to access the HDR */
2480
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 dummy = RGen(cinfo, VGA_PEL_MSK);
2482 udelay(200);
2483 dummy = RGen(cinfo, VGA_PEL_MSK);
2484 udelay(200);
2485 dummy = RGen(cinfo, VGA_PEL_MSK);
2486 udelay(200);
2487 dummy = RGen(cinfo, VGA_PEL_MSK);
2488 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490 WGen(cinfo, VGA_PEL_MSK, val);
2491 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492
2493 if (cinfo->btype == BT_PICASSO) {
2494 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002495 dummy = RGen(cinfo, VGA_PEL_IW);
2496 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497
2498 /* and at the end, restore the mask value */
2499 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 WGen(cinfo, VGA_PEL_MSK, 0xff);
2501 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 }
2503}
2504
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002506static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507{
2508#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002509 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512#endif
2513}
2514
2515/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517{
2518#ifdef CONFIG_ZORRO
2519 /* writing an arbitrary value to this one causes the monitor switcher */
2520 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002523 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524#endif
2525}
2526
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 unsigned char green, unsigned char blue)
2530{
2531 unsigned int data = VGA_PEL_D;
2532
2533 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535
2536 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002537 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002538 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 /* but DAC data register IS, at least for Picasso II */
2540 if (cinfo->btype == BT_PICASSO)
2541 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542 vga_w(cinfo->regbase, data, red);
2543 vga_w(cinfo->regbase, data, green);
2544 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 vga_w(cinfo->regbase, data, blue);
2547 vga_w(cinfo->regbase, data, green);
2548 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 }
2550}
2551
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552#if 0
2553/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555 unsigned char *green, unsigned char *blue)
2556{
2557 unsigned int data = VGA_PEL_D;
2558
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560
2561 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2562 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2563 if (cinfo->btype == BT_PICASSO)
2564 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002565 *red = vga_r(cinfo->regbase, data);
2566 *green = vga_r(cinfo->regbase, data);
2567 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 *blue = vga_r(cinfo->regbase, data);
2570 *green = vga_r(cinfo->regbase, data);
2571 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 }
2573}
2574#endif
2575
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576/*******************************************************************
2577 cirrusfb_WaitBLT()
2578
2579 Wait for the BitBLT engine to complete a possible earlier job
2580*********************************************************************/
2581
2582/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002585 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002586 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587}
2588
2589/*******************************************************************
2590 cirrusfb_BitBLT()
2591
2592 perform accelerated "scrolling"
2593********************************************************************/
2594
Krzysztof Helt8343c892009-03-31 15:25:11 -07002595static void cirrusfb_set_blitter(u8 __iomem *regbase,
2596 u_short nwidth, u_short nheight,
2597 u_long nsrc, u_long ndest,
2598 u_short bltmode, u_short line_length)
2599
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002602 /* dest pitch low */
2603 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2604 /* dest pitch hi */
2605 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2606 /* source pitch low */
2607 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2608 /* source pitch hi */
2609 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610
2611 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 /* BLT width low */
2613 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2614 /* BLT width hi */
2615 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616
2617 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002618 /* BLT height low */
2619 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2620 /* BLT width hi */
2621 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622
2623 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002624 /* BLT dest low */
2625 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2626 /* BLT dest mid */
2627 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2628 /* BLT dest hi */
2629 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630
2631 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632 /* BLT src low */
2633 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2634 /* BLT src mid */
2635 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2636 /* BLT src hi */
2637 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638
2639 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002640 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641
2642 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644
2645 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002646 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002647}
2648
2649/*******************************************************************
2650 cirrusfb_BitBLT()
2651
2652 perform accelerated "scrolling"
2653********************************************************************/
2654
2655static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2656 u_short curx, u_short cury,
2657 u_short destx, u_short desty,
2658 u_short width, u_short height,
2659 u_short line_length)
2660{
2661 u_short nwidth = width - 1;
2662 u_short nheight = height - 1;
2663 u_long nsrc, ndest;
2664 u_char bltmode;
2665
2666 bltmode = 0x00;
2667 /* if source adr < dest addr, do the Blt backwards */
2668 if (cury <= desty) {
2669 if (cury == desty) {
2670 /* if src and dest are on the same line, check x */
2671 if (curx < destx)
2672 bltmode |= 0x01;
2673 } else
2674 bltmode |= 0x01;
2675 }
2676 /* standard case: forward blitting */
2677 nsrc = (cury * line_length) + curx;
2678 ndest = (desty * line_length) + destx;
2679 if (bltmode) {
2680 /* this means start addresses are at the end,
2681 * counting backwards
2682 */
2683 nsrc += nheight * line_length + nwidth;
2684 ndest += nheight * line_length + nwidth;
2685 }
2686
2687 cirrusfb_WaitBLT(regbase);
2688
2689 cirrusfb_set_blitter(regbase, nwidth, nheight,
2690 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002691}
2692
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693/*******************************************************************
2694 cirrusfb_RectFill()
2695
2696 perform accelerated rectangle fill
2697********************************************************************/
2698
Krzysztof Helt8503df62007-10-16 01:29:08 -07002699static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002701 u32 fg_color, u32 bg_color, u_short line_length,
2702 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002704 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 u_char op;
2706
Krzysztof Helt8503df62007-10-16 01:29:08 -07002707 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 /* This is a ColorExpand Blt, using the */
2710 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002711 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2712 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713
Krzysztof Helt9e848062009-03-31 15:25:11 -07002714 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002715 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002716 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2717 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2718 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002719 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002720 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002721 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2722 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002723 op = 0xa0;
2724 }
2725 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002726 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2727 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2728 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002730 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002731 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732}
2733
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734/**************************************************************************
2735 * bestclock() - determine closest possible clock lower(?) than the
2736 * desired pixel clock
2737 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002738static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002740 int n, d;
2741 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742
Krzysztof Helt8503df62007-10-16 01:29:08 -07002743 assert(nom != NULL);
2744 assert(den != NULL);
2745 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
2747 *nom = 0;
2748 *den = 0;
2749 *div = 0;
2750
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751 if (freq < 8000)
2752 freq = 8000;
2753
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002754 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755
2756 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002757 int s = 0;
2758
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002759 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002761 int temp = d;
2762
2763 if (temp > 31) {
2764 s = 1;
2765 temp >>= 1;
2766 }
2767 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002768 h = h > freq ? h - freq : freq - h;
2769 if (h < diff) {
2770 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002772 *den = temp;
2773 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 }
2775 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002776 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002778 if (d > 31) {
2779 s = 1;
2780 d >>= 1;
2781 }
2782 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002783 h = h > freq ? h - freq : freq - h;
2784 if (h < diff) {
2785 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002787 *den = d;
2788 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 }
2790 }
2791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792}
2793
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794/* -------------------------------------------------------------------------
2795 *
2796 * debugging functions
2797 *
2798 * -------------------------------------------------------------------------
2799 */
2800
2801#ifdef CIRRUSFB_DEBUG
2802
2803/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 * cirrusfb_dbg_print_regs
2805 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2806 * @reg_class: type of registers to read: %CRT, or %SEQ
2807 *
2808 * DESCRIPTION:
2809 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2810 * old-style I/O ports are queried for information, otherwise MMIO is
2811 * used at the given @base address to query the information.
2812 */
2813
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002814static void cirrusfb_dbg_print_regs(struct fb_info *info,
2815 caddr_t regbase,
2816 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817{
2818 va_list list;
2819 unsigned char val = 0;
2820 unsigned reg;
2821 char *name;
2822
Krzysztof Helt8503df62007-10-16 01:29:08 -07002823 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824
Krzysztof Helt8503df62007-10-16 01:29:08 -07002825 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002827 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828
2829 switch (reg_class) {
2830 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 break;
2833 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002834 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 break;
2836 default:
2837 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002838 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 break;
2840 }
2841
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002842 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843
Krzysztof Helt8503df62007-10-16 01:29:08 -07002844 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 }
2846
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848}
2849
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851 * cirrusfb_dbg_reg_dump
2852 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2853 *
2854 * DESCRIPTION:
2855 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2856 * old-style I/O ports are queried for information, otherwise MMIO is
2857 * used at the given @base address to query the information.
2858 */
2859
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002860static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002862 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002864 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 "CR00", 0x00,
2866 "CR01", 0x01,
2867 "CR02", 0x02,
2868 "CR03", 0x03,
2869 "CR04", 0x04,
2870 "CR05", 0x05,
2871 "CR06", 0x06,
2872 "CR07", 0x07,
2873 "CR08", 0x08,
2874 "CR09", 0x09,
2875 "CR0A", 0x0A,
2876 "CR0B", 0x0B,
2877 "CR0C", 0x0C,
2878 "CR0D", 0x0D,
2879 "CR0E", 0x0E,
2880 "CR0F", 0x0F,
2881 "CR10", 0x10,
2882 "CR11", 0x11,
2883 "CR12", 0x12,
2884 "CR13", 0x13,
2885 "CR14", 0x14,
2886 "CR15", 0x15,
2887 "CR16", 0x16,
2888 "CR17", 0x17,
2889 "CR18", 0x18,
2890 "CR22", 0x22,
2891 "CR24", 0x24,
2892 "CR26", 0x26,
2893 "CR2D", 0x2D,
2894 "CR2E", 0x2E,
2895 "CR2F", 0x2F,
2896 "CR30", 0x30,
2897 "CR31", 0x31,
2898 "CR32", 0x32,
2899 "CR33", 0x33,
2900 "CR34", 0x34,
2901 "CR35", 0x35,
2902 "CR36", 0x36,
2903 "CR37", 0x37,
2904 "CR38", 0x38,
2905 "CR39", 0x39,
2906 "CR3A", 0x3A,
2907 "CR3B", 0x3B,
2908 "CR3C", 0x3C,
2909 "CR3D", 0x3D,
2910 "CR3E", 0x3E,
2911 "CR3F", 0x3F,
2912 NULL);
2913
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002914 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002916 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002918 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 "SR00", 0x00,
2920 "SR01", 0x01,
2921 "SR02", 0x02,
2922 "SR03", 0x03,
2923 "SR04", 0x04,
2924 "SR08", 0x08,
2925 "SR09", 0x09,
2926 "SR0A", 0x0A,
2927 "SR0B", 0x0B,
2928 "SR0D", 0x0D,
2929 "SR10", 0x10,
2930 "SR11", 0x11,
2931 "SR12", 0x12,
2932 "SR13", 0x13,
2933 "SR14", 0x14,
2934 "SR15", 0x15,
2935 "SR16", 0x16,
2936 "SR17", 0x17,
2937 "SR18", 0x18,
2938 "SR19", 0x19,
2939 "SR1A", 0x1A,
2940 "SR1B", 0x1B,
2941 "SR1C", 0x1C,
2942 "SR1D", 0x1D,
2943 "SR1E", 0x1E,
2944 "SR1F", 0x1F,
2945 NULL);
2946
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002947 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948}
2949
2950#endif /* CIRRUSFB_DEBUG */
2951