blob: 378d60e01902892884e7b8e0c396e416d6b5f7ea [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/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
105 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700106};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/*
109 * per-board-type information, used for enumerating and abstracting
110 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700111 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 * use direct indexing on this array
113 * NOTE: '__initdata' cannot be used as some of this info
114 * is required at runtime. Maybe separate into an init-only and
115 * a run-time table?
116 */
117static const struct cirrusfb_board_info_rec {
118 char *name; /* ASCII name of chipset */
119 long maxclock[5]; /* maximum video clock */
120 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700121 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
122 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700123 /* construct bit 19 of screen start address */
124 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* initial SR07 value, then for each mode */
127 unsigned char sr07;
128 unsigned char sr07_1bpp;
129 unsigned char sr07_1bpp_mux;
130 unsigned char sr07_8bpp;
131 unsigned char sr07_8bpp_mux;
132
133 unsigned char sr1f; /* SR1F VGA initial register value */
134} cirrusfb_board_info[] = {
135 [BT_SD64] = {
136 .name = "CL SD64",
137 .maxclock = {
138 /* guess */
139 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700140 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700142 .init_sr07 = true,
143 .init_sr1f = true,
144 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .sr07 = 0xF0,
146 .sr07_1bpp = 0xF0,
147 .sr07_8bpp = 0xF1,
148 .sr1f = 0x20
149 },
150 [BT_PICCOLO] = {
151 .name = "CL Piccolo",
152 .maxclock = {
153 /* guess */
154 90000, 90000, 90000, 90000, 90000
155 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700156 .init_sr07 = true,
157 .init_sr1f = true,
158 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 .sr07 = 0x80,
160 .sr07_1bpp = 0x80,
161 .sr07_8bpp = 0x81,
162 .sr1f = 0x22
163 },
164 [BT_PICASSO] = {
165 .name = "CL Picasso",
166 .maxclock = {
167 /* guess */
168 90000, 90000, 90000, 90000, 90000
169 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700170 .init_sr07 = true,
171 .init_sr1f = true,
172 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .sr07 = 0x20,
174 .sr07_1bpp = 0x20,
175 .sr07_8bpp = 0x21,
176 .sr1f = 0x22
177 },
178 [BT_SPECTRUM] = {
179 .name = "CL Spectrum",
180 .maxclock = {
181 /* guess */
182 90000, 90000, 90000, 90000, 90000
183 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700184 .init_sr07 = true,
185 .init_sr1f = true,
186 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 .sr07 = 0x80,
188 .sr07_1bpp = 0x80,
189 .sr07_8bpp = 0x81,
190 .sr1f = 0x22
191 },
192 [BT_PICASSO4] = {
193 .name = "CL Picasso4",
194 .maxclock = {
195 135100, 135100, 85500, 85500, 0
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = false,
199 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x20,
201 .sr07_1bpp = 0x20,
202 .sr07_8bpp = 0x21,
203 .sr1f = 0
204 },
205 [BT_ALPINE] = {
206 .name = "CL Alpine",
207 .maxclock = {
208 /* for the GD5430. GD5446 can do more... */
209 85500, 85500, 50000, 28500, 0
210 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700211 .init_sr07 = true,
212 .init_sr1f = true,
213 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 .sr07 = 0xA0,
215 .sr07_1bpp = 0xA1,
216 .sr07_1bpp_mux = 0xA7,
217 .sr07_8bpp = 0xA1,
218 .sr07_8bpp_mux = 0xA7,
219 .sr1f = 0x1C
220 },
221 [BT_GD5480] = {
222 .name = "CL GD5480",
223 .maxclock = {
224 135100, 200000, 200000, 135100, 135100
225 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700226 .init_sr07 = true,
227 .init_sr1f = true,
228 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 .sr07 = 0x10,
230 .sr07_1bpp = 0x11,
231 .sr07_8bpp = 0x11,
232 .sr1f = 0x1C
233 },
234 [BT_LAGUNA] = {
235 .name = "CL Laguna",
236 .maxclock = {
237 /* guess */
238 135100, 135100, 135100, 135100, 135100,
239 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700240 .init_sr07 = false,
241 .init_sr1f = false,
242 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244};
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#ifdef CONFIG_PCI
247#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000248 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700251 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
252 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
253 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
254 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
255 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
256 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
257 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
258 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
259 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
260 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
261 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 { 0, }
263};
264MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
265#undef CHIP
266#endif /* CONFIG_PCI */
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268#ifdef CONFIG_ZORRO
269static const struct zorro_device_id cirrusfb_zorro_table[] = {
270 {
271 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
272 .driver_data = BT_SD64,
273 }, {
274 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
275 .driver_data = BT_PICCOLO,
276 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700277 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 .driver_data = BT_PICASSO,
279 }, {
280 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
281 .driver_data = BT_SPECTRUM,
282 }, {
283 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
284 .driver_data = BT_PICASSO4,
285 },
286 { 0 }
287};
288
289static const struct {
290 zorro_id id2;
291 unsigned long size;
292} cirrusfb_zorro_table2[] = {
293 [BT_SD64] = {
294 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
295 .size = 0x400000
296 },
297 [BT_PICCOLO] = {
298 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
299 .size = 0x200000
300 },
301 [BT_PICASSO] = {
302 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
303 .size = 0x200000
304 },
305 [BT_SPECTRUM] = {
306 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
307 .size = 0x200000
308 },
309 [BT_PICASSO4] = {
310 .id2 = 0,
311 .size = 0x400000
312 }
313};
314#endif /* CONFIG_ZORRO */
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316struct cirrusfb_regs {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700317 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318};
319
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700321enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700322 CRT,
323 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700324};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700325#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
327/* info about board */
328struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700330 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700331 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 unsigned char SFR; /* Shadow of special function register */
333
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 struct cirrusfb_regs currentmode;
335 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700336 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700338 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339};
340
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700341static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700342static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344/****************************************************************************/
345/**** BEGIN PROTOTYPES ******************************************************/
346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700348static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700350static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351#endif
352
Krzysztof Helt8503df62007-10-16 01:29:08 -0700353static int cirrusfb_open(struct fb_info *info, int user);
354static int cirrusfb_release(struct fb_info *info, int user);
355static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
356 unsigned blue, unsigned transp,
357 struct fb_info *info);
358static int cirrusfb_check_var(struct fb_var_screeninfo *var,
359 struct fb_info *info);
360static int cirrusfb_set_par(struct fb_info *info);
361static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
362 struct fb_info *info);
363static int cirrusfb_blank(int blank_mode, struct fb_info *info);
364static void cirrusfb_fillrect(struct fb_info *info,
365 const struct fb_fillrect *region);
366static void cirrusfb_copyarea(struct fb_info *info,
367 const struct fb_copyarea *area);
368static void cirrusfb_imageblit(struct fb_info *info,
369 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371/* function table of the above functions */
372static struct fb_ops cirrusfb_ops = {
373 .owner = THIS_MODULE,
374 .fb_open = cirrusfb_open,
375 .fb_release = cirrusfb_release,
376 .fb_setcolreg = cirrusfb_setcolreg,
377 .fb_check_var = cirrusfb_check_var,
378 .fb_set_par = cirrusfb_set_par,
379 .fb_pan_display = cirrusfb_pan_display,
380 .fb_blank = cirrusfb_blank,
381 .fb_fillrect = cirrusfb_fillrect,
382 .fb_copyarea = cirrusfb_copyarea,
383 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384};
385
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700387static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700388static void switch_monitor(struct cirrusfb_info *cinfo, int on);
389static void WGen(const struct cirrusfb_info *cinfo,
390 int regnum, unsigned char val);
391static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
392static void AttrOn(const struct cirrusfb_info *cinfo);
393static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
394static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
395static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
396static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
397 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700399static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
400 unsigned char *red, unsigned char *green,
401 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700403static void cirrusfb_WaitBLT(u8 __iomem *regbase);
404static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
405 u_short curx, u_short cury,
406 u_short destx, u_short desty,
407 u_short width, u_short height,
408 u_short line_length);
409static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
410 u_short x, u_short y,
411 u_short width, u_short height,
412 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700414static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700417static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
418static void cirrusfb_dbg_print_regs(struct fb_info *info,
419 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700420 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421#endif /* CIRRUSFB_DEBUG */
422
423/*** END PROTOTYPES ********************************************************/
424/*****************************************************************************/
425/*** BEGIN Interface Used by the World ***************************************/
426
Krzysztof Helt8503df62007-10-16 01:29:08 -0700427static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
429/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700430static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
432 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700433 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 return 0;
435}
436
437/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700438static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439{
440 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700441 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 return 0;
443}
444
445/**** END Interface used by the World *************************************/
446/****************************************************************************/
447/**** BEGIN Hardware specific Routines **************************************/
448
Krzysztof Helt486ff382008-10-15 22:03:42 -0700449/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700450static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700452 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700453 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
Krzysztof Helt486ff382008-10-15 22:03:42 -0700455 /* Read MCLK value */
456 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700457 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700460 * should divide it by to get VCLK
461 */
462
463 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700464 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700465 return 1;
466 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700467 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700468 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 }
470
Krzysztof Helt486ff382008-10-15 22:03:42 -0700471 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472}
473
474static int cirrusfb_check_var(struct fb_var_screeninfo *var,
475 struct fb_info *info)
476{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700477 int yres;
478 /* memory size in pixels */
479 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700482 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 var->red.offset = 0;
484 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700485 var->green = var->red;
486 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 break;
488
489 case 8:
490 var->red.offset = 0;
491 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700492 var->green = var->red;
493 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 break;
495
496 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700497 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 var->red.offset = 2;
499 var->green.offset = -3;
500 var->blue.offset = 8;
501 } else {
502 var->red.offset = 10;
503 var->green.offset = 5;
504 var->blue.offset = 0;
505 }
506 var->red.length = 5;
507 var->green.length = 5;
508 var->blue.length = 5;
509 break;
510
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700512 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 var->red.offset = 8;
514 var->green.offset = 16;
515 var->blue.offset = 24;
516 } else {
517 var->red.offset = 16;
518 var->green.offset = 8;
519 var->blue.offset = 0;
520 }
521 var->red.length = 8;
522 var->green.length = 8;
523 var->blue.length = 8;
524 break;
525
526 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700527 dev_dbg(info->device,
528 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700529 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 /* should never occur */
531 break;
532 }
533
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700534 if (var->xres_virtual < var->xres)
535 var->xres_virtual = var->xres;
536 /* use highest possible virtual resolution */
537 if (var->yres_virtual == -1) {
538 var->yres_virtual = pixels / var->xres_virtual;
539
540 dev_info(info->device,
541 "virtual resolution set to maximum of %dx%d\n",
542 var->xres_virtual, var->yres_virtual);
543 }
544 if (var->yres_virtual < var->yres)
545 var->yres_virtual = var->yres;
546
547 if (var->xres_virtual * var->yres_virtual > pixels) {
548 dev_err(info->device, "mode %dx%dx%d rejected... "
549 "virtual resolution too high to fit into video memory!\n",
550 var->xres_virtual, var->yres_virtual,
551 var->bits_per_pixel);
552 return -EINVAL;
553 }
554
555
556 if (var->xoffset < 0)
557 var->xoffset = 0;
558 if (var->yoffset < 0)
559 var->yoffset = 0;
560
561 /* truncate xoffset and yoffset to maximum if too high */
562 if (var->xoffset > var->xres_virtual - var->xres)
563 var->xoffset = var->xres_virtual - var->xres - 1;
564 if (var->yoffset > var->yres_virtual - var->yres)
565 var->yoffset = var->yres_virtual - var->yres - 1;
566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 var->red.msb_right =
568 var->green.msb_right =
569 var->blue.msb_right =
570 var->transp.offset =
571 var->transp.length =
572 var->transp.msb_right = 0;
573
574 yres = var->yres;
575 if (var->vmode & FB_VMODE_DOUBLE)
576 yres *= 2;
577 else if (var->vmode & FB_VMODE_INTERLACED)
578 yres = (yres + 1) / 2;
579
580 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700581 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700582 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 return -EINVAL;
584 }
585
586 return 0;
587}
588
Krzysztof Helt8503df62007-10-16 01:29:08 -0700589static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700591 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593 long freq;
594 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700595 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Krzysztof Helt8503df62007-10-16 01:29:08 -0700598 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700600 info->fix.line_length = var->xres_virtual / 8;
601 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 break;
603
604 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700605 info->fix.line_length = var->xres_virtual;
606 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 break;
608
609 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700611 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700612 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 break;
614
615 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700616 dev_dbg(info->device,
617 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700618 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 /* should never occur */
620 break;
621 }
622
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700623 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700626 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700628 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
630 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
631 regs->multiplexing = 0;
632
633 /* If the frequency is greater than we can support, we might be able
634 * to use multiplexing for the video mode */
635 if (freq > maxclock) {
636 switch (cinfo->btype) {
637 case BT_ALPINE:
638 case BT_GD5480:
639 regs->multiplexing = 1;
640 break;
641
642 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700643 dev_err(info->device,
644 "Frequency greater than maxclock (%ld kHz)\n",
645 maxclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 return -EINVAL;
647 }
648 }
649#if 0
650 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
651 * the VCLK is double the pixel clock. */
652 switch (var->bits_per_pixel) {
653 case 16:
654 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700655 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700656 /* Xbh has this type of clock for 32-bit */
657 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 break;
659 }
660#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 return 0;
662}
663
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700664static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700666 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700667 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700668
Krzysztof Helt8503df62007-10-16 01:29:08 -0700669 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700670 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
Krzysztof Helt486ff382008-10-15 22:03:42 -0700672 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700673 dev_dbg(info->device, "Set %s as pixclock source.\n",
674 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700675 old1f |= 0x40;
676 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
677 if (div == 2)
678 old1e |= 1;
679
680 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700682 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683}
684
685/*************************************************************************
686 cirrusfb_set_par_foo()
687
688 actually writes the values for a new video mode into the hardware,
689**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700690static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
692 struct cirrusfb_info *cinfo = info->par;
693 struct fb_var_screeninfo *var = &info->var;
694 struct cirrusfb_regs regs;
695 u8 __iomem *regbase = cinfo->regbase;
696 unsigned char tmp;
697 int offset = 0, err;
698 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700699 int hdispend, hsyncstart, hsyncend, htotal;
700 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700701 long freq;
702 int nom, den, div;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700703 unsigned int control, format, threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700705 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700707 dev_dbg(info->device, "pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700709 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700712 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700714 dev_dbg(info->device, "mode change aborted. invalid var.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 return -EINVAL;
716 }
717
718 bi = &cirrusfb_board_info[cinfo->btype];
719
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700720 hsyncstart = var->xres + var->right_margin;
721 hsyncend = hsyncstart + var->hsync_len;
722 htotal = (hsyncend + var->left_margin) / 8 - 5;
723 hdispend = var->xres / 8 - 1;
724 hsyncstart = hsyncstart / 8 + 1;
725 hsyncend = hsyncend / 8 + 1;
726
727 yres = var->yres;
728 vsyncstart = yres + var->lower_margin;
729 vsyncend = vsyncstart + var->vsync_len;
730 vtotal = vsyncend + var->upper_margin;
731 vdispend = yres - 1;
732
733 if (var->vmode & FB_VMODE_DOUBLE) {
734 yres *= 2;
735 vsyncstart *= 2;
736 vsyncend *= 2;
737 vtotal *= 2;
738 } else if (var->vmode & FB_VMODE_INTERLACED) {
739 yres = (yres + 1) / 2;
740 vsyncstart = (vsyncstart + 1) / 2;
741 vsyncend = (vsyncend + 1) / 2;
742 vtotal = (vtotal + 1) / 2;
743 }
744
745 vtotal -= 2;
746 vsyncstart -= 1;
747 vsyncend -= 1;
748
749 if (yres >= 1024) {
750 vtotal /= 2;
751 vsyncstart /= 2;
752 vsyncend /= 2;
753 vdispend /= 2;
754 }
755 if (regs.multiplexing) {
756 htotal /= 2;
757 hsyncstart /= 2;
758 hsyncend /= 2;
759 hdispend /= 2;
760 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700762 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
764 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700765 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700768 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700771 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700772 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
Krzysztof Helt8503df62007-10-16 01:29:08 -0700774 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700775 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700776 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700777 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 tmp = hsyncend % 32;
783 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700785 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700786 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700794 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700796 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700806 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700807 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
809 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700810 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 tmp |= 0x20;
812 if (var->vmode & FB_VMODE_DOUBLE)
813 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700815 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700817 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700818 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700820 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700821 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700823 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700826 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700829 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700830 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700832 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700833 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
835 tmp = 0;
836 if (var->vmode & FB_VMODE_INTERLACED)
837 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700838 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700840 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700842 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700844 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 tmp |= 128;
846
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700847 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700848 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700850 freq = PICOS2KHZ(var->pixclock);
851 bestclock(freq, &nom, &den, &div);
852
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700853 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
854 freq, nom, den, div);
855
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 /* set VCLK0 */
857 /* hardware RefClock: 14.31818 MHz */
858 /* formula: VClk = (OSC * N) / (D * (1+P)) */
859 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
860
Krzysztof Helt486ff382008-10-15 22:03:42 -0700861 if (cinfo->btype == BT_ALPINE) {
862 /* if freq is close to mclk or mclk/2 select mclk
863 * as clock source
864 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700865 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700866 if (divMCLK) {
867 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700868 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700869 }
870 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700871 if (cinfo->btype == BT_LAGUNA) {
872 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
873 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
874 unsigned short tile_control;
875
876 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
877 fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4);
878
879 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
880 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
881 control = fb_readw(cinfo->laguna_mmio + 0x402);
882 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
883 control &= ~0x6800;
884 format = 0;
885 threshold &= 0xffe0;
886 threshold &= 0x3fbf;
887 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700888 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700889 tmp = den << 1;
890 if (div != 0)
891 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700892 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
893 if ((cinfo->btype == BT_SD64) ||
894 (cinfo->btype == BT_ALPINE) ||
895 (cinfo->btype == BT_GD5480))
896 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700898 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
899 /* Laguna chipset has reversed clock registers */
900 if (cinfo->btype == BT_LAGUNA) {
901 vga_wseq(regbase, CL_SEQRE, tmp);
902 vga_wseq(regbase, CL_SEQR1E, nom);
903 } else {
904 vga_wseq(regbase, CL_SEQRB, nom);
905 vga_wseq(regbase, CL_SEQR1B, tmp);
906 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700907 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700909 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700911 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 else
913 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
914 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700915 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916
Krzysztof Helt8503df62007-10-16 01:29:08 -0700917/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
918 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
920 /* don't know if it would hurt to also program this if no interlaced */
921 /* mode is used, but I feel better this way.. :-) */
922 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700923 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700930 /* enable display memory & CRTC I/O address for color mode */
931 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
933 tmp |= 0x40;
934 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
935 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700936 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Krzysztof Helt8503df62007-10-16 01:29:08 -0700938 /* Screen A Preset Row-Scan register */
939 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
940 /* text cursor on and start line */
941 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
942 /* text cursor end line */
943 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 /******************************************************
946 *
947 * 1 bpp
948 *
949 */
950
951 /* programming for different color depths */
952 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700953 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700954 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955
956 /* SR07 */
957 switch (cinfo->btype) {
958 case BT_SD64:
959 case BT_PICCOLO:
960 case BT_PICASSO:
961 case BT_SPECTRUM:
962 case BT_PICASSO4:
963 case BT_ALPINE:
964 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700966 regs.multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 bi->sr07_1bpp_mux : bi->sr07_1bpp);
968 break;
969
970 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700971 vga_wseq(regbase, CL_SEQR7,
972 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 break;
974
975 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700976 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
978 }
979
980 /* Extended Sequencer Mode */
981 switch (cinfo->btype) {
982 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700983 /* setting the SEQRF on SD64 is not necessary
984 * (only during init)
985 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700986 /* MCLK select */
987 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 break;
989
990 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700991 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700992 /* ### ueberall 0x22? */
993 /* ##vorher 1c MCLK select */
994 vga_wseq(regbase, CL_SEQR1F, 0x22);
995 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
996 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 break;
998
999 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* ##vorher 22 MCLK select */
1001 vga_wseq(regbase, CL_SEQR1F, 0x22);
1002 /* ## vorher d0 avoid FIFO underruns..? */
1003 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 break;
1005
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 case BT_PICASSO4:
1007 case BT_ALPINE:
1008 case BT_GD5480:
1009 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 /* do nothing */
1011 break;
1012
1013 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001014 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 break;
1016 }
1017
Krzysztof Helt8503df62007-10-16 01:29:08 -07001018 /* pixel mask: pass-through for first plane */
1019 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001021 /* hidden dac reg: 1280x1024 */
1022 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001024 /* hidden dac: nothing */
1025 WHDR(cinfo, 0);
1026 /* memory mode: odd/even, ext. memory */
1027 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1028 /* plane mask: only write to first plane */
1029 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 offset = var->xres_virtual / 16;
1031 }
1032
1033 /******************************************************
1034 *
1035 * 8 bpp
1036 *
1037 */
1038
1039 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001040 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 switch (cinfo->btype) {
1042 case BT_SD64:
1043 case BT_PICCOLO:
1044 case BT_PICASSO:
1045 case BT_SPECTRUM:
1046 case BT_PICASSO4:
1047 case BT_ALPINE:
1048 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 regs.multiplexing ?
1051 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1052 break;
1053
1054 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001055 vga_wseq(regbase, CL_SEQR7,
1056 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001057 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 break;
1059
1060 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001061 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 break;
1063 }
1064
1065 switch (cinfo->btype) {
1066 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* MCLK select */
1068 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 break;
1070
1071 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001074 /* ### vorher 1c MCLK select */
1075 vga_wseq(regbase, CL_SEQR1F, 0x22);
1076 /* Fast Page-Mode writes */
1077 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 break;
1079
1080 case BT_PICASSO4:
1081#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001082 /* ### INCOMPLETE!! */
1083 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 break;
1087
1088 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 /* We already set SRF and SR1F */
1090 break;
1091
1092 case BT_GD5480:
1093 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 /* do nothing */
1095 break;
1096
1097 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001098 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 break;
1100 }
1101
Krzysztof Helt8503df62007-10-16 01:29:08 -07001102 /* mode register: 256 color mode */
1103 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1104 /* pixel mask: pass-through all planes */
1105 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001107 /* hidden dac reg: 1280x1024 */
1108 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001110 /* hidden dac: nothing */
1111 WHDR(cinfo, 0);
1112 /* memory mode: chain4, ext. memory */
1113 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1114 /* plane mask: enable writing to all 4 planes */
1115 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 offset = var->xres_virtual / 8;
1117 }
1118
1119 /******************************************************
1120 *
1121 * 16 bpp
1122 *
1123 */
1124
1125 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001126 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 switch (cinfo->btype) {
1128 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 /* Extended Sequencer Mode: 256c col. mode */
1130 vga_wseq(regbase, CL_SEQR7, 0xf7);
1131 /* MCLK select */
1132 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 break;
1134
1135 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001136 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 vga_wseq(regbase, CL_SEQR7, 0x87);
1138 /* Fast Page-Mode writes */
1139 vga_wseq(regbase, CL_SEQRF, 0xb0);
1140 /* MCLK select */
1141 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 break;
1143
1144 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001145 vga_wseq(regbase, CL_SEQR7, 0x27);
1146 /* Fast Page-Mode writes */
1147 vga_wseq(regbase, CL_SEQRF, 0xb0);
1148 /* MCLK select */
1149 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 break;
1151
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001153 vga_wseq(regbase, CL_SEQR7, 0x27);
1154/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
1157 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001158 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
1161 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001162 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 /* We already set SRF and SR1F */
1164 break;
1165
1166 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 vga_wseq(regbase, CL_SEQR7,
1168 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001169 control |= 0x2000;
1170 format |= 0x1400;
1171 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 break;
1173
1174 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001175 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177 }
1178
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 /* mode register: 256 color mode */
1180 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1181 /* pixel mask: pass-through all planes */
1182 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185#elif defined(CONFIG_ZORRO)
1186 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001187 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 /* memory mode: chain4, ext. memory */
1190 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1191 /* plane mask: enable writing to all 4 planes */
1192 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193 offset = var->xres_virtual / 4;
1194 }
1195
1196 /******************************************************
1197 *
1198 * 32 bpp
1199 *
1200 */
1201
1202 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001203 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 switch (cinfo->btype) {
1205 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001206 /* Extended Sequencer Mode: 256c col. mode */
1207 vga_wseq(regbase, CL_SEQR7, 0xf9);
1208 /* MCLK select */
1209 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 break;
1211
1212 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001213 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001214 vga_wseq(regbase, CL_SEQR7, 0x85);
1215 /* Fast Page-Mode writes */
1216 vga_wseq(regbase, CL_SEQRF, 0xb0);
1217 /* MCLK select */
1218 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 break;
1220
1221 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001222 vga_wseq(regbase, CL_SEQR7, 0x25);
1223 /* Fast Page-Mode writes */
1224 vga_wseq(regbase, CL_SEQRF, 0xb0);
1225 /* MCLK select */
1226 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 break;
1228
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001230 vga_wseq(regbase, CL_SEQR7, 0x25);
1231/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232 break;
1233
1234 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001235 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 break;
1237
1238 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001239 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 /* We already set SRF and SR1F */
1241 break;
1242
1243 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001244 vga_wseq(regbase, CL_SEQR7,
1245 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001246 control |= 0x6000;
1247 format |= 0x3400;
1248 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 break;
1250
1251 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001252 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 break;
1254 }
1255
Krzysztof Helt8503df62007-10-16 01:29:08 -07001256 /* mode register: 256 color mode */
1257 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1258 /* pixel mask: pass-through all planes */
1259 WGen(cinfo, VGA_PEL_MSK, 0xff);
1260 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1261 WHDR(cinfo, 0xc5);
1262 /* memory mode: chain4, ext. memory */
1263 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1264 /* plane mask: enable writing to all 4 planes */
1265 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 offset = var->xres_virtual / 4;
1267 }
1268
1269 /******************************************************
1270 *
1271 * unknown/unsupported bpp
1272 *
1273 */
1274
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001276 dev_err(info->device,
1277 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Krzysztof Helt8503df62007-10-16 01:29:08 -07001280 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 tmp = 0x22;
1282 if (offset & 0x100)
1283 tmp |= 0x10; /* offset overflow bit */
1284
Krzysztof Helt8503df62007-10-16 01:29:08 -07001285 /* screen start addr #16-18, fastpagemode cycles */
1286 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001288 /* screen start address bit 19 */
1289 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001292 if (cinfo->btype == BT_LAGUNA ||
1293 cinfo->btype == BT_GD5480) {
1294
1295 tmp = 0;
1296 if ((htotal + 5) & 256)
1297 tmp |= 128;
1298 if (hdispend & 256)
1299 tmp |= 64;
1300 if (hsyncstart & 256)
1301 tmp |= 48;
1302 if (vtotal & 1024)
1303 tmp |= 8;
1304 if (vdispend & 1024)
1305 tmp |= 4;
1306 if (vsyncstart & 1024)
1307 tmp |= 3;
1308
1309 vga_wcrt(regbase, CL_CRT1E, tmp);
1310 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1311 }
1312
1313
Krzysztof Helt8503df62007-10-16 01:29:08 -07001314 /* text cursor location high */
1315 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1316 /* text cursor location low */
1317 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1318 /* underline row scanline = at very bottom */
1319 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Krzysztof Helt8503df62007-10-16 01:29:08 -07001321 /* controller mode */
1322 vga_wattr(regbase, VGA_ATC_MODE, 1);
1323 /* overscan (border) color */
1324 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1325 /* color plane enable */
1326 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1327 /* pixel panning */
1328 vga_wattr(regbase, CL_AR33, 0);
1329 /* color select */
1330 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331
1332 /* [ EGS: SetOffset(); ] */
1333 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001334 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335
Krzysztof Helt8503df62007-10-16 01:29:08 -07001336 /* set/reset register */
1337 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1338 /* set/reset enable */
1339 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1340 /* color compare */
1341 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1342 /* data rotate */
1343 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1344 /* read map select */
1345 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1346 /* miscellaneous register */
1347 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1348 /* color don't care */
1349 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1350 /* bit mask */
1351 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
Krzysztof Helt8503df62007-10-16 01:29:08 -07001353 /* graphics cursor attributes: nothing special */
1354 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001356 if (cinfo->btype == BT_LAGUNA) {
1357 /* no tiles */
1358 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1359 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1360 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 /* finally, turn on everything - turn off "FullBandwidth" bit */
1363 /* also, set "DotClock%2" bit where requested */
1364 tmp = 0x01;
1365
1366/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1367 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1368 tmp |= 0x08;
1369*/
1370
Krzysztof Helt8503df62007-10-16 01:29:08 -07001371 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001372 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
1376 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001380 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381#endif
1382
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 return 0;
1384}
1385
1386/* for some reason incomprehensible to me, cirrusfb requires that you write
1387 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001388static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 cirrusfb_set_par_foo(info);
1391 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392}
1393
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1395 unsigned blue, unsigned transp,
1396 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 struct cirrusfb_info *cinfo = info->par;
1399
1400 if (regno > 255)
1401 return -EINVAL;
1402
1403 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1404 u32 v;
1405 red >>= (16 - info->var.red.length);
1406 green >>= (16 - info->var.green.length);
1407 blue >>= (16 - info->var.blue.length);
1408
Krzysztof Helt8503df62007-10-16 01:29:08 -07001409 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 return 1;
1411 v = (red << info->var.red.offset) |
1412 (green << info->var.green.offset) |
1413 (blue << info->var.blue.offset);
1414
Krzysztof Helt060b6002007-10-16 01:29:13 -07001415 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 return 0;
1417 }
1418
Krzysztof Helt8503df62007-10-16 01:29:08 -07001419 if (info->var.bits_per_pixel == 8)
1420 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 return 0;
1423
1424}
1425
1426/*************************************************************************
1427 cirrusfb_pan_display()
1428
1429 performs display panning - provided hardware permits this
1430**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1432 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433{
1434 int xoffset = 0;
1435 int yoffset = 0;
1436 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001437 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 struct cirrusfb_info *cinfo = info->par;
1439
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001440 dev_dbg(info->device,
1441 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 /* no range checks for xoffset and yoffset, */
1444 /* as fb_pan_display has already done this */
1445 if (var->vmode & FB_VMODE_YWRAP)
1446 return -EINVAL;
1447
1448 info->var.xoffset = var->xoffset;
1449 info->var.yoffset = var->yoffset;
1450
1451 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1452 yoffset = var->yoffset;
1453
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001454 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
1456 if (info->var.bits_per_pixel == 1) {
1457 /* base is already correct */
1458 xpix = (unsigned char) (var->xoffset % 8);
1459 } else {
1460 base /= 4;
1461 xpix = (unsigned char) ((xoffset % 4) * 2);
1462 }
1463
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
1466 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1468 (unsigned char) (base & 0xff));
1469 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1470 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001472 /* 0xf2 is %11110010, exclude tmp bits */
1473 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 /* construct bits 16, 17 and 18 of screen start address */
1475 if (base & 0x10000)
1476 tmp |= 0x01;
1477 if (base & 0x20000)
1478 tmp |= 0x04;
1479 if (base & 0x40000)
1480 tmp |= 0x08;
1481
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001482 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
1484 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001485 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1486 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Krzysztof Helt8503df62007-10-16 01:29:08 -07001488 /* write pixel panning value to AR33; this does not quite work in 8bpp
1489 *
1490 * ### Piccolo..? Will this work?
1491 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Krzysztof Helt8503df62007-10-16 01:29:08 -07001497 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498}
1499
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501{
1502 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001503 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1504 * then the caller blanks by setting the CLUT (Color Look Up Table)
1505 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1506 * failed due to e.g. a video mode which doesn't support it.
1507 * Implements VESA suspend and powerdown modes on hardware that
1508 * supports disabling hsync/vsync:
1509 * blank_mode == 2: suspend vsync
1510 * blank_mode == 3: suspend hsync
1511 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 */
1513 unsigned char val;
1514 struct cirrusfb_info *cinfo = info->par;
1515 int current_mode = cinfo->blank_mode;
1516
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001517 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
1519 if (info->state != FBINFO_STATE_RUNNING ||
1520 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001521 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 return 0;
1523 }
1524
1525 /* Undo current */
1526 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001527 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001529 val = 0;
1530 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001531 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001532 val = 0x20;
1533
1534 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1535 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537 switch (blank_mode) {
1538 case FB_BLANK_UNBLANK:
1539 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001540 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001543 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 break;
1545 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001546 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 break;
1548 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001549 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001550 break;
1551 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001552 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 return 1;
1554 }
1555
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001556 vga_wgfx(cinfo->regbase, CL_GRE, val);
1557
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001559 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560
1561 /* Let fbcon do a soft blank for us */
1562 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1563}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001564
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565/**** END Hardware specific Routines **************************************/
1566/****************************************************************************/
1567/**** BEGIN Internal Routines ***********************************************/
1568
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001569static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001571 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 const struct cirrusfb_board_info_rec *bi;
1573
Krzysztof Helt8503df62007-10-16 01:29:08 -07001574 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575
1576 bi = &cirrusfb_board_info[cinfo->btype];
1577
1578 /* reset board globally */
1579 switch (cinfo->btype) {
1580 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 WSFR(cinfo, 0x01);
1582 udelay(500);
1583 WSFR(cinfo, 0x51);
1584 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 break;
1586 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 WSFR2(cinfo, 0xff);
1588 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 break;
1590 case BT_SD64:
1591 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001592 WSFR(cinfo, 0x1f);
1593 udelay(500);
1594 WSFR(cinfo, 0x4f);
1595 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596 break;
1597 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001598 /* disable flickerfixer */
1599 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1600 mdelay(100);
1601 /* from Klaus' NetBSD driver: */
1602 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1603 /* put blitter into 542x compat */
1604 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1605 /* mode */
1606 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 break;
1608
1609 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001610 /* from Klaus' NetBSD driver: */
1611 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 break;
1613
1614 case BT_ALPINE:
1615 /* Nothing to do to reset the board. */
1616 break;
1617
1618 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001619 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 break;
1621 }
1622
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001623 /* make sure RAM size set by this point */
1624 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625
1626 /* the P4 is not fully initialized here; I rely on it having been */
1627 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001628 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
1630 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1632 WGen(cinfo, CL_POS102, 0x01);
1633 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
1635 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001636 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 /* reset sequencer logic */
1639 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* FullBandwidth (video off) and 8/9 dot clock */
1642 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1643 /* polarity (-/-), disable access to display memory,
1644 * VGA_CRTC_START_HI base address: color
1645 */
1646 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* "magic cookie" - doesn't make any sense to me.. */
1649/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1650 /* unlock all extension registers */
1651 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 /* reset blitter */
1654 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655
1656 switch (cinfo->btype) {
1657 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 break;
1660 case BT_ALPINE:
1661 break;
1662 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001663 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 break;
1665 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001666 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1667 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 break;
1669 }
1670 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001671 /* plane mask: nothing */
1672 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1673 /* character map select: doesn't even matter in gx mode */
1674 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1675 /* memory mode: chain-4, no odd/even, ext. memory */
1676 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
1678 /* controller-internal base address of video memory */
1679 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681
Krzysztof Helt8503df62007-10-16 01:29:08 -07001682 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1683 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Krzysztof Helt8503df62007-10-16 01:29:08 -07001685 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1686 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1687 /* graphics cursor Y position (..."... ) */
1688 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1689 /* graphics cursor attributes */
1690 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1691 /* graphics cursor pattern address */
1692 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 /* writing these on a P4 might give problems.. */
1695 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696 /* configuration readback and ext. color */
1697 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1698 /* signature generator */
1699 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 }
1701
1702 /* MCLK select etc. */
1703 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001704 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 /* Screen A preset row scan: none */
1707 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1708 /* Text cursor start: disable text cursor */
1709 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1710 /* Text cursor end: - */
1711 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1712 /* Screen start address high: 0 */
1713 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1714 /* Screen start address low: 0 */
1715 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1716 /* text cursor location high: 0 */
1717 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1718 /* text cursor location low: 0 */
1719 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 /* Underline Row scanline: - */
1722 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1723 /* mode control: timing enable, byte mode, no compat modes */
1724 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1725 /* Line Compare: not needed */
1726 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001728 /* ext. display controls: ext.adr. wrap */
1729 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730
Krzysztof Helt8503df62007-10-16 01:29:08 -07001731 /* Set/Reset registes: - */
1732 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1733 /* Set/Reset enable: - */
1734 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1735 /* Color Compare: - */
1736 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1737 /* Data Rotate: - */
1738 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1739 /* Read Map Select: - */
1740 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1741 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1742 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1743 /* Miscellaneous: memory map base address, graphics mode */
1744 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1745 /* Color Don't care: involve all planes */
1746 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1747 /* Bit Mask: no mask at all */
1748 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 /* (5434 can't have bit 3 set for bitblt) */
1751 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001753 /* Graphics controller mode extensions: finer granularity,
1754 * 8byte data latches
1755 */
1756 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1759 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1760 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1761 /* Background color byte 1: - */
1762 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1763 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764
Krzysztof Helt8503df62007-10-16 01:29:08 -07001765 /* Attribute Controller palette registers: "identity mapping" */
1766 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1767 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1768 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1769 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1770 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1771 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1772 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1773 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1774 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1775 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1776 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1777 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1778 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1779 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1780 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1781 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782
Krzysztof Helt8503df62007-10-16 01:29:08 -07001783 /* Attribute Controller mode: graphics mode */
1784 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1785 /* Overscan color reg.: reg. 0 */
1786 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1787 /* Color Plane enable: Enable all 4 planes */
1788 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1789/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1790 /* Color Select: - */
1791 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
Krzysztof Helt8503df62007-10-16 01:29:08 -07001793 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
1795 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001796 /* polarity (-/-), enable display mem,
1797 * VGA_CRTC_START_HI i/o base = color
1798 */
1799 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
Krzysztof Helt8503df62007-10-16 01:29:08 -07001801 /* BLT Start/status: Blitter reset */
1802 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1803 /* - " - : "end-of-reset" */
1804 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
1806 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001807 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 return;
1809}
1810
Krzysztof Helt8503df62007-10-16 01:29:08 -07001811static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812{
1813#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1814 static int IsOn = 0; /* XXX not ok for multiple boards */
1815
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 if (cinfo->btype == BT_PICASSO4)
1817 return; /* nothing to switch */
1818 if (cinfo->btype == BT_ALPINE)
1819 return; /* nothing to switch */
1820 if (cinfo->btype == BT_GD5480)
1821 return; /* nothing to switch */
1822 if (cinfo->btype == BT_PICASSO) {
1823 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001824 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825 return;
1826 }
1827 if (on) {
1828 switch (cinfo->btype) {
1829 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831 break;
1832 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001833 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 break;
1835 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001836 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 break;
1838 default: /* do nothing */ break;
1839 }
1840 } else {
1841 switch (cinfo->btype) {
1842 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844 break;
1845 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001846 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 break;
1848 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001849 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001851 default: /* do nothing */
1852 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 }
1854 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855#endif /* CONFIG_ZORRO */
1856}
1857
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858/******************************************/
1859/* Linux 2.6-style accelerated functions */
1860/******************************************/
1861
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862static void cirrusfb_fillrect(struct fb_info *info,
1863 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 struct fb_fillrect modded;
1866 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001867 struct cirrusfb_info *cinfo = info->par;
1868 int m = info->var.bits_per_pixel;
1869 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1870 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
1872 if (info->state != FBINFO_STATE_RUNNING)
1873 return;
1874 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1875 cfb_fillrect(info, region);
1876 return;
1877 }
1878
1879 vxres = info->var.xres_virtual;
1880 vyres = info->var.yres_virtual;
1881
1882 memcpy(&modded, region, sizeof(struct fb_fillrect));
1883
Krzysztof Helt8503df62007-10-16 01:29:08 -07001884 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 modded.dx >= vxres || modded.dy >= vyres)
1886 return;
1887
Krzysztof Helt8503df62007-10-16 01:29:08 -07001888 if (modded.dx + modded.width > vxres)
1889 modded.width = vxres - modded.dx;
1890 if (modded.dy + modded.height > vyres)
1891 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892
Krzysztof Helt060b6002007-10-16 01:29:13 -07001893 cirrusfb_RectFill(cinfo->regbase,
1894 info->var.bits_per_pixel,
1895 (region->dx * m) / 8, region->dy,
1896 (region->width * m) / 8, region->height,
1897 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001898 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899}
1900
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901static void cirrusfb_copyarea(struct fb_info *info,
1902 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 struct fb_copyarea modded;
1905 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001906 struct cirrusfb_info *cinfo = info->par;
1907 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
1909 if (info->state != FBINFO_STATE_RUNNING)
1910 return;
1911 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1912 cfb_copyarea(info, area);
1913 return;
1914 }
1915
1916 vxres = info->var.xres_virtual;
1917 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001918 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
Krzysztof Helt8503df62007-10-16 01:29:08 -07001920 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 modded.sx >= vxres || modded.sy >= vyres ||
1922 modded.dx >= vxres || modded.dy >= vyres)
1923 return;
1924
Krzysztof Helt8503df62007-10-16 01:29:08 -07001925 if (modded.sx + modded.width > vxres)
1926 modded.width = vxres - modded.sx;
1927 if (modded.dx + modded.width > vxres)
1928 modded.width = vxres - modded.dx;
1929 if (modded.sy + modded.height > vyres)
1930 modded.height = vyres - modded.sy;
1931 if (modded.dy + modded.height > vyres)
1932 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Krzysztof Helt060b6002007-10-16 01:29:13 -07001934 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1935 (area->sx * m) / 8, area->sy,
1936 (area->dx * m) / 8, area->dy,
1937 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001938 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001939
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940}
1941
Krzysztof Helt8503df62007-10-16 01:29:08 -07001942static void cirrusfb_imageblit(struct fb_info *info,
1943 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
1945 struct cirrusfb_info *cinfo = info->par;
1946
Krzysztof Helt8503df62007-10-16 01:29:08 -07001947 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 cfb_imageblit(info, image);
1949}
1950
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951#ifdef CONFIG_PPC_PREP
1952#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1953#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001954static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 *display = PREP_VIDEO_BASE;
1957 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958}
1959
1960#endif /* CONFIG_PPC_PREP */
1961
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001963static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964
1965/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1966 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1967 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1968 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001969static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1970 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971{
1972 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001973 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001975 if (cinfo->btype == BT_LAGUNA) {
1976 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1977
1978 mem = ((SR14 & 7) + 1) << 20;
1979 } else {
1980 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1981 switch ((SRF & 0x18)) {
1982 case 0x08:
1983 mem = 512 * 1024;
1984 break;
1985 case 0x10:
1986 mem = 1024 * 1024;
1987 break;
1988 /* 64-bit DRAM data bus width; assume 2MB.
1989 * Also indicates 2MB memory on the 5430.
1990 */
1991 case 0x18:
1992 mem = 2048 * 1024;
1993 break;
1994 default:
1995 dev_warn(info->device, "Unknown memory size!\n");
1996 mem = 1024 * 1024;
1997 }
1998 /* If DRAM bank switching is enabled, there must be
1999 * twice as much memory installed. (4MB on the 5434)
2000 */
2001 if (SRF & 0x80)
2002 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002004
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 return mem;
2007}
2008
Krzysztof Helt8503df62007-10-16 01:29:08 -07002009static void get_pci_addrs(const struct pci_dev *pdev,
2010 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002012 assert(pdev != NULL);
2013 assert(display != NULL);
2014 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 *display = 0;
2017 *registers = 0;
2018
2019 /* This is a best-guess for now */
2020
2021 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2022 *display = pci_resource_start(pdev, 1);
2023 *registers = pci_resource_start(pdev, 0);
2024 } else {
2025 *display = pci_resource_start(pdev, 0);
2026 *registers = pci_resource_start(pdev, 1);
2027 }
2028
Krzysztof Helt8503df62007-10-16 01:29:08 -07002029 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030}
2031
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002032static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002034 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002035 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002037 if (cinfo->laguna_mmio == NULL)
2038 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002039 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040#if 0 /* if system didn't claim this region, we would... */
2041 release_mem_region(0xA0000, 65535);
2042#endif
2043 if (release_io_ports)
2044 release_region(0x3C0, 32);
2045 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046}
2047#endif /* CONFIG_PCI */
2048
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00002050static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Al Virod91f5bb2007-10-17 00:27:18 +01002052 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002053 struct zorro_dev *zdev = to_zorro_dev(info->device);
2054
2055 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
2057 if (cinfo->btype == BT_PICASSO4) {
2058 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002059 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002060 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002062 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002063 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065}
2066#endif /* CONFIG_ZORRO */
2067
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002068static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002070 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 struct fb_var_screeninfo *var = &info->var;
2072
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 info->pseudo_palette = cinfo->pseudo_palette;
2074 info->flags = FBINFO_DEFAULT
2075 | FBINFO_HWACCEL_XPAN
2076 | FBINFO_HWACCEL_YPAN
2077 | FBINFO_HWACCEL_FILLRECT
2078 | FBINFO_HWACCEL_COPYAREA;
2079 if (noaccel)
2080 info->flags |= FBINFO_HWACCEL_DISABLED;
2081 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 if (cinfo->btype == BT_GD5480) {
2083 if (var->bits_per_pixel == 16)
2084 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002085 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 info->screen_base += 2 * MB_;
2087 }
2088
2089 /* Fill fix common fields */
2090 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2091 sizeof(info->fix.id));
2092
2093 /* monochrome: only 1 memory plane */
2094 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002095 info->fix.smem_len = info->screen_size;
2096 if (var->bits_per_pixel == 1)
2097 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 info->fix.xpanstep = 1;
2100 info->fix.ypanstep = 1;
2101 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102
2103 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 info->fix.mmio_len = 0;
2105 info->fix.accel = FB_ACCEL_NONE;
2106
2107 fb_alloc_cmap(&info->cmap, 256, 0);
2108
2109 return 0;
2110}
2111
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002112static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002114 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002116 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 btype = cinfo->btype;
2119
2120 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002121 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002123 /* set all the vital stuff */
2124 cirrusfb_set_fbinfo(info);
2125
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002126 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002128 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2129 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002130 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002131 err = -EINVAL;
2132 goto err_dealloc_cmap;
2133 }
2134
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 info->var.activate = FB_ACTIVATE_NOW;
2136
2137 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2138 if (err < 0) {
2139 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002140 dev_dbg(info->device,
2141 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002142 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 }
2144
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 err = register_framebuffer(info);
2146 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_err(info->device,
2148 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 goto err_dealloc_cmap;
2150 }
2151
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 return 0;
2153
2154err_dealloc_cmap:
2155 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002156 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002157 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 return err;
2159}
2160
Krzysztof Helt8503df62007-10-16 01:29:08 -07002161static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162{
2163 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164
Krzysztof Helt8503df62007-10-16 01:29:08 -07002165 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002166 unregister_framebuffer(info);
2167 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002168 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002169 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002170 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171}
2172
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002174static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2175 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176{
2177 struct cirrusfb_info *cinfo;
2178 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002179 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 unsigned long board_addr, board_size;
2181 int ret;
2182
2183 ret = pci_enable_device(pdev);
2184 if (ret < 0) {
2185 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2186 goto err_out;
2187 }
2188
2189 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2190 if (!info) {
2191 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2192 ret = -ENOMEM;
2193 goto err_disable;
2194 }
2195
2196 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002197 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002199 dev_dbg(info->device,
2200 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
2201 (unsigned long long)pdev->resource[0].start, btype);
2202 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2203 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204
Krzysztof Helt8503df62007-10-16 01:29:08 -07002205 if (isPReP) {
2206 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002208 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002210 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002211 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002213 dev_dbg(info->device,
2214 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002215 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002216 /* FIXME: this forces VGA. alternatives? */
2217 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002218 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219 }
2220
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002221 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002222 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223
2224 board_size = (btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002225 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
2227 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002228 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002229 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2230 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 goto err_release_fb;
2232 }
2233#if 0 /* if the system didn't claim this region, we would... */
2234 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002235 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2236 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 ret = -EBUSY;
2238 goto err_release_regions;
2239 }
2240#endif
2241 if (request_region(0x3C0, 32, "cirrusfb"))
2242 release_io_ports = 1;
2243
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002244 info->screen_base = ioremap(board_addr, board_size);
2245 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 ret = -EIO;
2247 goto err_release_legacy;
2248 }
2249
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002250 info->fix.smem_start = board_addr;
2251 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 cinfo->unmap = cirrusfb_pci_unmap;
2253
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002254 dev_info(info->device,
2255 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2256 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 pci_set_drvdata(pdev, info);
2258
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002259 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002260 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002261 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002262 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
2264err_release_legacy:
2265 if (release_io_ports)
2266 release_region(0x3C0, 32);
2267#if 0
2268 release_mem_region(0xA0000, 65535);
2269err_release_regions:
2270#endif
2271 pci_release_regions(pdev);
2272err_release_fb:
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002273 if (cinfo->laguna_mmio == NULL)
2274 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 framebuffer_release(info);
2276err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277err_out:
2278 return ret;
2279}
2280
Krzysztof Helt8503df62007-10-16 01:29:08 -07002281static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282{
2283 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284
Krzysztof Helt8503df62007-10-16 01:29:08 -07002285 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286}
2287
2288static struct pci_driver cirrusfb_pci_driver = {
2289 .name = "cirrusfb",
2290 .id_table = cirrusfb_pci_table,
2291 .probe = cirrusfb_pci_register,
2292 .remove = __devexit_p(cirrusfb_pci_unregister),
2293#ifdef CONFIG_PM
2294#if 0
2295 .suspend = cirrusfb_pci_suspend,
2296 .resume = cirrusfb_pci_resume,
2297#endif
2298#endif
2299};
2300#endif /* CONFIG_PCI */
2301
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002303static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2304 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305{
2306 struct cirrusfb_info *cinfo;
2307 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002308 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 struct zorro_dev *z2 = NULL;
2310 unsigned long board_addr, board_size, size;
2311 int ret;
2312
2313 btype = ent->driver_data;
2314 if (cirrusfb_zorro_table2[btype].id2)
2315 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2316 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317
2318 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2319 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002320 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 ret = -ENOMEM;
2322 goto err_out;
2323 }
2324
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002325 dev_info(info->device, "%s board detected\n",
2326 cirrusfb_board_info[btype].name);
2327
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329 cinfo->btype = btype;
2330
Al Viro36ea96a2007-10-27 19:46:58 +01002331 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002332 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334 board_addr = zorro_resource_start(z);
2335 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002336 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337
2338 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002339 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2340 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341 ret = -EBUSY;
2342 goto err_release_fb;
2343 }
2344
Linus Torvalds1da177e2005-04-16 15:20:36 -07002345 ret = -EIO;
2346
2347 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002348 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
2350 /* To be precise, for the P4 this is not the */
2351 /* begin of the board, but the begin of RAM. */
2352 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2353 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002354 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 if (!cinfo->regbase)
2356 goto err_release_region;
2357
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002358 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002359 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002361 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002363 info->fix.smem_start = board_addr + 16777216;
2364 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2365 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 goto err_unmap_regbase;
2367 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002368 dev_info(info->device, " REG at $%lx\n",
2369 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002371 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002373 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002375 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2376 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 goto err_release_region;
2378
2379 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002380 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002381 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002383 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002384 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 }
2386 cinfo->unmap = cirrusfb_zorro_unmap;
2387
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002388 dev_info(info->device,
2389 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2390 board_size / MB_, board_addr);
2391
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392 zorro_set_drvdata(z, info);
2393
Al Virod91f5bb2007-10-17 00:27:18 +01002394 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002395 if (ret) {
2396 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002397 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002398 iounmap(cinfo->regbase - 0x600000);
2399 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002400 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002401 }
2402 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403
2404err_unmap_regbase:
2405 /* Parental advisory: explicit hack */
2406 iounmap(cinfo->regbase - 0x600000);
2407err_release_region:
2408 release_region(board_addr, board_size);
2409err_release_fb:
2410 framebuffer_release(info);
2411err_out:
2412 return ret;
2413}
2414
2415void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2416{
2417 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418
Krzysztof Helt8503df62007-10-16 01:29:08 -07002419 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420}
2421
2422static struct zorro_driver cirrusfb_zorro_driver = {
2423 .name = "cirrusfb",
2424 .id_table = cirrusfb_zorro_table,
2425 .probe = cirrusfb_zorro_register,
2426 .remove = __devexit_p(cirrusfb_zorro_unregister),
2427};
2428#endif /* CONFIG_ZORRO */
2429
2430static int __init cirrusfb_init(void)
2431{
2432 int error = 0;
2433
2434#ifndef MODULE
2435 char *option = NULL;
2436
2437 if (fb_get_options("cirrusfb", &option))
2438 return -ENODEV;
2439 cirrusfb_setup(option);
2440#endif
2441
2442#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002443 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444#endif
2445#ifdef CONFIG_PCI
2446 error |= pci_register_driver(&cirrusfb_pci_driver);
2447#endif
2448 return error;
2449}
2450
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002452static int __init cirrusfb_setup(char *options)
2453{
Vlada Pericee119402008-11-19 15:36:45 -08002454 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 if (!options || !*options)
2457 return 0;
2458
Krzysztof Helt8503df62007-10-16 01:29:08 -07002459 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002460 if (!*this_opt)
2461 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 if (!strcmp(this_opt, "noaccel"))
2464 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002465 else if (!strncmp(this_opt, "mode:", 5))
2466 mode_option = this_opt + 5;
2467 else
2468 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 }
2470 return 0;
2471}
2472#endif
2473
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 /*
2475 * Modularization
2476 */
2477
2478MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2479MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2480MODULE_LICENSE("GPL");
2481
Krzysztof Helt8503df62007-10-16 01:29:08 -07002482static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483{
2484#ifdef CONFIG_PCI
2485 pci_unregister_driver(&cirrusfb_pci_driver);
2486#endif
2487#ifdef CONFIG_ZORRO
2488 zorro_unregister_driver(&cirrusfb_zorro_driver);
2489#endif
2490}
2491
2492module_init(cirrusfb_init);
2493
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002494module_param(mode_option, charp, 0);
2495MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002496module_param(noaccel, bool, 0);
2497MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002498
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499#ifdef MODULE
2500module_exit(cirrusfb_exit);
2501#endif
2502
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503/**********************************************************************/
2504/* about the following functions - I have used the same names for the */
2505/* functions as Markus Wild did in his Retina driver for NetBSD as */
2506/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002507/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508/**********************************************************************/
2509
2510/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512 int regnum, unsigned char val)
2513{
2514 unsigned long regofs = 0;
2515
2516 if (cinfo->btype == BT_PICASSO) {
2517 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002518/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2519 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2521 regofs = 0xfff;
2522 }
2523
Krzysztof Helt8503df62007-10-16 01:29:08 -07002524 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525}
2526
2527/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002528static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529{
2530 unsigned long regofs = 0;
2531
2532 if (cinfo->btype == BT_PICASSO) {
2533 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2535 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2537 regofs = 0xfff;
2538 }
2539
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541}
2542
2543/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 /* if we're just in "write value" mode, write back the */
2550 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002551 vga_w(cinfo->regbase, VGA_ATT_IW,
2552 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 }
2554 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2556 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557
2558 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560}
2561
2562/*** WHDR() - write into the Hidden DAC register ***/
2563/* as the HDR is the only extension register that requires special treatment
2564 * (the other extension registers are accessible just like the "ordinary"
2565 * registers of their functional group) here is a specialized routine for
2566 * accessing the HDR
2567 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569{
2570 unsigned char dummy;
2571
2572 if (cinfo->btype == BT_PICASSO) {
2573 /* Klaus' hint for correct access to HDR on some boards */
2574 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575 WGen(cinfo, VGA_PEL_MSK, 0x00);
2576 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 dummy = RGen(cinfo, VGA_PEL_IW);
2579 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 }
2581 /* now do the usual stuff to access the HDR */
2582
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 dummy = RGen(cinfo, VGA_PEL_MSK);
2584 udelay(200);
2585 dummy = RGen(cinfo, VGA_PEL_MSK);
2586 udelay(200);
2587 dummy = RGen(cinfo, VGA_PEL_MSK);
2588 udelay(200);
2589 dummy = RGen(cinfo, VGA_PEL_MSK);
2590 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592 WGen(cinfo, VGA_PEL_MSK, val);
2593 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594
2595 if (cinfo->btype == BT_PICASSO) {
2596 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002597 dummy = RGen(cinfo, VGA_PEL_IW);
2598 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599
2600 /* and at the end, restore the mask value */
2601 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002602 WGen(cinfo, VGA_PEL_MSK, 0xff);
2603 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 }
2605}
2606
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609{
2610#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002611 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002613 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614#endif
2615}
2616
2617/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002618static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619{
2620#ifdef CONFIG_ZORRO
2621 /* writing an arbitrary value to this one causes the monitor switcher */
2622 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626#endif
2627}
2628
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002631 unsigned char green, unsigned char blue)
2632{
2633 unsigned int data = VGA_PEL_D;
2634
2635 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002636 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2639 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2640 /* but DAC data register IS, at least for Picasso II */
2641 if (cinfo->btype == BT_PICASSO)
2642 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643 vga_w(cinfo->regbase, data, red);
2644 vga_w(cinfo->regbase, data, green);
2645 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002647 vga_w(cinfo->regbase, data, blue);
2648 vga_w(cinfo->regbase, data, green);
2649 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 }
2651}
2652
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653#if 0
2654/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002655static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 unsigned char *green, unsigned char *blue)
2657{
2658 unsigned int data = VGA_PEL_D;
2659
Krzysztof Helt8503df62007-10-16 01:29:08 -07002660 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2663 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2664 if (cinfo->btype == BT_PICASSO)
2665 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002666 *red = vga_r(cinfo->regbase, data);
2667 *green = vga_r(cinfo->regbase, data);
2668 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670 *blue = vga_r(cinfo->regbase, data);
2671 *green = vga_r(cinfo->regbase, data);
2672 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673 }
2674}
2675#endif
2676
Linus Torvalds1da177e2005-04-16 15:20:36 -07002677/*******************************************************************
2678 cirrusfb_WaitBLT()
2679
2680 Wait for the BitBLT engine to complete a possible earlier job
2681*********************************************************************/
2682
2683/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002684static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002685{
2686 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002687 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 /* do nothing */ ;
2689}
2690
2691/*******************************************************************
2692 cirrusfb_BitBLT()
2693
2694 perform accelerated "scrolling"
2695********************************************************************/
2696
Krzysztof Helt8503df62007-10-16 01:29:08 -07002697static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2698 u_short curx, u_short cury,
2699 u_short destx, u_short desty,
2700 u_short width, u_short height,
2701 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702{
2703 u_short nwidth, nheight;
2704 u_long nsrc, ndest;
2705 u_char bltmode;
2706
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 nwidth = width - 1;
2708 nheight = height - 1;
2709
2710 bltmode = 0x00;
2711 /* if source adr < dest addr, do the Blt backwards */
2712 if (cury <= desty) {
2713 if (cury == desty) {
2714 /* if src and dest are on the same line, check x */
2715 if (curx < destx)
2716 bltmode |= 0x01;
2717 } else
2718 bltmode |= 0x01;
2719 }
2720 if (!bltmode) {
2721 /* standard case: forward blitting */
2722 nsrc = (cury * line_length) + curx;
2723 ndest = (desty * line_length) + destx;
2724 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002725 /* this means start addresses are at the end,
2726 * counting backwards
2727 */
2728 nsrc = cury * line_length + curx +
2729 nheight * line_length + nwidth;
2730 ndest = desty * line_length + destx +
2731 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 }
2733
2734 /*
2735 run-down of registers to be programmed:
2736 destination pitch
2737 source pitch
2738 BLT width/height
2739 source start
2740 destination start
2741 BLT mode
2742 BLT ROP
2743 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2744 start/stop
2745 */
2746
Krzysztof Helt8503df62007-10-16 01:29:08 -07002747 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748
2749 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002750 /* dest pitch low */
2751 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2752 /* dest pitch hi */
2753 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2754 /* source pitch low */
2755 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2756 /* source pitch hi */
2757 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758
2759 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 /* BLT width low */
2761 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2762 /* BLT width hi */
2763 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764
2765 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002766 /* BLT height low */
2767 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2768 /* BLT width hi */
2769 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770
2771 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002772 /* BLT dest low */
2773 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2774 /* BLT dest mid */
2775 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2776 /* BLT dest hi */
2777 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778
2779 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002780 /* BLT src low */
2781 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2782 /* BLT src mid */
2783 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2784 /* BLT src hi */
2785 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786
2787 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002788 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789
2790 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002791 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792
2793 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002794 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795}
2796
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797/*******************************************************************
2798 cirrusfb_RectFill()
2799
2800 perform accelerated rectangle fill
2801********************************************************************/
2802
Krzysztof Helt8503df62007-10-16 01:29:08 -07002803static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 u_short x, u_short y, u_short width, u_short height,
2805 u_char color, u_short line_length)
2806{
2807 u_short nwidth, nheight;
2808 u_long ndest;
2809 u_char op;
2810
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 nwidth = width - 1;
2812 nheight = height - 1;
2813
2814 ndest = (y * line_length) + x;
2815
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817
2818 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002819 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2820 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2821 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2822 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823
2824 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002825 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2826 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827
2828 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002829 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2830 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831
2832 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002833 /* BLT dest low */
2834 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2835 /* BLT dest mid */
2836 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2837 /* BLT dest hi */
2838 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839
2840 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002841 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2842 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2843 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
2845 /* This is a ColorExpand Blt, using the */
2846 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2848 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849
2850 op = 0xc0;
2851 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002852 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2853 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 op = 0x50;
2855 op = 0xd0;
2856 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2858 vga_wgfx(regbase, CL_GR11, color); /* background color */
2859 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2860 vga_wgfx(regbase, CL_GR13, color); /* background color */
2861 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2862 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 op = 0x50;
2864 op = 0xf0;
2865 }
2866 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868
2869 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871
2872 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002873 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874}
2875
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876/**************************************************************************
2877 * bestclock() - determine closest possible clock lower(?) than the
2878 * desired pixel clock
2879 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002880static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002882 int n, d;
2883 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884
Krzysztof Helt8503df62007-10-16 01:29:08 -07002885 assert(nom != NULL);
2886 assert(den != NULL);
2887 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
2889 *nom = 0;
2890 *den = 0;
2891 *div = 0;
2892
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 if (freq < 8000)
2894 freq = 8000;
2895
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002896 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897
2898 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002899 int s = 0;
2900
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002901 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002903 int temp = d;
2904
2905 if (temp > 31) {
2906 s = 1;
2907 temp >>= 1;
2908 }
2909 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002910 h = h > freq ? h - freq : freq - h;
2911 if (h < diff) {
2912 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002914 *den = temp;
2915 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916 }
2917 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002918 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002920 if (d > 31) {
2921 s = 1;
2922 d >>= 1;
2923 }
2924 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002925 h = h > freq ? h - freq : freq - h;
2926 if (h < diff) {
2927 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002929 *den = d;
2930 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931 }
2932 }
2933 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934}
2935
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936/* -------------------------------------------------------------------------
2937 *
2938 * debugging functions
2939 *
2940 * -------------------------------------------------------------------------
2941 */
2942
2943#ifdef CIRRUSFB_DEBUG
2944
2945/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 * cirrusfb_dbg_print_regs
2947 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2948 * @reg_class: type of registers to read: %CRT, or %SEQ
2949 *
2950 * DESCRIPTION:
2951 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2952 * old-style I/O ports are queried for information, otherwise MMIO is
2953 * used at the given @base address to query the information.
2954 */
2955
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002956static void cirrusfb_dbg_print_regs(struct fb_info *info,
2957 caddr_t regbase,
2958 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002959{
2960 va_list list;
2961 unsigned char val = 0;
2962 unsigned reg;
2963 char *name;
2964
Krzysztof Helt8503df62007-10-16 01:29:08 -07002965 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966
Krzysztof Helt8503df62007-10-16 01:29:08 -07002967 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002969 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970
2971 switch (reg_class) {
2972 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002973 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002974 break;
2975 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002976 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977 break;
2978 default:
2979 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002980 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981 break;
2982 }
2983
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002984 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985
Krzysztof Helt8503df62007-10-16 01:29:08 -07002986 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002987 }
2988
Krzysztof Helt8503df62007-10-16 01:29:08 -07002989 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990}
2991
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002993 * cirrusfb_dbg_reg_dump
2994 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2995 *
2996 * DESCRIPTION:
2997 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2998 * old-style I/O ports are queried for information, otherwise MMIO is
2999 * used at the given @base address to query the information.
3000 */
3001
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003002static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003003{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003004 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003006 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007 "CR00", 0x00,
3008 "CR01", 0x01,
3009 "CR02", 0x02,
3010 "CR03", 0x03,
3011 "CR04", 0x04,
3012 "CR05", 0x05,
3013 "CR06", 0x06,
3014 "CR07", 0x07,
3015 "CR08", 0x08,
3016 "CR09", 0x09,
3017 "CR0A", 0x0A,
3018 "CR0B", 0x0B,
3019 "CR0C", 0x0C,
3020 "CR0D", 0x0D,
3021 "CR0E", 0x0E,
3022 "CR0F", 0x0F,
3023 "CR10", 0x10,
3024 "CR11", 0x11,
3025 "CR12", 0x12,
3026 "CR13", 0x13,
3027 "CR14", 0x14,
3028 "CR15", 0x15,
3029 "CR16", 0x16,
3030 "CR17", 0x17,
3031 "CR18", 0x18,
3032 "CR22", 0x22,
3033 "CR24", 0x24,
3034 "CR26", 0x26,
3035 "CR2D", 0x2D,
3036 "CR2E", 0x2E,
3037 "CR2F", 0x2F,
3038 "CR30", 0x30,
3039 "CR31", 0x31,
3040 "CR32", 0x32,
3041 "CR33", 0x33,
3042 "CR34", 0x34,
3043 "CR35", 0x35,
3044 "CR36", 0x36,
3045 "CR37", 0x37,
3046 "CR38", 0x38,
3047 "CR39", 0x39,
3048 "CR3A", 0x3A,
3049 "CR3B", 0x3B,
3050 "CR3C", 0x3C,
3051 "CR3D", 0x3D,
3052 "CR3E", 0x3E,
3053 "CR3F", 0x3F,
3054 NULL);
3055
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003056 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003058 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003060 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 "SR00", 0x00,
3062 "SR01", 0x01,
3063 "SR02", 0x02,
3064 "SR03", 0x03,
3065 "SR04", 0x04,
3066 "SR08", 0x08,
3067 "SR09", 0x09,
3068 "SR0A", 0x0A,
3069 "SR0B", 0x0B,
3070 "SR0D", 0x0D,
3071 "SR10", 0x10,
3072 "SR11", 0x11,
3073 "SR12", 0x12,
3074 "SR13", 0x13,
3075 "SR14", 0x14,
3076 "SR15", 0x15,
3077 "SR16", 0x16,
3078 "SR17", 0x17,
3079 "SR18", 0x18,
3080 "SR19", 0x19,
3081 "SR1A", 0x1A,
3082 "SR1B", 0x1B,
3083 "SR1C", 0x1C,
3084 "SR1D", 0x1D,
3085 "SR1E", 0x1E,
3086 "SR1F", 0x1F,
3087 NULL);
3088
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003089 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090}
3091
3092#endif /* CIRRUSFB_DEBUG */
3093