blob: 53572c0f3474cb2df1cb169ecd65b30249a225ee [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;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700697 int err;
698 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700700 int hdispend, hsyncstart, hsyncend, htotal;
701 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700702 long freq;
703 int nom, den, div;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700704 unsigned int control, format, threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700706 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700708 dev_dbg(info->device, "pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700710 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700713 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700715 dev_dbg(info->device, "mode change aborted. invalid var.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 return -EINVAL;
717 }
718
719 bi = &cirrusfb_board_info[cinfo->btype];
720
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700721 hsyncstart = var->xres + var->right_margin;
722 hsyncend = hsyncstart + var->hsync_len;
723 htotal = (hsyncend + var->left_margin) / 8 - 5;
724 hdispend = var->xres / 8 - 1;
725 hsyncstart = hsyncstart / 8 + 1;
726 hsyncend = hsyncend / 8 + 1;
727
728 yres = var->yres;
729 vsyncstart = yres + var->lower_margin;
730 vsyncend = vsyncstart + var->vsync_len;
731 vtotal = vsyncend + var->upper_margin;
732 vdispend = yres - 1;
733
734 if (var->vmode & FB_VMODE_DOUBLE) {
735 yres *= 2;
736 vsyncstart *= 2;
737 vsyncend *= 2;
738 vtotal *= 2;
739 } else if (var->vmode & FB_VMODE_INTERLACED) {
740 yres = (yres + 1) / 2;
741 vsyncstart = (vsyncstart + 1) / 2;
742 vsyncend = (vsyncend + 1) / 2;
743 vtotal = (vtotal + 1) / 2;
744 }
745
746 vtotal -= 2;
747 vsyncstart -= 1;
748 vsyncend -= 1;
749
750 if (yres >= 1024) {
751 vtotal /= 2;
752 vsyncstart /= 2;
753 vsyncend /= 2;
754 vdispend /= 2;
755 }
756 if (regs.multiplexing) {
757 htotal /= 2;
758 hsyncstart /= 2;
759 hsyncend /= 2;
760 hdispend /= 2;
761 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700763 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700769 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700770 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700772 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700773 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
Krzysztof Helt8503df62007-10-16 01:29:08 -0700775 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700776 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700777 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700780 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 tmp = hsyncend % 32;
784 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700787 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700789 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700793 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700797 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700799 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700801 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700807 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700808 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
810 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700811 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 tmp |= 0x20;
813 if (var->vmode & FB_VMODE_DOUBLE)
814 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700816 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700819 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700821 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700824 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700825 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700827 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700830 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700833 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
836 tmp = 0;
837 if (var->vmode & FB_VMODE_INTERLACED)
838 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700839 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700841 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700843 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700845 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 tmp |= 128;
847
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700848 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700849 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700851 freq = PICOS2KHZ(var->pixclock);
852 bestclock(freq, &nom, &den, &div);
853
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700854 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
855 freq, nom, den, div);
856
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 /* set VCLK0 */
858 /* hardware RefClock: 14.31818 MHz */
859 /* formula: VClk = (OSC * N) / (D * (1+P)) */
860 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
861
Krzysztof Helt486ff382008-10-15 22:03:42 -0700862 if (cinfo->btype == BT_ALPINE) {
863 /* if freq is close to mclk or mclk/2 select mclk
864 * as clock source
865 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700866 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700867 if (divMCLK) {
868 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700869 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700870 }
871 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700872 if (cinfo->btype == BT_LAGUNA) {
873 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
874 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
875 unsigned short tile_control;
876
877 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
878 fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4);
879
880 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
881 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
882 control = fb_readw(cinfo->laguna_mmio + 0x402);
883 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
884 control &= ~0x6800;
885 format = 0;
886 threshold &= 0xffe0;
887 threshold &= 0x3fbf;
888 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700889 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700890 tmp = den << 1;
891 if (div != 0)
892 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700893 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
894 if ((cinfo->btype == BT_SD64) ||
895 (cinfo->btype == BT_ALPINE) ||
896 (cinfo->btype == BT_GD5480))
897 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700899 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
900 /* Laguna chipset has reversed clock registers */
901 if (cinfo->btype == BT_LAGUNA) {
902 vga_wseq(regbase, CL_SEQRE, tmp);
903 vga_wseq(regbase, CL_SEQR1E, nom);
904 } else {
905 vga_wseq(regbase, CL_SEQRB, nom);
906 vga_wseq(regbase, CL_SEQR1B, tmp);
907 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700908 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700910 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 else
914 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
915 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
919 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 /* don't know if it would hurt to also program this if no interlaced */
922 /* mode is used, but I feel better this way.. :-) */
923 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700924 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700926 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927
Krzysztof Helt8503df62007-10-16 01:29:08 -0700928 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
930 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 /* enable display memory & CRTC I/O address for color mode */
932 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
934 tmp |= 0x40;
935 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
936 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700937 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 /* Screen A Preset Row-Scan register */
940 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
941 /* text cursor on and start line */
942 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
943 /* text cursor end line */
944 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945
946 /******************************************************
947 *
948 * 1 bpp
949 *
950 */
951
952 /* programming for different color depths */
953 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700954 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700955 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
957 /* SR07 */
958 switch (cinfo->btype) {
959 case BT_SD64:
960 case BT_PICCOLO:
961 case BT_PICASSO:
962 case BT_SPECTRUM:
963 case BT_PICASSO4:
964 case BT_ALPINE:
965 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700966 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700967 regs.multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 bi->sr07_1bpp_mux : bi->sr07_1bpp);
969 break;
970
971 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700972 vga_wseq(regbase, CL_SEQR7,
973 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 break;
975
976 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700977 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 break;
979 }
980
981 /* Extended Sequencer Mode */
982 switch (cinfo->btype) {
983 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 /* setting the SEQRF on SD64 is not necessary
985 * (only during init)
986 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700987 /* MCLK select */
988 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 break;
990
991 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700992 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700993 /* ### ueberall 0x22? */
994 /* ##vorher 1c MCLK select */
995 vga_wseq(regbase, CL_SEQR1F, 0x22);
996 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
997 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 break;
999
1000 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001001 /* ##vorher 22 MCLK select */
1002 vga_wseq(regbase, CL_SEQR1F, 0x22);
1003 /* ## vorher d0 avoid FIFO underruns..? */
1004 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 break;
1006
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 case BT_PICASSO4:
1008 case BT_ALPINE:
1009 case BT_GD5480:
1010 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 /* do nothing */
1012 break;
1013
1014 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001015 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 break;
1017 }
1018
Krzysztof Helt8503df62007-10-16 01:29:08 -07001019 /* pixel mask: pass-through for first plane */
1020 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001022 /* hidden dac reg: 1280x1024 */
1023 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001025 /* hidden dac: nothing */
1026 WHDR(cinfo, 0);
1027 /* memory mode: odd/even, ext. memory */
1028 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1029 /* plane mask: only write to first plane */
1030 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
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 }
1117
1118 /******************************************************
1119 *
1120 * 16 bpp
1121 *
1122 */
1123
1124 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001125 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 switch (cinfo->btype) {
1127 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001128 /* Extended Sequencer Mode: 256c col. mode */
1129 vga_wseq(regbase, CL_SEQR7, 0xf7);
1130 /* MCLK select */
1131 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 break;
1133
1134 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001135 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001136 vga_wseq(regbase, CL_SEQR7, 0x87);
1137 /* Fast Page-Mode writes */
1138 vga_wseq(regbase, CL_SEQRF, 0xb0);
1139 /* MCLK select */
1140 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 break;
1142
1143 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001144 vga_wseq(regbase, CL_SEQR7, 0x27);
1145 /* Fast Page-Mode writes */
1146 vga_wseq(regbase, CL_SEQRF, 0xb0);
1147 /* MCLK select */
1148 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 break;
1150
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001152 vga_wseq(regbase, CL_SEQR7, 0x27);
1153/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 break;
1155
1156 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001157 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 break;
1159
1160 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001161 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 /* We already set SRF and SR1F */
1163 break;
1164
1165 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001166 vga_wseq(regbase, CL_SEQR7,
1167 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001168 control |= 0x2000;
1169 format |= 0x1400;
1170 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 break;
1172
1173 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001174 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 break;
1176 }
1177
Krzysztof Helt8503df62007-10-16 01:29:08 -07001178 /* mode register: 256 color mode */
1179 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1180 /* pixel mask: pass-through all planes */
1181 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001183 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184#elif defined(CONFIG_ZORRO)
1185 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 /* memory mode: chain4, ext. memory */
1189 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1190 /* plane mask: enable writing to all 4 planes */
1191 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 }
1193
1194 /******************************************************
1195 *
1196 * 32 bpp
1197 *
1198 */
1199
1200 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001201 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 switch (cinfo->btype) {
1203 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001204 /* Extended Sequencer Mode: 256c col. mode */
1205 vga_wseq(regbase, CL_SEQR7, 0xf9);
1206 /* MCLK select */
1207 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 break;
1209
1210 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001211 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001212 vga_wseq(regbase, CL_SEQR7, 0x85);
1213 /* Fast Page-Mode writes */
1214 vga_wseq(regbase, CL_SEQRF, 0xb0);
1215 /* MCLK select */
1216 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 break;
1218
1219 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001220 vga_wseq(regbase, CL_SEQR7, 0x25);
1221 /* Fast Page-Mode writes */
1222 vga_wseq(regbase, CL_SEQRF, 0xb0);
1223 /* MCLK select */
1224 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 break;
1226
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001228 vga_wseq(regbase, CL_SEQR7, 0x25);
1229/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 break;
1231
1232 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001233 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 break;
1235
1236 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001237 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 /* We already set SRF and SR1F */
1239 break;
1240
1241 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001242 vga_wseq(regbase, CL_SEQR7,
1243 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001244 control |= 0x6000;
1245 format |= 0x3400;
1246 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 break;
1248
1249 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001250 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 break;
1252 }
1253
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 /* mode register: 256 color mode */
1255 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1256 /* pixel mask: pass-through all planes */
1257 WGen(cinfo, VGA_PEL_MSK, 0xff);
1258 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1259 WHDR(cinfo, 0xc5);
1260 /* memory mode: chain4, ext. memory */
1261 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1262 /* plane mask: enable writing to all 4 planes */
1263 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 }
1265
1266 /******************************************************
1267 *
1268 * unknown/unsupported bpp
1269 *
1270 */
1271
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001273 dev_err(info->device,
1274 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276
Krzysztof Helt6683e012009-03-31 15:25:06 -07001277 pitch = info->fix.line_length >> 3;
1278 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001280 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 tmp |= 0x10; /* offset overflow bit */
1282
Krzysztof Helt8503df62007-10-16 01:29:08 -07001283 /* screen start addr #16-18, fastpagemode cycles */
1284 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001286 /* screen start address bit 19 */
1287 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001288 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001290 if (cinfo->btype == BT_LAGUNA ||
1291 cinfo->btype == BT_GD5480) {
1292
1293 tmp = 0;
1294 if ((htotal + 5) & 256)
1295 tmp |= 128;
1296 if (hdispend & 256)
1297 tmp |= 64;
1298 if (hsyncstart & 256)
1299 tmp |= 48;
1300 if (vtotal & 1024)
1301 tmp |= 8;
1302 if (vdispend & 1024)
1303 tmp |= 4;
1304 if (vsyncstart & 1024)
1305 tmp |= 3;
1306
1307 vga_wcrt(regbase, CL_CRT1E, tmp);
1308 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1309 }
1310
1311
Krzysztof Helt8503df62007-10-16 01:29:08 -07001312 /* text cursor location high */
1313 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1314 /* text cursor location low */
1315 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1316 /* underline row scanline = at very bottom */
1317 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
Krzysztof Helt8503df62007-10-16 01:29:08 -07001319 /* controller mode */
1320 vga_wattr(regbase, VGA_ATC_MODE, 1);
1321 /* overscan (border) color */
1322 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1323 /* color plane enable */
1324 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1325 /* pixel panning */
1326 vga_wattr(regbase, CL_AR33, 0);
1327 /* color select */
1328 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
1330 /* [ EGS: SetOffset(); ] */
1331 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333
Krzysztof Helt8503df62007-10-16 01:29:08 -07001334 /* set/reset register */
1335 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1336 /* set/reset enable */
1337 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1338 /* color compare */
1339 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1340 /* data rotate */
1341 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1342 /* read map select */
1343 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1344 /* miscellaneous register */
1345 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1346 /* color don't care */
1347 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1348 /* bit mask */
1349 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Krzysztof Helt8503df62007-10-16 01:29:08 -07001351 /* graphics cursor attributes: nothing special */
1352 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001354 if (cinfo->btype == BT_LAGUNA) {
1355 /* no tiles */
1356 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1357 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1358 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1359 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360 /* finally, turn on everything - turn off "FullBandwidth" bit */
1361 /* also, set "DotClock%2" bit where requested */
1362 tmp = 0x01;
1363
1364/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1365 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1366 tmp |= 0x08;
1367*/
1368
Krzysztof Helt8503df62007-10-16 01:29:08 -07001369 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001370 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
1372 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
1377#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001378 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379#endif
1380
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381 return 0;
1382}
1383
1384/* for some reason incomprehensible to me, cirrusfb requires that you write
1385 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001386static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001388 cirrusfb_set_par_foo(info);
1389 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390}
1391
Krzysztof Helt8503df62007-10-16 01:29:08 -07001392static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1393 unsigned blue, unsigned transp,
1394 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395{
1396 struct cirrusfb_info *cinfo = info->par;
1397
1398 if (regno > 255)
1399 return -EINVAL;
1400
1401 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1402 u32 v;
1403 red >>= (16 - info->var.red.length);
1404 green >>= (16 - info->var.green.length);
1405 blue >>= (16 - info->var.blue.length);
1406
Krzysztof Helt8503df62007-10-16 01:29:08 -07001407 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 return 1;
1409 v = (red << info->var.red.offset) |
1410 (green << info->var.green.offset) |
1411 (blue << info->var.blue.offset);
1412
Krzysztof Helt060b6002007-10-16 01:29:13 -07001413 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 return 0;
1415 }
1416
Krzysztof Helt8503df62007-10-16 01:29:08 -07001417 if (info->var.bits_per_pixel == 8)
1418 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
1420 return 0;
1421
1422}
1423
1424/*************************************************************************
1425 cirrusfb_pan_display()
1426
1427 performs display panning - provided hardware permits this
1428**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001429static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1430 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431{
1432 int xoffset = 0;
1433 int yoffset = 0;
1434 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001435 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 struct cirrusfb_info *cinfo = info->par;
1437
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001438 dev_dbg(info->device,
1439 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
1441 /* no range checks for xoffset and yoffset, */
1442 /* as fb_pan_display has already done this */
1443 if (var->vmode & FB_VMODE_YWRAP)
1444 return -EINVAL;
1445
1446 info->var.xoffset = var->xoffset;
1447 info->var.yoffset = var->yoffset;
1448
1449 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1450 yoffset = var->yoffset;
1451
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001452 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453
1454 if (info->var.bits_per_pixel == 1) {
1455 /* base is already correct */
1456 xpix = (unsigned char) (var->xoffset % 8);
1457 } else {
1458 base /= 4;
1459 xpix = (unsigned char) ((xoffset % 4) * 2);
1460 }
1461
Krzysztof Helt8503df62007-10-16 01:29:08 -07001462 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
1464 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001465 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1466 (unsigned char) (base & 0xff));
1467 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1468 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001470 /* 0xf2 is %11110010, exclude tmp bits */
1471 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472 /* construct bits 16, 17 and 18 of screen start address */
1473 if (base & 0x10000)
1474 tmp |= 0x01;
1475 if (base & 0x20000)
1476 tmp |= 0x04;
1477 if (base & 0x40000)
1478 tmp |= 0x08;
1479
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001480 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001483 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1484 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001485
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 /* write pixel panning value to AR33; this does not quite work in 8bpp
1487 *
1488 * ### Piccolo..? Will this work?
1489 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001491 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496}
1497
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499{
1500 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1502 * then the caller blanks by setting the CLUT (Color Look Up Table)
1503 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1504 * failed due to e.g. a video mode which doesn't support it.
1505 * Implements VESA suspend and powerdown modes on hardware that
1506 * supports disabling hsync/vsync:
1507 * blank_mode == 2: suspend vsync
1508 * blank_mode == 3: suspend hsync
1509 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 */
1511 unsigned char val;
1512 struct cirrusfb_info *cinfo = info->par;
1513 int current_mode = cinfo->blank_mode;
1514
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001515 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
1517 if (info->state != FBINFO_STATE_RUNNING ||
1518 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001519 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 return 0;
1521 }
1522
1523 /* Undo current */
1524 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001525 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001526 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001527 val = 0;
1528 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001529 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001530 val = 0x20;
1531
1532 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1533 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
1535 switch (blank_mode) {
1536 case FB_BLANK_UNBLANK:
1537 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001538 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 break;
1540 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001541 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 break;
1543 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001544 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 break;
1546 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001547 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001550 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 return 1;
1552 }
1553
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001554 vga_wgfx(cinfo->regbase, CL_GRE, val);
1555
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001557 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559 /* Let fbcon do a soft blank for us */
1560 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1561}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001562
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563/**** END Hardware specific Routines **************************************/
1564/****************************************************************************/
1565/**** BEGIN Internal Routines ***********************************************/
1566
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001567static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001569 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 const struct cirrusfb_board_info_rec *bi;
1571
Krzysztof Helt8503df62007-10-16 01:29:08 -07001572 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573
1574 bi = &cirrusfb_board_info[cinfo->btype];
1575
1576 /* reset board globally */
1577 switch (cinfo->btype) {
1578 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001579 WSFR(cinfo, 0x01);
1580 udelay(500);
1581 WSFR(cinfo, 0x51);
1582 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 break;
1584 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 WSFR2(cinfo, 0xff);
1586 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 break;
1588 case BT_SD64:
1589 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 WSFR(cinfo, 0x1f);
1591 udelay(500);
1592 WSFR(cinfo, 0x4f);
1593 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 break;
1595 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001596 /* disable flickerfixer */
1597 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1598 mdelay(100);
1599 /* from Klaus' NetBSD driver: */
1600 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1601 /* put blitter into 542x compat */
1602 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1603 /* mode */
1604 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 break;
1606
1607 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* from Klaus' NetBSD driver: */
1609 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 break;
1611
1612 case BT_ALPINE:
1613 /* Nothing to do to reset the board. */
1614 break;
1615
1616 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001617 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 break;
1619 }
1620
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001621 /* make sure RAM size set by this point */
1622 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623
1624 /* the P4 is not fully initialized here; I rely on it having been */
1625 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001626 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
1628 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001629 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1630 WGen(cinfo, CL_POS102, 0x01);
1631 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
1633 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
Krzysztof Helt8503df62007-10-16 01:29:08 -07001636 /* reset sequencer logic */
1637 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
Krzysztof Helt8503df62007-10-16 01:29:08 -07001639 /* FullBandwidth (video off) and 8/9 dot clock */
1640 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1641 /* polarity (-/-), disable access to display memory,
1642 * VGA_CRTC_START_HI base address: color
1643 */
1644 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Krzysztof Helt8503df62007-10-16 01:29:08 -07001646 /* "magic cookie" - doesn't make any sense to me.. */
1647/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1648 /* unlock all extension registers */
1649 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 /* reset blitter */
1652 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
1654 switch (cinfo->btype) {
1655 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 break;
1658 case BT_ALPINE:
1659 break;
1660 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001661 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 break;
1663 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1665 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 break;
1667 }
1668 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001669 /* plane mask: nothing */
1670 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1671 /* character map select: doesn't even matter in gx mode */
1672 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1673 /* memory mode: chain-4, no odd/even, ext. memory */
1674 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675
1676 /* controller-internal base address of video memory */
1677 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1681 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682
Krzysztof Helt8503df62007-10-16 01:29:08 -07001683 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1684 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1685 /* graphics cursor Y position (..."... ) */
1686 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1687 /* graphics cursor attributes */
1688 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1689 /* graphics cursor pattern address */
1690 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
1692 /* writing these on a P4 might give problems.. */
1693 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 /* configuration readback and ext. color */
1695 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1696 /* signature generator */
1697 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 }
1699
1700 /* MCLK select etc. */
1701 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703
Krzysztof Helt8503df62007-10-16 01:29:08 -07001704 /* Screen A preset row scan: none */
1705 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1706 /* Text cursor start: disable text cursor */
1707 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1708 /* Text cursor end: - */
1709 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1710 /* Screen start address high: 0 */
1711 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1712 /* Screen start address low: 0 */
1713 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1714 /* text cursor location high: 0 */
1715 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1716 /* text cursor location low: 0 */
1717 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 /* Underline Row scanline: - */
1720 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1721 /* mode control: timing enable, byte mode, no compat modes */
1722 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1723 /* Line Compare: not needed */
1724 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001726 /* ext. display controls: ext.adr. wrap */
1727 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728
Krzysztof Helt8503df62007-10-16 01:29:08 -07001729 /* Set/Reset registes: - */
1730 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1731 /* Set/Reset enable: - */
1732 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1733 /* Color Compare: - */
1734 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1735 /* Data Rotate: - */
1736 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1737 /* Read Map Select: - */
1738 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1739 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1740 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1741 /* Miscellaneous: memory map base address, graphics mode */
1742 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1743 /* Color Don't care: involve all planes */
1744 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1745 /* Bit Mask: no mask at all */
1746 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001748 /* (5434 can't have bit 3 set for bitblt) */
1749 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 /* Graphics controller mode extensions: finer granularity,
1752 * 8byte data latches
1753 */
1754 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755
Krzysztof Helt8503df62007-10-16 01:29:08 -07001756 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1757 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1758 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1759 /* Background color byte 1: - */
1760 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1761 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763 /* Attribute Controller palette registers: "identity mapping" */
1764 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1765 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1766 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1767 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1768 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1769 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1770 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1771 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1772 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1773 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1774 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1775 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1776 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1777 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1778 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1779 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Krzysztof Helt8503df62007-10-16 01:29:08 -07001781 /* Attribute Controller mode: graphics mode */
1782 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1783 /* Overscan color reg.: reg. 0 */
1784 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1785 /* Color Plane enable: Enable all 4 planes */
1786 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1787/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1788 /* Color Select: - */
1789 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790
Krzysztof Helt8503df62007-10-16 01:29:08 -07001791 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001794 /* polarity (-/-), enable display mem,
1795 * VGA_CRTC_START_HI i/o base = color
1796 */
1797 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Krzysztof Helt8503df62007-10-16 01:29:08 -07001799 /* BLT Start/status: Blitter reset */
1800 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1801 /* - " - : "end-of-reset" */
1802 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
1804 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001805 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806 return;
1807}
1808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001810{
1811#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1812 static int IsOn = 0; /* XXX not ok for multiple boards */
1813
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 if (cinfo->btype == BT_PICASSO4)
1815 return; /* nothing to switch */
1816 if (cinfo->btype == BT_ALPINE)
1817 return; /* nothing to switch */
1818 if (cinfo->btype == BT_GD5480)
1819 return; /* nothing to switch */
1820 if (cinfo->btype == BT_PICASSO) {
1821 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 return;
1824 }
1825 if (on) {
1826 switch (cinfo->btype) {
1827 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001828 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829 break;
1830 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001831 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 break;
1833 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001834 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835 break;
1836 default: /* do nothing */ break;
1837 }
1838 } else {
1839 switch (cinfo->btype) {
1840 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001841 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001842 break;
1843 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001844 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 break;
1846 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001847 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001849 default: /* do nothing */
1850 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 }
1852 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853#endif /* CONFIG_ZORRO */
1854}
1855
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856/******************************************/
1857/* Linux 2.6-style accelerated functions */
1858/******************************************/
1859
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static void cirrusfb_fillrect(struct fb_info *info,
1861 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 struct fb_fillrect modded;
1864 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001865 struct cirrusfb_info *cinfo = info->par;
1866 int m = info->var.bits_per_pixel;
1867 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1868 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869
1870 if (info->state != FBINFO_STATE_RUNNING)
1871 return;
1872 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1873 cfb_fillrect(info, region);
1874 return;
1875 }
1876
1877 vxres = info->var.xres_virtual;
1878 vyres = info->var.yres_virtual;
1879
1880 memcpy(&modded, region, sizeof(struct fb_fillrect));
1881
Krzysztof Helt8503df62007-10-16 01:29:08 -07001882 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883 modded.dx >= vxres || modded.dy >= vyres)
1884 return;
1885
Krzysztof Helt8503df62007-10-16 01:29:08 -07001886 if (modded.dx + modded.width > vxres)
1887 modded.width = vxres - modded.dx;
1888 if (modded.dy + modded.height > vyres)
1889 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890
Krzysztof Helt060b6002007-10-16 01:29:13 -07001891 cirrusfb_RectFill(cinfo->regbase,
1892 info->var.bits_per_pixel,
1893 (region->dx * m) / 8, region->dy,
1894 (region->width * m) / 8, region->height,
1895 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001896 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897}
1898
Krzysztof Helt8503df62007-10-16 01:29:08 -07001899static void cirrusfb_copyarea(struct fb_info *info,
1900 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 struct fb_copyarea modded;
1903 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001904 struct cirrusfb_info *cinfo = info->par;
1905 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906
1907 if (info->state != FBINFO_STATE_RUNNING)
1908 return;
1909 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1910 cfb_copyarea(info, area);
1911 return;
1912 }
1913
1914 vxres = info->var.xres_virtual;
1915 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001916 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 modded.sx >= vxres || modded.sy >= vyres ||
1920 modded.dx >= vxres || modded.dy >= vyres)
1921 return;
1922
Krzysztof Helt8503df62007-10-16 01:29:08 -07001923 if (modded.sx + modded.width > vxres)
1924 modded.width = vxres - modded.sx;
1925 if (modded.dx + modded.width > vxres)
1926 modded.width = vxres - modded.dx;
1927 if (modded.sy + modded.height > vyres)
1928 modded.height = vyres - modded.sy;
1929 if (modded.dy + modded.height > vyres)
1930 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931
Krzysztof Helt060b6002007-10-16 01:29:13 -07001932 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1933 (area->sx * m) / 8, area->sy,
1934 (area->dx * m) / 8, area->dy,
1935 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001936 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001937
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938}
1939
Krzysztof Helt8503df62007-10-16 01:29:08 -07001940static void cirrusfb_imageblit(struct fb_info *info,
1941 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942{
1943 struct cirrusfb_info *cinfo = info->par;
1944
Krzysztof Helt8503df62007-10-16 01:29:08 -07001945 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 cfb_imageblit(info, image);
1947}
1948
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949#ifdef CONFIG_PPC_PREP
1950#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1951#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001952static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 *display = PREP_VIDEO_BASE;
1955 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956}
1957
1958#endif /* CONFIG_PPC_PREP */
1959
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001961static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1964 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1965 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1966 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001967static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1968 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969{
1970 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001971 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001973 if (cinfo->btype == BT_LAGUNA) {
1974 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1975
1976 mem = ((SR14 & 7) + 1) << 20;
1977 } else {
1978 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1979 switch ((SRF & 0x18)) {
1980 case 0x08:
1981 mem = 512 * 1024;
1982 break;
1983 case 0x10:
1984 mem = 1024 * 1024;
1985 break;
1986 /* 64-bit DRAM data bus width; assume 2MB.
1987 * Also indicates 2MB memory on the 5430.
1988 */
1989 case 0x18:
1990 mem = 2048 * 1024;
1991 break;
1992 default:
1993 dev_warn(info->device, "Unknown memory size!\n");
1994 mem = 1024 * 1024;
1995 }
1996 /* If DRAM bank switching is enabled, there must be
1997 * twice as much memory installed. (4MB on the 5434)
1998 */
1999 if (SRF & 0x80)
2000 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002002
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 return mem;
2005}
2006
Krzysztof Helt8503df62007-10-16 01:29:08 -07002007static void get_pci_addrs(const struct pci_dev *pdev,
2008 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002010 assert(pdev != NULL);
2011 assert(display != NULL);
2012 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 *display = 0;
2015 *registers = 0;
2016
2017 /* This is a best-guess for now */
2018
2019 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2020 *display = pci_resource_start(pdev, 1);
2021 *registers = pci_resource_start(pdev, 0);
2022 } else {
2023 *display = pci_resource_start(pdev, 0);
2024 *registers = pci_resource_start(pdev, 1);
2025 }
2026
Krzysztof Helt8503df62007-10-16 01:29:08 -07002027 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028}
2029
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002030static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002032 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002033 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002035 if (cinfo->laguna_mmio == NULL)
2036 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002037 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038#if 0 /* if system didn't claim this region, we would... */
2039 release_mem_region(0xA0000, 65535);
2040#endif
2041 if (release_io_ports)
2042 release_region(0x3C0, 32);
2043 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044}
2045#endif /* CONFIG_PCI */
2046
Linus Torvalds1da177e2005-04-16 15:20:36 -07002047#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00002048static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049{
Al Virod91f5bb2007-10-17 00:27:18 +01002050 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002051 struct zorro_dev *zdev = to_zorro_dev(info->device);
2052
2053 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054
2055 if (cinfo->btype == BT_PICASSO4) {
2056 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002057 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002058 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002060 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002061 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063}
2064#endif /* CONFIG_ZORRO */
2065
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002066static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002068 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 struct fb_var_screeninfo *var = &info->var;
2070
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 info->pseudo_palette = cinfo->pseudo_palette;
2072 info->flags = FBINFO_DEFAULT
2073 | FBINFO_HWACCEL_XPAN
2074 | FBINFO_HWACCEL_YPAN
2075 | FBINFO_HWACCEL_FILLRECT
2076 | FBINFO_HWACCEL_COPYAREA;
2077 if (noaccel)
2078 info->flags |= FBINFO_HWACCEL_DISABLED;
2079 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 if (cinfo->btype == BT_GD5480) {
2081 if (var->bits_per_pixel == 16)
2082 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002083 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 info->screen_base += 2 * MB_;
2085 }
2086
2087 /* Fill fix common fields */
2088 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2089 sizeof(info->fix.id));
2090
2091 /* monochrome: only 1 memory plane */
2092 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002093 info->fix.smem_len = info->screen_size;
2094 if (var->bits_per_pixel == 1)
2095 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 info->fix.xpanstep = 1;
2098 info->fix.ypanstep = 1;
2099 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
2101 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 info->fix.mmio_len = 0;
2103 info->fix.accel = FB_ACCEL_NONE;
2104
2105 fb_alloc_cmap(&info->cmap, 256, 0);
2106
2107 return 0;
2108}
2109
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002110static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002112 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002114 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 btype = cinfo->btype;
2117
2118 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002119 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002121 /* set all the vital stuff */
2122 cirrusfb_set_fbinfo(info);
2123
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002124 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002126 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2127 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002128 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002129 err = -EINVAL;
2130 goto err_dealloc_cmap;
2131 }
2132
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 info->var.activate = FB_ACTIVATE_NOW;
2134
2135 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2136 if (err < 0) {
2137 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002138 dev_dbg(info->device,
2139 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002140 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 }
2142
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 err = register_framebuffer(info);
2144 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002145 dev_err(info->device,
2146 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 goto err_dealloc_cmap;
2148 }
2149
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 return 0;
2151
2152err_dealloc_cmap:
2153 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002154 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002155 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002156 return err;
2157}
2158
Krzysztof Helt8503df62007-10-16 01:29:08 -07002159static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160{
2161 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162
Krzysztof Helt8503df62007-10-16 01:29:08 -07002163 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002164 unregister_framebuffer(info);
2165 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002167 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002168 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169}
2170
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002172static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2173 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174{
2175 struct cirrusfb_info *cinfo;
2176 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002177 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 unsigned long board_addr, board_size;
2179 int ret;
2180
2181 ret = pci_enable_device(pdev);
2182 if (ret < 0) {
2183 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2184 goto err_out;
2185 }
2186
2187 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2188 if (!info) {
2189 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2190 ret = -ENOMEM;
2191 goto err_disable;
2192 }
2193
2194 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002195 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002197 dev_dbg(info->device,
2198 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
2199 (unsigned long long)pdev->resource[0].start, btype);
2200 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2201 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202
Krzysztof Helt8503df62007-10-16 01:29:08 -07002203 if (isPReP) {
2204 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002206 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002208 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002209 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002210 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002211 dev_dbg(info->device,
2212 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002213 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002214 /* FIXME: this forces VGA. alternatives? */
2215 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002216 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 }
2218
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002219 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002220 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221
2222 board_size = (btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002223 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224
2225 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002226 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002227 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2228 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 goto err_release_fb;
2230 }
2231#if 0 /* if the system didn't claim this region, we would... */
2232 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002233 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2234 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 ret = -EBUSY;
2236 goto err_release_regions;
2237 }
2238#endif
2239 if (request_region(0x3C0, 32, "cirrusfb"))
2240 release_io_ports = 1;
2241
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002242 info->screen_base = ioremap(board_addr, board_size);
2243 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 ret = -EIO;
2245 goto err_release_legacy;
2246 }
2247
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002248 info->fix.smem_start = board_addr;
2249 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 cinfo->unmap = cirrusfb_pci_unmap;
2251
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002252 dev_info(info->device,
2253 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2254 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 pci_set_drvdata(pdev, info);
2256
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002257 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002258 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002259 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002260 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
2262err_release_legacy:
2263 if (release_io_ports)
2264 release_region(0x3C0, 32);
2265#if 0
2266 release_mem_region(0xA0000, 65535);
2267err_release_regions:
2268#endif
2269 pci_release_regions(pdev);
2270err_release_fb:
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002271 if (cinfo->laguna_mmio == NULL)
2272 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 framebuffer_release(info);
2274err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275err_out:
2276 return ret;
2277}
2278
Krzysztof Helt8503df62007-10-16 01:29:08 -07002279static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280{
2281 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
Krzysztof Helt8503df62007-10-16 01:29:08 -07002283 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284}
2285
2286static struct pci_driver cirrusfb_pci_driver = {
2287 .name = "cirrusfb",
2288 .id_table = cirrusfb_pci_table,
2289 .probe = cirrusfb_pci_register,
2290 .remove = __devexit_p(cirrusfb_pci_unregister),
2291#ifdef CONFIG_PM
2292#if 0
2293 .suspend = cirrusfb_pci_suspend,
2294 .resume = cirrusfb_pci_resume,
2295#endif
2296#endif
2297};
2298#endif /* CONFIG_PCI */
2299
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002301static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2302 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303{
2304 struct cirrusfb_info *cinfo;
2305 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002306 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 struct zorro_dev *z2 = NULL;
2308 unsigned long board_addr, board_size, size;
2309 int ret;
2310
2311 btype = ent->driver_data;
2312 if (cirrusfb_zorro_table2[btype].id2)
2313 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2314 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
2316 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2317 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002318 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 ret = -ENOMEM;
2320 goto err_out;
2321 }
2322
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002323 dev_info(info->device, "%s board detected\n",
2324 cirrusfb_board_info[btype].name);
2325
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327 cinfo->btype = btype;
2328
Al Viro36ea96a2007-10-27 19:46:58 +01002329 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002330 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 board_addr = zorro_resource_start(z);
2333 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002334 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335
2336 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002337 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2338 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339 ret = -EBUSY;
2340 goto err_release_fb;
2341 }
2342
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 ret = -EIO;
2344
2345 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002346 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347
2348 /* To be precise, for the P4 this is not the */
2349 /* begin of the board, but the begin of RAM. */
2350 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2351 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002352 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 if (!cinfo->regbase)
2354 goto err_release_region;
2355
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002356 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002357 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002359 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002361 info->fix.smem_start = board_addr + 16777216;
2362 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2363 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 goto err_unmap_regbase;
2365 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002366 dev_info(info->device, " REG at $%lx\n",
2367 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002368
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002369 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002371 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002373 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2374 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002375 goto err_release_region;
2376
2377 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002378 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002379 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002381 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 }
2384 cinfo->unmap = cirrusfb_zorro_unmap;
2385
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002386 dev_info(info->device,
2387 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2388 board_size / MB_, board_addr);
2389
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 zorro_set_drvdata(z, info);
2391
Al Virod91f5bb2007-10-17 00:27:18 +01002392 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002393 if (ret) {
2394 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002395 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002396 iounmap(cinfo->regbase - 0x600000);
2397 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002398 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002399 }
2400 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401
2402err_unmap_regbase:
2403 /* Parental advisory: explicit hack */
2404 iounmap(cinfo->regbase - 0x600000);
2405err_release_region:
2406 release_region(board_addr, board_size);
2407err_release_fb:
2408 framebuffer_release(info);
2409err_out:
2410 return ret;
2411}
2412
2413void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2414{
2415 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002416
Krzysztof Helt8503df62007-10-16 01:29:08 -07002417 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002418}
2419
2420static struct zorro_driver cirrusfb_zorro_driver = {
2421 .name = "cirrusfb",
2422 .id_table = cirrusfb_zorro_table,
2423 .probe = cirrusfb_zorro_register,
2424 .remove = __devexit_p(cirrusfb_zorro_unregister),
2425};
2426#endif /* CONFIG_ZORRO */
2427
2428static int __init cirrusfb_init(void)
2429{
2430 int error = 0;
2431
2432#ifndef MODULE
2433 char *option = NULL;
2434
2435 if (fb_get_options("cirrusfb", &option))
2436 return -ENODEV;
2437 cirrusfb_setup(option);
2438#endif
2439
2440#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002441 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442#endif
2443#ifdef CONFIG_PCI
2444 error |= pci_register_driver(&cirrusfb_pci_driver);
2445#endif
2446 return error;
2447}
2448
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002450static int __init cirrusfb_setup(char *options)
2451{
Vlada Pericee119402008-11-19 15:36:45 -08002452 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454 if (!options || !*options)
2455 return 0;
2456
Krzysztof Helt8503df62007-10-16 01:29:08 -07002457 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002458 if (!*this_opt)
2459 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 if (!strcmp(this_opt, "noaccel"))
2462 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002463 else if (!strncmp(this_opt, "mode:", 5))
2464 mode_option = this_opt + 5;
2465 else
2466 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 }
2468 return 0;
2469}
2470#endif
2471
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 /*
2473 * Modularization
2474 */
2475
2476MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2477MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2478MODULE_LICENSE("GPL");
2479
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481{
2482#ifdef CONFIG_PCI
2483 pci_unregister_driver(&cirrusfb_pci_driver);
2484#endif
2485#ifdef CONFIG_ZORRO
2486 zorro_unregister_driver(&cirrusfb_zorro_driver);
2487#endif
2488}
2489
2490module_init(cirrusfb_init);
2491
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002492module_param(mode_option, charp, 0);
2493MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002494module_param(noaccel, bool, 0);
2495MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002496
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497#ifdef MODULE
2498module_exit(cirrusfb_exit);
2499#endif
2500
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501/**********************************************************************/
2502/* about the following functions - I have used the same names for the */
2503/* functions as Markus Wild did in his Retina driver for NetBSD as */
2504/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506/**********************************************************************/
2507
2508/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002509static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 int regnum, unsigned char val)
2511{
2512 unsigned long regofs = 0;
2513
2514 if (cinfo->btype == BT_PICASSO) {
2515 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2517 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2519 regofs = 0xfff;
2520 }
2521
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523}
2524
2525/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527{
2528 unsigned long regofs = 0;
2529
2530 if (cinfo->btype == BT_PICASSO) {
2531 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2533 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2535 regofs = 0xfff;
2536 }
2537
Krzysztof Helt8503df62007-10-16 01:29:08 -07002538 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539}
2540
2541/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547 /* if we're just in "write value" mode, write back the */
2548 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 vga_w(cinfo->regbase, VGA_ATT_IW,
2550 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 }
2552 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2554 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
2556 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002557 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558}
2559
2560/*** WHDR() - write into the Hidden DAC register ***/
2561/* as the HDR is the only extension register that requires special treatment
2562 * (the other extension registers are accessible just like the "ordinary"
2563 * registers of their functional group) here is a specialized routine for
2564 * accessing the HDR
2565 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567{
2568 unsigned char dummy;
2569
2570 if (cinfo->btype == BT_PICASSO) {
2571 /* Klaus' hint for correct access to HDR on some boards */
2572 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573 WGen(cinfo, VGA_PEL_MSK, 0x00);
2574 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002576 dummy = RGen(cinfo, VGA_PEL_IW);
2577 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578 }
2579 /* now do the usual stuff to access the HDR */
2580
Krzysztof Helt8503df62007-10-16 01:29:08 -07002581 dummy = RGen(cinfo, VGA_PEL_MSK);
2582 udelay(200);
2583 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 WGen(cinfo, VGA_PEL_MSK, val);
2591 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
2593 if (cinfo->btype == BT_PICASSO) {
2594 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 dummy = RGen(cinfo, VGA_PEL_IW);
2596 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597
2598 /* and at the end, restore the mask value */
2599 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002600 WGen(cinfo, VGA_PEL_MSK, 0xff);
2601 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 }
2603}
2604
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002606static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607{
2608#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002611 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612#endif
2613}
2614
2615/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002616static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617{
2618#ifdef CONFIG_ZORRO
2619 /* writing an arbitrary value to this one causes the monitor switcher */
2620 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002621 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624#endif
2625}
2626
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 unsigned char green, unsigned char blue)
2630{
2631 unsigned int data = VGA_PEL_D;
2632
2633 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002634 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635
2636 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2637 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2638 /* but DAC data register IS, at least for Picasso II */
2639 if (cinfo->btype == BT_PICASSO)
2640 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002641 vga_w(cinfo->regbase, data, red);
2642 vga_w(cinfo->regbase, data, green);
2643 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 vga_w(cinfo->regbase, data, blue);
2646 vga_w(cinfo->regbase, data, green);
2647 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648 }
2649}
2650
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651#if 0
2652/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002653static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654 unsigned char *green, unsigned char *blue)
2655{
2656 unsigned int data = VGA_PEL_D;
2657
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659
2660 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2661 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2662 if (cinfo->btype == BT_PICASSO)
2663 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 *red = vga_r(cinfo->regbase, data);
2665 *green = vga_r(cinfo->regbase, data);
2666 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002668 *blue = vga_r(cinfo->regbase, data);
2669 *green = vga_r(cinfo->regbase, data);
2670 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671 }
2672}
2673#endif
2674
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675/*******************************************************************
2676 cirrusfb_WaitBLT()
2677
2678 Wait for the BitBLT engine to complete a possible earlier job
2679*********************************************************************/
2680
2681/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002682static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683{
2684 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002685 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686 /* do nothing */ ;
2687}
2688
2689/*******************************************************************
2690 cirrusfb_BitBLT()
2691
2692 perform accelerated "scrolling"
2693********************************************************************/
2694
Krzysztof Helt8503df62007-10-16 01:29:08 -07002695static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2696 u_short curx, u_short cury,
2697 u_short destx, u_short desty,
2698 u_short width, u_short height,
2699 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700{
2701 u_short nwidth, nheight;
2702 u_long nsrc, ndest;
2703 u_char bltmode;
2704
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 nwidth = width - 1;
2706 nheight = height - 1;
2707
2708 bltmode = 0x00;
2709 /* if source adr < dest addr, do the Blt backwards */
2710 if (cury <= desty) {
2711 if (cury == desty) {
2712 /* if src and dest are on the same line, check x */
2713 if (curx < destx)
2714 bltmode |= 0x01;
2715 } else
2716 bltmode |= 0x01;
2717 }
2718 if (!bltmode) {
2719 /* standard case: forward blitting */
2720 nsrc = (cury * line_length) + curx;
2721 ndest = (desty * line_length) + destx;
2722 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723 /* this means start addresses are at the end,
2724 * counting backwards
2725 */
2726 nsrc = cury * line_length + curx +
2727 nheight * line_length + nwidth;
2728 ndest = desty * line_length + destx +
2729 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 }
2731
2732 /*
2733 run-down of registers to be programmed:
2734 destination pitch
2735 source pitch
2736 BLT width/height
2737 source start
2738 destination start
2739 BLT mode
2740 BLT ROP
2741 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2742 start/stop
2743 */
2744
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002746
2747 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002748 /* dest pitch low */
2749 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2750 /* dest pitch hi */
2751 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2752 /* source pitch low */
2753 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2754 /* source pitch hi */
2755 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756
2757 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002758 /* BLT width low */
2759 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2760 /* BLT width hi */
2761 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
2763 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002764 /* BLT height low */
2765 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2766 /* BLT width hi */
2767 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768
2769 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002770 /* BLT dest low */
2771 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2772 /* BLT dest mid */
2773 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2774 /* BLT dest hi */
2775 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776
2777 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002778 /* BLT src low */
2779 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2780 /* BLT src mid */
2781 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2782 /* BLT src hi */
2783 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784
2785 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002786 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
2788 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002789 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790
2791 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002792 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793}
2794
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795/*******************************************************************
2796 cirrusfb_RectFill()
2797
2798 perform accelerated rectangle fill
2799********************************************************************/
2800
Krzysztof Helt8503df62007-10-16 01:29:08 -07002801static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 u_short x, u_short y, u_short width, u_short height,
2803 u_char color, u_short line_length)
2804{
2805 u_short nwidth, nheight;
2806 u_long ndest;
2807 u_char op;
2808
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 nwidth = width - 1;
2810 nheight = height - 1;
2811
2812 ndest = (y * line_length) + x;
2813
Krzysztof Helt8503df62007-10-16 01:29:08 -07002814 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002815
2816 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002817 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2818 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2819 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2820 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821
2822 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002823 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2824 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825
2826 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002827 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2828 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829
2830 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 /* BLT dest low */
2832 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2833 /* BLT dest mid */
2834 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2835 /* BLT dest hi */
2836 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837
2838 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002839 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2840 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2841 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842
2843 /* This is a ColorExpand Blt, using the */
2844 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2846 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847
2848 op = 0xc0;
2849 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2851 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 op = 0x50;
2853 op = 0xd0;
2854 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002855 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2856 vga_wgfx(regbase, CL_GR11, color); /* background color */
2857 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2858 vga_wgfx(regbase, CL_GR13, color); /* background color */
2859 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2860 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 op = 0x50;
2862 op = 0xf0;
2863 }
2864 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002865 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866
2867 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869
2870 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002871 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872}
2873
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874/**************************************************************************
2875 * bestclock() - determine closest possible clock lower(?) than the
2876 * desired pixel clock
2877 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002878static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002880 int n, d;
2881 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882
Krzysztof Helt8503df62007-10-16 01:29:08 -07002883 assert(nom != NULL);
2884 assert(den != NULL);
2885 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886
2887 *nom = 0;
2888 *den = 0;
2889 *div = 0;
2890
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 if (freq < 8000)
2892 freq = 8000;
2893
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002894 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895
2896 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002897 int s = 0;
2898
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002899 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002901 int temp = d;
2902
2903 if (temp > 31) {
2904 s = 1;
2905 temp >>= 1;
2906 }
2907 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002908 h = h > freq ? h - freq : freq - h;
2909 if (h < diff) {
2910 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002912 *den = temp;
2913 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 }
2915 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002916 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002918 if (d > 31) {
2919 s = 1;
2920 d >>= 1;
2921 }
2922 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002923 h = h > freq ? h - freq : freq - h;
2924 if (h < diff) {
2925 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002927 *den = d;
2928 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929 }
2930 }
2931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932}
2933
Linus Torvalds1da177e2005-04-16 15:20:36 -07002934/* -------------------------------------------------------------------------
2935 *
2936 * debugging functions
2937 *
2938 * -------------------------------------------------------------------------
2939 */
2940
2941#ifdef CIRRUSFB_DEBUG
2942
2943/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944 * cirrusfb_dbg_print_regs
2945 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2946 * @reg_class: type of registers to read: %CRT, or %SEQ
2947 *
2948 * DESCRIPTION:
2949 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2950 * old-style I/O ports are queried for information, otherwise MMIO is
2951 * used at the given @base address to query the information.
2952 */
2953
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002954static void cirrusfb_dbg_print_regs(struct fb_info *info,
2955 caddr_t regbase,
2956 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957{
2958 va_list list;
2959 unsigned char val = 0;
2960 unsigned reg;
2961 char *name;
2962
Krzysztof Helt8503df62007-10-16 01:29:08 -07002963 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964
Krzysztof Helt8503df62007-10-16 01:29:08 -07002965 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002966 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002967 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968
2969 switch (reg_class) {
2970 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002971 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 break;
2973 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002974 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 break;
2976 default:
2977 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002978 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 break;
2980 }
2981
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002982 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983
Krzysztof Helt8503df62007-10-16 01:29:08 -07002984 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985 }
2986
Krzysztof Helt8503df62007-10-16 01:29:08 -07002987 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002988}
2989
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991 * cirrusfb_dbg_reg_dump
2992 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2993 *
2994 * DESCRIPTION:
2995 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2996 * old-style I/O ports are queried for information, otherwise MMIO is
2997 * used at the given @base address to query the information.
2998 */
2999
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003000static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003001{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003002 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003003
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003004 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005 "CR00", 0x00,
3006 "CR01", 0x01,
3007 "CR02", 0x02,
3008 "CR03", 0x03,
3009 "CR04", 0x04,
3010 "CR05", 0x05,
3011 "CR06", 0x06,
3012 "CR07", 0x07,
3013 "CR08", 0x08,
3014 "CR09", 0x09,
3015 "CR0A", 0x0A,
3016 "CR0B", 0x0B,
3017 "CR0C", 0x0C,
3018 "CR0D", 0x0D,
3019 "CR0E", 0x0E,
3020 "CR0F", 0x0F,
3021 "CR10", 0x10,
3022 "CR11", 0x11,
3023 "CR12", 0x12,
3024 "CR13", 0x13,
3025 "CR14", 0x14,
3026 "CR15", 0x15,
3027 "CR16", 0x16,
3028 "CR17", 0x17,
3029 "CR18", 0x18,
3030 "CR22", 0x22,
3031 "CR24", 0x24,
3032 "CR26", 0x26,
3033 "CR2D", 0x2D,
3034 "CR2E", 0x2E,
3035 "CR2F", 0x2F,
3036 "CR30", 0x30,
3037 "CR31", 0x31,
3038 "CR32", 0x32,
3039 "CR33", 0x33,
3040 "CR34", 0x34,
3041 "CR35", 0x35,
3042 "CR36", 0x36,
3043 "CR37", 0x37,
3044 "CR38", 0x38,
3045 "CR39", 0x39,
3046 "CR3A", 0x3A,
3047 "CR3B", 0x3B,
3048 "CR3C", 0x3C,
3049 "CR3D", 0x3D,
3050 "CR3E", 0x3E,
3051 "CR3F", 0x3F,
3052 NULL);
3053
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003054 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003056 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003058 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003059 "SR00", 0x00,
3060 "SR01", 0x01,
3061 "SR02", 0x02,
3062 "SR03", 0x03,
3063 "SR04", 0x04,
3064 "SR08", 0x08,
3065 "SR09", 0x09,
3066 "SR0A", 0x0A,
3067 "SR0B", 0x0B,
3068 "SR0D", 0x0D,
3069 "SR10", 0x10,
3070 "SR11", 0x11,
3071 "SR12", 0x12,
3072 "SR13", 0x13,
3073 "SR14", 0x14,
3074 "SR15", 0x15,
3075 "SR16", 0x16,
3076 "SR17", 0x17,
3077 "SR18", 0x18,
3078 "SR19", 0x19,
3079 "SR1A", 0x1A,
3080 "SR1B", 0x1B,
3081 "SR1C", 0x1C,
3082 "SR1D", 0x1D,
3083 "SR1E", 0x1E,
3084 "SR1F", 0x1F,
3085 NULL);
3086
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003087 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088}
3089
3090#endif /* CIRRUSFB_DEBUG */
3091