blob: 9bb811d56721a4067a6039842ad00204a50fff51 [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,
Krzysztof Helt7cade312009-03-31 15:25:13 -070098 BT_SD64, /* GD5434 */
99 BT_PICCOLO, /* GD5426 */
100 BT_PICASSO, /* GD5426 or GD5428 */
101 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700148 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700150 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700151 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 },
153 [BT_PICCOLO] = {
154 .name = "CL Piccolo",
155 .maxclock = {
156 /* guess */
157 90000, 90000, 90000, 90000, 90000
158 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700159 .init_sr07 = true,
160 .init_sr1f = true,
161 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 .sr07 = 0x80,
163 .sr07_1bpp = 0x80,
164 .sr07_8bpp = 0x81,
165 .sr1f = 0x22
166 },
167 [BT_PICASSO] = {
168 .name = "CL Picasso",
169 .maxclock = {
170 /* guess */
171 90000, 90000, 90000, 90000, 90000
172 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700173 .init_sr07 = true,
174 .init_sr1f = true,
175 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 .sr07 = 0x20,
177 .sr07_1bpp = 0x20,
178 .sr07_8bpp = 0x21,
179 .sr1f = 0x22
180 },
181 [BT_SPECTRUM] = {
182 .name = "CL Spectrum",
183 .maxclock = {
184 /* guess */
185 90000, 90000, 90000, 90000, 90000
186 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700187 .init_sr07 = true,
188 .init_sr1f = true,
189 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 .sr07 = 0x80,
191 .sr07_1bpp = 0x80,
192 .sr07_8bpp = 0x81,
193 .sr1f = 0x22
194 },
195 [BT_PICASSO4] = {
196 .name = "CL Picasso4",
197 .maxclock = {
198 135100, 135100, 85500, 85500, 0
199 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700200 .init_sr07 = true,
201 .init_sr1f = false,
202 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700203 .sr07 = 0xA0,
204 .sr07_1bpp = 0xA0,
205 .sr07_1bpp_mux = 0xA6,
206 .sr07_8bpp = 0xA1,
207 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 .sr1f = 0
209 },
210 [BT_ALPINE] = {
211 .name = "CL Alpine",
212 .maxclock = {
213 /* for the GD5430. GD5446 can do more... */
214 85500, 85500, 50000, 28500, 0
215 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700216 .init_sr07 = true,
217 .init_sr1f = true,
218 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700220 .sr07_1bpp = 0xA0,
221 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 .sr07_8bpp = 0xA1,
223 .sr07_8bpp_mux = 0xA7,
224 .sr1f = 0x1C
225 },
226 [BT_GD5480] = {
227 .name = "CL GD5480",
228 .maxclock = {
229 135100, 200000, 200000, 135100, 135100
230 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700231 .init_sr07 = true,
232 .init_sr1f = true,
233 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 .sr07 = 0x10,
235 .sr07_1bpp = 0x11,
236 .sr07_8bpp = 0x11,
237 .sr1f = 0x1C
238 },
239 [BT_LAGUNA] = {
240 .name = "CL Laguna",
241 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700242 /* taken from X11 code */
243 170000, 170000, 170000, 170000, 135100,
244 },
245 .init_sr07 = false,
246 .init_sr1f = false,
247 .scrn_start_bit19 = true,
248 },
249 [BT_LAGUNAB] = {
250 .name = "CL Laguna AGP",
251 .maxclock = {
252 /* taken from X11 code */
253 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700255 .init_sr07 = false,
256 .init_sr1f = false,
257 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 }
259};
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261#ifdef CONFIG_PCI
262#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000263 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
265static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700266 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700267 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
268 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700269 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
270 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
271 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
272 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
275 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700276 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 { 0, }
278};
279MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
280#undef CHIP
281#endif /* CONFIG_PCI */
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283#ifdef CONFIG_ZORRO
284static const struct zorro_device_id cirrusfb_zorro_table[] = {
285 {
286 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
287 .driver_data = BT_SD64,
288 }, {
289 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
290 .driver_data = BT_PICCOLO,
291 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700292 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 .driver_data = BT_PICASSO,
294 }, {
295 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
296 .driver_data = BT_SPECTRUM,
297 }, {
298 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
299 .driver_data = BT_PICASSO4,
300 },
301 { 0 }
302};
303
304static const struct {
305 zorro_id id2;
306 unsigned long size;
307} cirrusfb_zorro_table2[] = {
308 [BT_SD64] = {
309 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
310 .size = 0x400000
311 },
312 [BT_PICCOLO] = {
313 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
314 .size = 0x200000
315 },
316 [BT_PICASSO] = {
317 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
318 .size = 0x200000
319 },
320 [BT_SPECTRUM] = {
321 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
322 .size = 0x200000
323 },
324 [BT_PICASSO4] = {
325 .id2 = 0,
326 .size = 0x400000
327 }
328};
329#endif /* CONFIG_ZORRO */
330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700332enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700333 CRT,
334 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700335};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700336#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338/* info about board */
339struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700341 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700342 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 unsigned char SFR; /* Shadow of special function register */
344
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700345 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700346 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700348 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700350 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351};
352
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700353static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700354static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356/****************************************************************************/
357/**** BEGIN PROTOTYPES ******************************************************/
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
361 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700364static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365static void switch_monitor(struct cirrusfb_info *cinfo, int on);
366static void WGen(const struct cirrusfb_info *cinfo,
367 int regnum, unsigned char val);
368static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
369static void AttrOn(const struct cirrusfb_info *cinfo);
370static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
371static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
372static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
373static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700376static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
377 unsigned char *red, unsigned char *green,
378 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700380static void cirrusfb_WaitBLT(u8 __iomem *regbase);
381static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
382 u_short curx, u_short cury,
383 u_short destx, u_short desty,
384 u_short width, u_short height,
385 u_short line_length);
386static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
387 u_short x, u_short y,
388 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700389 u32 fg_color, u32 bg_color,
390 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700392static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
394#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700395static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
396static void cirrusfb_dbg_print_regs(struct fb_info *info,
397 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700398 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399#endif /* CIRRUSFB_DEBUG */
400
401/*** END PROTOTYPES ********************************************************/
402/*****************************************************************************/
403/*** BEGIN Interface Used by the World ***************************************/
404
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700405static inline int is_laguna(const struct cirrusfb_info *cinfo)
406{
407 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
408}
409
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700416 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 return 0;
418}
419
420/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
423 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700424 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return 0;
426}
427
428/**** END Interface used by the World *************************************/
429/****************************************************************************/
430/**** BEGIN Hardware specific Routines **************************************/
431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700433static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700435 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700436 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Krzysztof Helt486ff382008-10-15 22:03:42 -0700438 /* Read MCLK value */
439 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700440 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700443 * should divide it by to get VCLK
444 */
445
446 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 1;
449 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700450 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Krzysztof Helt99a45842009-03-31 15:25:09 -0700457static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
458 struct fb_info *info)
459{
460 long freq;
461 long maxclock;
462 struct cirrusfb_info *cinfo = info->par;
463 unsigned maxclockidx = var->bits_per_pixel >> 3;
464
465 /* convert from ps to kHz */
466 freq = PICOS2KHZ(var->pixclock);
467
468 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
469
470 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
471 cinfo->multiplexing = 0;
472
473 /* If the frequency is greater than we can support, we might be able
474 * to use multiplexing for the video mode */
475 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700476 dev_err(info->device,
477 "Frequency greater than maxclock (%ld kHz)\n",
478 maxclock);
479 return -EINVAL;
480 }
481 /*
482 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
483 * pixel clock
484 */
485 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700486 switch (cinfo->btype) {
487 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700488 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700489 case BT_PICASSO4:
490 if (freq > 85500)
491 cinfo->multiplexing = 1;
492 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700493 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700494 if (freq > 135100)
495 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700496 break;
497
498 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700499 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700500 }
501 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700502
503 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700504 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700505 cinfo->doubleVCLK = 0;
506 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
507 var->bits_per_pixel == 16) {
508 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700509 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700510
Krzysztof Helt99a45842009-03-31 15:25:09 -0700511 return 0;
512}
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514static int cirrusfb_check_var(struct fb_var_screeninfo *var,
515 struct fb_info *info)
516{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700517 int yres;
518 /* memory size in pixels */
519 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700520 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700523 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 var->red.offset = 0;
525 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700526 var->green = var->red;
527 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 break;
529
530 case 8:
531 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700532 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700533 var->green = var->red;
534 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 break;
536
537 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700538 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 var->red.offset = 2;
540 var->green.offset = -3;
541 var->blue.offset = 8;
542 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700543 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 var->green.offset = 5;
545 var->blue.offset = 0;
546 }
547 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700548 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 var->blue.length = 5;
550 break;
551
Krzysztof Helt7cade312009-03-31 15:25:13 -0700552 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700553 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700554 var->red.offset = 0;
555 var->green.offset = 8;
556 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 } else {
558 var->red.offset = 16;
559 var->green.offset = 8;
560 var->blue.offset = 0;
561 }
562 var->red.length = 8;
563 var->green.length = 8;
564 var->blue.length = 8;
565 break;
566
567 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700568 dev_dbg(info->device,
569 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700570 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 /* should never occur */
572 break;
573 }
574
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700575 if (var->xres_virtual < var->xres)
576 var->xres_virtual = var->xres;
577 /* use highest possible virtual resolution */
578 if (var->yres_virtual == -1) {
579 var->yres_virtual = pixels / var->xres_virtual;
580
581 dev_info(info->device,
582 "virtual resolution set to maximum of %dx%d\n",
583 var->xres_virtual, var->yres_virtual);
584 }
585 if (var->yres_virtual < var->yres)
586 var->yres_virtual = var->yres;
587
588 if (var->xres_virtual * var->yres_virtual > pixels) {
589 dev_err(info->device, "mode %dx%dx%d rejected... "
590 "virtual resolution too high to fit into video memory!\n",
591 var->xres_virtual, var->yres_virtual,
592 var->bits_per_pixel);
593 return -EINVAL;
594 }
595
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700596 if (var->xoffset < 0)
597 var->xoffset = 0;
598 if (var->yoffset < 0)
599 var->yoffset = 0;
600
601 /* truncate xoffset and yoffset to maximum if too high */
602 if (var->xoffset > var->xres_virtual - var->xres)
603 var->xoffset = var->xres_virtual - var->xres - 1;
604 if (var->yoffset > var->yres_virtual - var->yres)
605 var->yoffset = var->yres_virtual - var->yres - 1;
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 var->red.msb_right =
608 var->green.msb_right =
609 var->blue.msb_right =
610 var->transp.offset =
611 var->transp.length =
612 var->transp.msb_right = 0;
613
614 yres = var->yres;
615 if (var->vmode & FB_VMODE_DOUBLE)
616 yres *= 2;
617 else if (var->vmode & FB_VMODE_INTERLACED)
618 yres = (yres + 1) / 2;
619
620 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700621 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700622 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return -EINVAL;
624 }
625
Krzysztof Helt99a45842009-03-31 15:25:09 -0700626 if (cirrusfb_check_pixclock(var, info))
627 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700629 if (!is_laguna(cinfo))
630 var->accel_flags = FB_ACCELF_TEXT;
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 return 0;
633}
634
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700638 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639
Krzysztof Helt8503df62007-10-16 01:29:08 -0700640 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700641 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700644 dev_dbg(info->device, "Set %s as pixclock source.\n",
645 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700646 old1f |= 0x40;
647 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
648 if (div == 2)
649 old1e |= 1;
650
651 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700653 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654}
655
656/*************************************************************************
657 cirrusfb_set_par_foo()
658
659 actually writes the values for a new video mode into the hardware,
660**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700661static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 struct cirrusfb_info *cinfo = info->par;
664 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 u8 __iomem *regbase = cinfo->regbase;
666 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700667 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700669 int hdispend, hsyncstart, hsyncend, htotal;
670 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700671 long freq;
672 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700673 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700675 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700677
678 switch (var->bits_per_pixel) {
679 case 1:
680 info->fix.line_length = var->xres_virtual / 8;
681 info->fix.visual = FB_VISUAL_MONO10;
682 break;
683
684 case 8:
685 info->fix.line_length = var->xres_virtual;
686 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
687 break;
688
689 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700690 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700691 info->fix.line_length = var->xres_virtual *
692 var->bits_per_pixel >> 3;
693 info->fix.visual = FB_VISUAL_TRUECOLOR;
694 break;
695 }
696 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700698 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 bi = &cirrusfb_board_info[cinfo->btype];
701
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 hsyncstart = var->xres + var->right_margin;
703 hsyncend = hsyncstart + var->hsync_len;
704 htotal = (hsyncend + var->left_margin) / 8 - 5;
705 hdispend = var->xres / 8 - 1;
706 hsyncstart = hsyncstart / 8 + 1;
707 hsyncend = hsyncend / 8 + 1;
708
709 yres = var->yres;
710 vsyncstart = yres + var->lower_margin;
711 vsyncend = vsyncstart + var->vsync_len;
712 vtotal = vsyncend + var->upper_margin;
713 vdispend = yres - 1;
714
715 if (var->vmode & FB_VMODE_DOUBLE) {
716 yres *= 2;
717 vsyncstart *= 2;
718 vsyncend *= 2;
719 vtotal *= 2;
720 } else if (var->vmode & FB_VMODE_INTERLACED) {
721 yres = (yres + 1) / 2;
722 vsyncstart = (vsyncstart + 1) / 2;
723 vsyncend = (vsyncend + 1) / 2;
724 vtotal = (vtotal + 1) / 2;
725 }
726
727 vtotal -= 2;
728 vsyncstart -= 1;
729 vsyncend -= 1;
730
731 if (yres >= 1024) {
732 vtotal /= 2;
733 vsyncstart /= 2;
734 vsyncend /= 2;
735 vdispend /= 2;
736 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700737 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700738 htotal /= 2;
739 hsyncstart /= 2;
740 hsyncend /= 2;
741 hdispend /= 2;
742 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700744 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700747 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700751 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700753 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
Krzysztof Helt8503df62007-10-16 01:29:08 -0700756 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700757 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700758 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700761 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 tmp = hsyncend % 32;
765 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700767 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700768 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700770 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700771 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700774 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700776 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700782 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700789 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 tmp |= 0x20;
794 if (var->vmode & FB_VMODE_DOUBLE)
795 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700796 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700797 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700799 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700802 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700805 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700808 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700811 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700812 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700814 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700815 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 tmp = 0;
818 if (var->vmode & FB_VMODE_INTERLACED)
819 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700820 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700822 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 tmp |= 128;
828
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700829 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700830 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700832 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700833 if (var->bits_per_pixel == 24)
834 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
835 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700836 if (cinfo->multiplexing)
837 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700838 if (cinfo->doubleVCLK)
839 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700840
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700841 bestclock(freq, &nom, &den, &div);
842
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700843 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
844 freq, nom, den, div);
845
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 /* set VCLK0 */
847 /* hardware RefClock: 14.31818 MHz */
848 /* formula: VClk = (OSC * N) / (D * (1+P)) */
849 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
850
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700851 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
852 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700853 /* if freq is close to mclk or mclk/2 select mclk
854 * as clock source
855 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700856 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700857 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700858 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700859 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700860 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700861 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700862 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
863 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
864 unsigned short tile_control;
865
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700866 if (cinfo->btype == BT_LAGUNAB) {
867 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
868 tile_control &= ~0x80;
869 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
870 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700871
872 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
873 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
874 control = fb_readw(cinfo->laguna_mmio + 0x402);
875 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
876 control &= ~0x6800;
877 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700878 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700879 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700880 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700881 tmp = den << 1;
882 if (div != 0)
883 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700884 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
885 if ((cinfo->btype == BT_SD64) ||
886 (cinfo->btype == BT_ALPINE) ||
887 (cinfo->btype == BT_GD5480))
888 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700890 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700891 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700892 vga_wseq(regbase, CL_SEQRE, tmp);
893 vga_wseq(regbase, CL_SEQR1E, nom);
894 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700895 vga_wseq(regbase, CL_SEQRE, nom);
896 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700897 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700900 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700902 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 else
904 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
905 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700906 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 /* don't know if it would hurt to also program this if no interlaced */
909 /* mode is used, but I feel better this way.. :-) */
910 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700911 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700915 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700917 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
919 tmp |= 0x40;
920 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
921 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700922 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Krzysztof Helt8503df62007-10-16 01:29:08 -0700924 /* text cursor on and start line */
925 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
926 /* text cursor end line */
927 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 /******************************************************
930 *
931 * 1 bpp
932 *
933 */
934
935 /* programming for different color depths */
936 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700937 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700938 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
940 /* SR07 */
941 switch (cinfo->btype) {
942 case BT_SD64:
943 case BT_PICCOLO:
944 case BT_PICASSO:
945 case BT_SPECTRUM:
946 case BT_PICASSO4:
947 case BT_ALPINE:
948 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700949 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700950 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 bi->sr07_1bpp_mux : bi->sr07_1bpp);
952 break;
953
954 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700955 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700956 vga_wseq(regbase, CL_SEQR7,
957 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 break;
959
960 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700961 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 break;
963 }
964
965 /* Extended Sequencer Mode */
966 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
968 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700969 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700970 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
971 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 break;
973
974 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700975 /* ## vorher d0 avoid FIFO underruns..? */
976 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
978
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700979 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 case BT_PICASSO4:
981 case BT_ALPINE:
982 case BT_GD5480:
983 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700984 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 /* do nothing */
986 break;
987
988 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700989 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 break;
991 }
992
Krzysztof Helt8503df62007-10-16 01:29:08 -0700993 /* pixel mask: pass-through for first plane */
994 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700995 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700996 /* hidden dac reg: 1280x1024 */
997 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700999 /* hidden dac: nothing */
1000 WHDR(cinfo, 0);
1001 /* memory mode: odd/even, ext. memory */
1002 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1003 /* plane mask: only write to first plane */
1004 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
1007 /******************************************************
1008 *
1009 * 8 bpp
1010 *
1011 */
1012
1013 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001014 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 switch (cinfo->btype) {
1016 case BT_SD64:
1017 case BT_PICCOLO:
1018 case BT_PICASSO:
1019 case BT_SPECTRUM:
1020 case BT_PICASSO4:
1021 case BT_ALPINE:
1022 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001023 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001024 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1026 break;
1027
1028 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001029 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001030 vga_wseq(regbase, CL_SEQR7,
1031 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001032 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034
1035 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001036 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038 }
1039
1040 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001044 /* Fast Page-Mode writes */
1045 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 break;
1047
1048 case BT_PICASSO4:
1049#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001050 /* ### INCOMPLETE!! */
1051 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001054 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 case BT_GD5480:
1056 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001057 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 /* do nothing */
1059 break;
1060
1061 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001062 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 break;
1064 }
1065
Krzysztof Helt8503df62007-10-16 01:29:08 -07001066 /* mode register: 256 color mode */
1067 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001068 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001069 /* hidden dac reg: 1280x1024 */
1070 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001072 /* hidden dac: nothing */
1073 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 }
1075
1076 /******************************************************
1077 *
1078 * 16 bpp
1079 *
1080 */
1081
1082 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001083 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001086 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001087 vga_wseq(regbase, CL_SEQR7, 0x87);
1088 /* Fast Page-Mode writes */
1089 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090 break;
1091
1092 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001093 vga_wseq(regbase, CL_SEQR7, 0x27);
1094 /* Fast Page-Mode writes */
1095 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 break;
1097
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001098 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001101 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001102 vga_wseq(regbase, CL_SEQR7,
1103 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 break;
1105
1106 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001107 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 /* We already set SRF and SR1F */
1109 break;
1110
1111 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001112 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001113 vga_wseq(regbase, CL_SEQR7,
1114 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001115 control |= 0x2000;
1116 format |= 0x1400;
1117 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 break;
1119
1120 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001121 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 break;
1123 }
1124
Krzysztof Helt8503df62007-10-16 01:29:08 -07001125 /* mode register: 256 color mode */
1126 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001128 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129#elif defined(CONFIG_ZORRO)
1130 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 }
1134
1135 /******************************************************
1136 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001137 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 *
1139 */
1140
Krzysztof Helt7cade312009-03-31 15:25:13 -07001141 else if (var->bits_per_pixel == 24) {
1142 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001145 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001146 vga_wseq(regbase, CL_SEQR7, 0x85);
1147 /* Fast Page-Mode writes */
1148 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 break;
1150
1151 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001152 vga_wseq(regbase, CL_SEQR7, 0x25);
1153 /* Fast Page-Mode writes */
1154 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001157 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001160 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001161 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 break;
1163
1164 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001165 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 /* We already set SRF and SR1F */
1167 break;
1168
1169 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001170 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001171 vga_wseq(regbase, CL_SEQR7,
1172 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001173 control |= 0x4000;
1174 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001175 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177
1178 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001179 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 break;
1181 }
1182
Krzysztof Helt8503df62007-10-16 01:29:08 -07001183 /* mode register: 256 color mode */
1184 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001185 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1186 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188
1189 /******************************************************
1190 *
1191 * unknown/unsupported bpp
1192 *
1193 */
1194
Krzysztof Helt8503df62007-10-16 01:29:08 -07001195 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001196 dev_err(info->device,
1197 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199
Krzysztof Helt6683e012009-03-31 15:25:06 -07001200 pitch = info->fix.line_length >> 3;
1201 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001203 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 tmp |= 0x10; /* offset overflow bit */
1205
Krzysztof Helt8503df62007-10-16 01:29:08 -07001206 /* screen start addr #16-18, fastpagemode cycles */
1207 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001209 /* screen start address bit 19 */
1210 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001211 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001213 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001214 tmp = 0;
1215 if ((htotal + 5) & 256)
1216 tmp |= 128;
1217 if (hdispend & 256)
1218 tmp |= 64;
1219 if (hsyncstart & 256)
1220 tmp |= 48;
1221 if (vtotal & 1024)
1222 tmp |= 8;
1223 if (vdispend & 1024)
1224 tmp |= 4;
1225 if (vsyncstart & 1024)
1226 tmp |= 3;
1227
1228 vga_wcrt(regbase, CL_CRT1E, tmp);
1229 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1230 }
1231
Krzysztof Helt8503df62007-10-16 01:29:08 -07001232 /* pixel panning */
1233 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234
1235 /* [ EGS: SetOffset(); ] */
1236 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001237 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001239 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001240 /* no tiles */
1241 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1242 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1243 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1244 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 /* finally, turn on everything - turn off "FullBandwidth" bit */
1246 /* also, set "DotClock%2" bit where requested */
1247 tmp = 0x01;
1248
1249/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1250 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1251 tmp |= 0x08;
1252*/
1253
Krzysztof Helt8503df62007-10-16 01:29:08 -07001254 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001255 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001258 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259#endif
1260
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 return 0;
1262}
1263
1264/* for some reason incomprehensible to me, cirrusfb requires that you write
1265 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001266static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001267{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001268 cirrusfb_set_par_foo(info);
1269 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270}
1271
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1273 unsigned blue, unsigned transp,
1274 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275{
1276 struct cirrusfb_info *cinfo = info->par;
1277
1278 if (regno > 255)
1279 return -EINVAL;
1280
1281 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1282 u32 v;
1283 red >>= (16 - info->var.red.length);
1284 green >>= (16 - info->var.green.length);
1285 blue >>= (16 - info->var.blue.length);
1286
Krzysztof Helt8503df62007-10-16 01:29:08 -07001287 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 return 1;
1289 v = (red << info->var.red.offset) |
1290 (green << info->var.green.offset) |
1291 (blue << info->var.blue.offset);
1292
Krzysztof Helt060b6002007-10-16 01:29:13 -07001293 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294 return 0;
1295 }
1296
Krzysztof Helt8503df62007-10-16 01:29:08 -07001297 if (info->var.bits_per_pixel == 8)
1298 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299
1300 return 0;
1301
1302}
1303
1304/*************************************************************************
1305 cirrusfb_pan_display()
1306
1307 performs display panning - provided hardware permits this
1308**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001309static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1310 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001312 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001314 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001315 struct cirrusfb_info *cinfo = info->par;
1316
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 /* no range checks for xoffset and yoffset, */
1318 /* as fb_pan_display has already done this */
1319 if (var->vmode & FB_VMODE_YWRAP)
1320 return -EINVAL;
1321
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
Krzysztof Helt99a45842009-03-31 15:25:09 -07001324 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
1326 if (info->var.bits_per_pixel == 1) {
1327 /* base is already correct */
1328 xpix = (unsigned char) (var->xoffset % 8);
1329 } else {
1330 base /= 4;
1331 xpix = (unsigned char) ((xoffset % 4) * 2);
1332 }
1333
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001334 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001335 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
1337 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001338 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1339 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001341 /* 0xf2 is %11110010, exclude tmp bits */
1342 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 /* construct bits 16, 17 and 18 of screen start address */
1344 if (base & 0x10000)
1345 tmp |= 0x01;
1346 if (base & 0x20000)
1347 tmp |= 0x04;
1348 if (base & 0x40000)
1349 tmp |= 0x08;
1350
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001351 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001354 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001355 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1356 if (is_laguna(cinfo))
1357 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1358 else
1359 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001360 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 /* write pixel panning value to AR33; this does not quite work in 8bpp
1364 *
1365 * ### Piccolo..? Will this work?
1366 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001368 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Krzysztof Helt8503df62007-10-16 01:29:08 -07001370 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371}
1372
Krzysztof Helt8503df62007-10-16 01:29:08 -07001373static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374{
1375 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001376 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1377 * then the caller blanks by setting the CLUT (Color Look Up Table)
1378 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1379 * failed due to e.g. a video mode which doesn't support it.
1380 * Implements VESA suspend and powerdown modes on hardware that
1381 * supports disabling hsync/vsync:
1382 * blank_mode == 2: suspend vsync
1383 * blank_mode == 3: suspend hsync
1384 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385 */
1386 unsigned char val;
1387 struct cirrusfb_info *cinfo = info->par;
1388 int current_mode = cinfo->blank_mode;
1389
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001390 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391
1392 if (info->state != FBINFO_STATE_RUNNING ||
1393 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001394 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395 return 0;
1396 }
1397
1398 /* Undo current */
1399 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001400 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001401 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001402 val = 0;
1403 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001404 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001405 val = 0x20;
1406
1407 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1408 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409
1410 switch (blank_mode) {
1411 case FB_BLANK_UNBLANK:
1412 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001413 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 break;
1415 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001416 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417 break;
1418 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001419 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 break;
1421 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001422 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 break;
1424 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001425 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 return 1;
1427 }
1428
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 vga_wgfx(cinfo->regbase, CL_GRE, val);
1430
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001432 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
1434 /* Let fbcon do a soft blank for us */
1435 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1436}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001437
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438/**** END Hardware specific Routines **************************************/
1439/****************************************************************************/
1440/**** BEGIN Internal Routines ***********************************************/
1441
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001442static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001444 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 const struct cirrusfb_board_info_rec *bi;
1446
Krzysztof Helt8503df62007-10-16 01:29:08 -07001447 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448
1449 bi = &cirrusfb_board_info[cinfo->btype];
1450
1451 /* reset board globally */
1452 switch (cinfo->btype) {
1453 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001454 WSFR(cinfo, 0x01);
1455 udelay(500);
1456 WSFR(cinfo, 0x51);
1457 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 break;
1459 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001460 WSFR2(cinfo, 0xff);
1461 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 break;
1463 case BT_SD64:
1464 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001465 WSFR(cinfo, 0x1f);
1466 udelay(500);
1467 WSFR(cinfo, 0x4f);
1468 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 break;
1470 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001471 /* disable flickerfixer */
1472 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1473 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001474 /* mode */
1475 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001476 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 /* from Klaus' NetBSD driver: */
1478 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001479 case BT_ALPINE: /* fall through */
1480 /* put blitter into 542x compat */
1481 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 break;
1483
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001484 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001485 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 /* Nothing to do to reset the board. */
1487 break;
1488
1489 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001490 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 break;
1492 }
1493
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001494 /* make sure RAM size set by this point */
1495 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
1497 /* the P4 is not fully initialized here; I rely on it having been */
1498 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001499 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
1501 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001502 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1503 WGen(cinfo, CL_POS102, 0x01);
1504 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001510 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 /* FullBandwidth (video off) and 8/9 dot clock */
1513 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
Krzysztof Helt8503df62007-10-16 01:29:08 -07001515 /* "magic cookie" - doesn't make any sense to me.. */
1516/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1517 /* unlock all extension registers */
1518 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 switch (cinfo->btype) {
1521 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001522 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 break;
1524 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001525 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001526 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 break;
1528 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001529#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001531#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532 break;
1533 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1535 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 break;
1537 }
1538 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001539 /* plane mask: nothing */
1540 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1541 /* character map select: doesn't even matter in gx mode */
1542 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001543 /* memory mode: chain4, ext. memory */
1544 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545
1546 /* controller-internal base address of video memory */
1547 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001548 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1551 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1554 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1555 /* graphics cursor Y position (..."... ) */
1556 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1557 /* graphics cursor attributes */
1558 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1559 /* graphics cursor pattern address */
1560 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
1562 /* writing these on a P4 might give problems.. */
1563 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001564 /* configuration readback and ext. color */
1565 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1566 /* signature generator */
1567 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 }
1569
Krzysztof Helt8503df62007-10-16 01:29:08 -07001570 /* Screen A preset row scan: none */
1571 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1572 /* Text cursor start: disable text cursor */
1573 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1574 /* Text cursor end: - */
1575 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001576 /* text cursor location high: 0 */
1577 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1578 /* text cursor location low: 0 */
1579 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 /* Underline Row scanline: - */
1582 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001584 /* ext. display controls: ext.adr. wrap */
1585 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 /* Set/Reset registes: - */
1588 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1589 /* Set/Reset enable: - */
1590 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1591 /* Color Compare: - */
1592 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1593 /* Data Rotate: - */
1594 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1595 /* Read Map Select: - */
1596 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1597 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1598 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1599 /* Miscellaneous: memory map base address, graphics mode */
1600 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1601 /* Color Don't care: involve all planes */
1602 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1603 /* Bit Mask: no mask at all */
1604 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001605
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001606 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1607 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* (5434 can't have bit 3 set for bitblt) */
1609 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* Graphics controller mode extensions: finer granularity,
1612 * 8byte data latches
1613 */
1614 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1617 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1618 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1619 /* Background color byte 1: - */
1620 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1621 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* Attribute Controller palette registers: "identity mapping" */
1624 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1625 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1626 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1627 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1628 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1629 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1630 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* Attribute Controller mode: graphics mode */
1642 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1643 /* Overscan color reg.: reg. 0 */
1644 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1645 /* Color Plane enable: Enable all 4 planes */
1646 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* Color Select: - */
1648 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 /* BLT Start/status: Blitter reset */
1653 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1654 /* - " - : "end-of-reset" */
1655 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
1657 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 return;
1660}
1661
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663{
1664#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1665 static int IsOn = 0; /* XXX not ok for multiple boards */
1666
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 if (cinfo->btype == BT_PICASSO4)
1668 return; /* nothing to switch */
1669 if (cinfo->btype == BT_ALPINE)
1670 return; /* nothing to switch */
1671 if (cinfo->btype == BT_GD5480)
1672 return; /* nothing to switch */
1673 if (cinfo->btype == BT_PICASSO) {
1674 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 return;
1677 }
1678 if (on) {
1679 switch (cinfo->btype) {
1680 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001681 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682 break;
1683 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 break;
1686 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688 break;
1689 default: /* do nothing */ break;
1690 }
1691 } else {
1692 switch (cinfo->btype) {
1693 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 break;
1696 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 break;
1699 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001700 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001702 default: /* do nothing */
1703 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 }
1705 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706#endif /* CONFIG_ZORRO */
1707}
1708
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709/******************************************/
1710/* Linux 2.6-style accelerated functions */
1711/******************************************/
1712
Krzysztof Helt8343c892009-03-31 15:25:11 -07001713static int cirrusfb_sync(struct fb_info *info)
1714{
1715 struct cirrusfb_info *cinfo = info->par;
1716
1717 if (!is_laguna(cinfo)) {
1718 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1719 cpu_relax();
1720 }
1721 return 0;
1722}
1723
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724static void cirrusfb_fillrect(struct fb_info *info,
1725 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 struct fb_fillrect modded;
1728 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001729 struct cirrusfb_info *cinfo = info->par;
1730 int m = info->var.bits_per_pixel;
1731 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1732 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733
1734 if (info->state != FBINFO_STATE_RUNNING)
1735 return;
1736 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1737 cfb_fillrect(info, region);
1738 return;
1739 }
1740
1741 vxres = info->var.xres_virtual;
1742 vyres = info->var.yres_virtual;
1743
1744 memcpy(&modded, region, sizeof(struct fb_fillrect));
1745
Krzysztof Helt8503df62007-10-16 01:29:08 -07001746 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747 modded.dx >= vxres || modded.dy >= vyres)
1748 return;
1749
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 if (modded.dx + modded.width > vxres)
1751 modded.width = vxres - modded.dx;
1752 if (modded.dy + modded.height > vyres)
1753 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754
Krzysztof Helt060b6002007-10-16 01:29:13 -07001755 cirrusfb_RectFill(cinfo->regbase,
1756 info->var.bits_per_pixel,
1757 (region->dx * m) / 8, region->dy,
1758 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001759 color, color,
1760 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761}
1762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763static void cirrusfb_copyarea(struct fb_info *info,
1764 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 struct fb_copyarea modded;
1767 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001768 struct cirrusfb_info *cinfo = info->par;
1769 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
1771 if (info->state != FBINFO_STATE_RUNNING)
1772 return;
1773 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1774 cfb_copyarea(info, area);
1775 return;
1776 }
1777
1778 vxres = info->var.xres_virtual;
1779 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001780 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781
Krzysztof Helt8503df62007-10-16 01:29:08 -07001782 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 modded.sx >= vxres || modded.sy >= vyres ||
1784 modded.dx >= vxres || modded.dy >= vyres)
1785 return;
1786
Krzysztof Helt8503df62007-10-16 01:29:08 -07001787 if (modded.sx + modded.width > vxres)
1788 modded.width = vxres - modded.sx;
1789 if (modded.dx + modded.width > vxres)
1790 modded.width = vxres - modded.dx;
1791 if (modded.sy + modded.height > vyres)
1792 modded.height = vyres - modded.sy;
1793 if (modded.dy + modded.height > vyres)
1794 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Krzysztof Helt060b6002007-10-16 01:29:13 -07001796 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1797 (area->sx * m) / 8, area->sy,
1798 (area->dx * m) / 8, area->dy,
1799 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001800 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001801
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802}
1803
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804static void cirrusfb_imageblit(struct fb_info *info,
1805 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806{
1807 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001808 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Krzysztof Helt9e848062009-03-31 15:25:11 -07001810 if (info->state != FBINFO_STATE_RUNNING)
1811 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001812 /* Alpine/SD64 does not work at 24bpp ??? */
1813 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1814 cfb_imageblit(info, image);
1815 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1816 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001817 cfb_imageblit(info, image);
1818 else {
1819 unsigned size = ((image->width + 7) >> 3) * image->height;
1820 int m = info->var.bits_per_pixel;
1821 u32 fg, bg;
1822
1823 if (info->var.bits_per_pixel == 8) {
1824 fg = image->fg_color;
1825 bg = image->bg_color;
1826 } else {
1827 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1828 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1829 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001830 if (info->var.bits_per_pixel == 24) {
1831 /* clear background first */
1832 cirrusfb_RectFill(cinfo->regbase,
1833 info->var.bits_per_pixel,
1834 (image->dx * m) / 8, image->dy,
1835 (image->width * m) / 8,
1836 image->height,
1837 bg, bg,
1838 info->fix.line_length, 0x40);
1839 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001840 cirrusfb_RectFill(cinfo->regbase,
1841 info->var.bits_per_pixel,
1842 (image->dx * m) / 8, image->dy,
1843 (image->width * m) / 8, image->height,
1844 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001845 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001846 memcpy(info->screen_base, image->data, size);
1847 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848}
1849
Linus Torvalds1da177e2005-04-16 15:20:36 -07001850#ifdef CONFIG_PPC_PREP
1851#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1852#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001853static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001854{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855 *display = PREP_VIDEO_BASE;
1856 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857}
1858
1859#endif /* CONFIG_PPC_PREP */
1860
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863
1864/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1865 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1866 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1867 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001868static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1869 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870{
1871 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001872 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001874 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001875 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1876
1877 mem = ((SR14 & 7) + 1) << 20;
1878 } else {
1879 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1880 switch ((SRF & 0x18)) {
1881 case 0x08:
1882 mem = 512 * 1024;
1883 break;
1884 case 0x10:
1885 mem = 1024 * 1024;
1886 break;
1887 /* 64-bit DRAM data bus width; assume 2MB.
1888 * Also indicates 2MB memory on the 5430.
1889 */
1890 case 0x18:
1891 mem = 2048 * 1024;
1892 break;
1893 default:
1894 dev_warn(info->device, "Unknown memory size!\n");
1895 mem = 1024 * 1024;
1896 }
1897 /* If DRAM bank switching is enabled, there must be
1898 * twice as much memory installed. (4MB on the 5434)
1899 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001900 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001901 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001903
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 return mem;
1906}
1907
Krzysztof Helt8503df62007-10-16 01:29:08 -07001908static void get_pci_addrs(const struct pci_dev *pdev,
1909 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001911 assert(pdev != NULL);
1912 assert(display != NULL);
1913 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001914
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 *display = 0;
1916 *registers = 0;
1917
1918 /* This is a best-guess for now */
1919
1920 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1921 *display = pci_resource_start(pdev, 1);
1922 *registers = pci_resource_start(pdev, 0);
1923 } else {
1924 *display = pci_resource_start(pdev, 0);
1925 *registers = pci_resource_start(pdev, 1);
1926 }
1927
Krzysztof Helt8503df62007-10-16 01:29:08 -07001928 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929}
1930
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001931static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001933 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001934 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001936 if (cinfo->laguna_mmio == NULL)
1937 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001938 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939#if 0 /* if system didn't claim this region, we would... */
1940 release_mem_region(0xA0000, 65535);
1941#endif
1942 if (release_io_ports)
1943 release_region(0x3C0, 32);
1944 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945}
1946#endif /* CONFIG_PCI */
1947
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001949static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950{
Al Virod91f5bb2007-10-17 00:27:18 +01001951 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001952 struct zorro_dev *zdev = to_zorro_dev(info->device);
1953
1954 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
1956 if (cinfo->btype == BT_PICASSO4) {
1957 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001958 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001959 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001961 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001962 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964}
1965#endif /* CONFIG_ZORRO */
1966
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001967/* function table of the above functions */
1968static struct fb_ops cirrusfb_ops = {
1969 .owner = THIS_MODULE,
1970 .fb_open = cirrusfb_open,
1971 .fb_release = cirrusfb_release,
1972 .fb_setcolreg = cirrusfb_setcolreg,
1973 .fb_check_var = cirrusfb_check_var,
1974 .fb_set_par = cirrusfb_set_par,
1975 .fb_pan_display = cirrusfb_pan_display,
1976 .fb_blank = cirrusfb_blank,
1977 .fb_fillrect = cirrusfb_fillrect,
1978 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001979 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001980 .fb_imageblit = cirrusfb_imageblit,
1981};
1982
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001983static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001985 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 struct fb_var_screeninfo *var = &info->var;
1987
Linus Torvalds1da177e2005-04-16 15:20:36 -07001988 info->pseudo_palette = cinfo->pseudo_palette;
1989 info->flags = FBINFO_DEFAULT
1990 | FBINFO_HWACCEL_XPAN
1991 | FBINFO_HWACCEL_YPAN
1992 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07001993 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001995 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07001997 info->fix.accel = FB_ACCEL_NONE;
1998 } else
1999 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2000
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002002
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 if (cinfo->btype == BT_GD5480) {
2004 if (var->bits_per_pixel == 16)
2005 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002006 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 info->screen_base += 2 * MB_;
2008 }
2009
2010 /* Fill fix common fields */
2011 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2012 sizeof(info->fix.id));
2013
2014 /* monochrome: only 1 memory plane */
2015 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002016 info->fix.smem_len = info->screen_size;
2017 if (var->bits_per_pixel == 1)
2018 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 info->fix.xpanstep = 1;
2021 info->fix.ypanstep = 1;
2022 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
2024 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026
2027 fb_alloc_cmap(&info->cmap, 256, 0);
2028
2029 return 0;
2030}
2031
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002032static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002034 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036
2037 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002038 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002040 /* set all the vital stuff */
2041 cirrusfb_set_fbinfo(info);
2042
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002043 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002045 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2046 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002047 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002048 err = -EINVAL;
2049 goto err_dealloc_cmap;
2050 }
2051
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 info->var.activate = FB_ACTIVATE_NOW;
2053
Krzysztof Helt99a45842009-03-31 15:25:09 -07002054 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 if (err < 0) {
2056 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002057 dev_dbg(info->device,
2058 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002059 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002060 }
2061
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 err = register_framebuffer(info);
2063 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002064 dev_err(info->device,
2065 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 goto err_dealloc_cmap;
2067 }
2068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 return 0;
2070
2071err_dealloc_cmap:
2072 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 return err;
2074}
2075
Krzysztof Helt8503df62007-10-16 01:29:08 -07002076static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077{
2078 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079
Krzysztof Helt8503df62007-10-16 01:29:08 -07002080 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002081 unregister_framebuffer(info);
2082 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002083 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002084 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002085 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086}
2087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002089static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2090 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091{
2092 struct cirrusfb_info *cinfo;
2093 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 unsigned long board_addr, board_size;
2095 int ret;
2096
2097 ret = pci_enable_device(pdev);
2098 if (ret < 0) {
2099 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2100 goto err_out;
2101 }
2102
2103 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2104 if (!info) {
2105 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2106 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002107 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108 }
2109
2110 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002111 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002113 dev_dbg(info->device,
2114 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002115 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002116 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2117 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118
Krzysztof Helt8503df62007-10-16 01:29:08 -07002119 if (isPReP) {
2120 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002122 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002124 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002125 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002127 dev_dbg(info->device,
2128 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002129 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002130 /* FIXME: this forces VGA. alternatives? */
2131 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002132 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 }
2134
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002135 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002136 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002138 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002139 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140
2141 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002142 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002143 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2144 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145 goto err_release_fb;
2146 }
2147#if 0 /* if the system didn't claim this region, we would... */
2148 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002149 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2150 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 ret = -EBUSY;
2152 goto err_release_regions;
2153 }
2154#endif
2155 if (request_region(0x3C0, 32, "cirrusfb"))
2156 release_io_ports = 1;
2157
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002158 info->screen_base = ioremap(board_addr, board_size);
2159 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 ret = -EIO;
2161 goto err_release_legacy;
2162 }
2163
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002164 info->fix.smem_start = board_addr;
2165 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 cinfo->unmap = cirrusfb_pci_unmap;
2167
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002168 dev_info(info->device,
2169 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2170 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 pci_set_drvdata(pdev, info);
2172
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002173 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002174 if (!ret)
2175 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002177 pci_set_drvdata(pdev, NULL);
2178 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179err_release_legacy:
2180 if (release_io_ports)
2181 release_region(0x3C0, 32);
2182#if 0
2183 release_mem_region(0xA0000, 65535);
2184err_release_regions:
2185#endif
2186 pci_release_regions(pdev);
2187err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002188 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002189 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191err_out:
2192 return ret;
2193}
2194
Krzysztof Helt8503df62007-10-16 01:29:08 -07002195static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196{
2197 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198
Krzysztof Helt8503df62007-10-16 01:29:08 -07002199 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200}
2201
2202static struct pci_driver cirrusfb_pci_driver = {
2203 .name = "cirrusfb",
2204 .id_table = cirrusfb_pci_table,
2205 .probe = cirrusfb_pci_register,
2206 .remove = __devexit_p(cirrusfb_pci_unregister),
2207#ifdef CONFIG_PM
2208#if 0
2209 .suspend = cirrusfb_pci_suspend,
2210 .resume = cirrusfb_pci_resume,
2211#endif
2212#endif
2213};
2214#endif /* CONFIG_PCI */
2215
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002217static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2218 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002219{
2220 struct cirrusfb_info *cinfo;
2221 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002222 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223 struct zorro_dev *z2 = NULL;
2224 unsigned long board_addr, board_size, size;
2225 int ret;
2226
2227 btype = ent->driver_data;
2228 if (cirrusfb_zorro_table2[btype].id2)
2229 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2230 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231
2232 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2233 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002234 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002235 ret = -ENOMEM;
2236 goto err_out;
2237 }
2238
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002239 dev_info(info->device, "%s board detected\n",
2240 cirrusfb_board_info[btype].name);
2241
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 cinfo->btype = btype;
2244
Al Viro36ea96a2007-10-27 19:46:58 +01002245 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002246 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002247
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 board_addr = zorro_resource_start(z);
2249 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002250 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251
2252 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002253 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2254 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 ret = -EBUSY;
2256 goto err_release_fb;
2257 }
2258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 ret = -EIO;
2260
2261 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002262 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
2264 /* To be precise, for the P4 this is not the */
2265 /* begin of the board, but the begin of RAM. */
2266 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2267 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 if (!cinfo->regbase)
2270 goto err_release_region;
2271
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002272 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002273 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002277 info->fix.smem_start = board_addr + 16777216;
2278 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2279 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 goto err_unmap_regbase;
2281 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002282 dev_info(info->device, " REG at $%lx\n",
2283 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002285 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002287 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002289 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2290 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 goto err_release_region;
2292
2293 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002294 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002295 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002297 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002298 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 }
2300 cinfo->unmap = cirrusfb_zorro_unmap;
2301
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002302 dev_info(info->device,
2303 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2304 board_size / MB_, board_addr);
2305
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 zorro_set_drvdata(z, info);
2307
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002308 /* MCLK select etc. */
2309 if (cirrusfb_board_info[btype].init_sr1f)
2310 vga_wseq(cinfo->regbase, CL_SEQR1F,
2311 cirrusfb_board_info[btype].sr1f);
2312
Al Virod91f5bb2007-10-17 00:27:18 +01002313 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002314 if (!ret)
2315 return 0;
2316
2317 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2318 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319
2320err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002321 if (btype == BT_PICASSO4)
2322 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323err_release_region:
2324 release_region(board_addr, board_size);
2325err_release_fb:
2326 framebuffer_release(info);
2327err_out:
2328 return ret;
2329}
2330
2331void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2332{
2333 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
Krzysztof Helt8503df62007-10-16 01:29:08 -07002335 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336}
2337
2338static struct zorro_driver cirrusfb_zorro_driver = {
2339 .name = "cirrusfb",
2340 .id_table = cirrusfb_zorro_table,
2341 .probe = cirrusfb_zorro_register,
2342 .remove = __devexit_p(cirrusfb_zorro_unregister),
2343};
2344#endif /* CONFIG_ZORRO */
2345
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002347static int __init cirrusfb_setup(char *options)
2348{
Vlada Pericee119402008-11-19 15:36:45 -08002349 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 if (!options || !*options)
2352 return 0;
2353
Krzysztof Helt8503df62007-10-16 01:29:08 -07002354 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002355 if (!*this_opt)
2356 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 if (!strcmp(this_opt, "noaccel"))
2359 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002360 else if (!strncmp(this_opt, "mode:", 5))
2361 mode_option = this_opt + 5;
2362 else
2363 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 }
2365 return 0;
2366}
2367#endif
2368
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369 /*
2370 * Modularization
2371 */
2372
2373MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2374MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2375MODULE_LICENSE("GPL");
2376
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002377static int __init cirrusfb_init(void)
2378{
2379 int error = 0;
2380
2381#ifndef MODULE
2382 char *option = NULL;
2383
2384 if (fb_get_options("cirrusfb", &option))
2385 return -ENODEV;
2386 cirrusfb_setup(option);
2387#endif
2388
2389#ifdef CONFIG_ZORRO
2390 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2391#endif
2392#ifdef CONFIG_PCI
2393 error |= pci_register_driver(&cirrusfb_pci_driver);
2394#endif
2395 return error;
2396}
2397
Krzysztof Helt8503df62007-10-16 01:29:08 -07002398static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399{
2400#ifdef CONFIG_PCI
2401 pci_unregister_driver(&cirrusfb_pci_driver);
2402#endif
2403#ifdef CONFIG_ZORRO
2404 zorro_unregister_driver(&cirrusfb_zorro_driver);
2405#endif
2406}
2407
2408module_init(cirrusfb_init);
2409
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002410module_param(mode_option, charp, 0);
2411MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002412module_param(noaccel, bool, 0);
2413MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002414
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415#ifdef MODULE
2416module_exit(cirrusfb_exit);
2417#endif
2418
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419/**********************************************************************/
2420/* about the following functions - I have used the same names for the */
2421/* functions as Markus Wild did in his Retina driver for NetBSD as */
2422/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002423/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424/**********************************************************************/
2425
2426/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428 int regnum, unsigned char val)
2429{
2430 unsigned long regofs = 0;
2431
2432 if (cinfo->btype == BT_PICASSO) {
2433 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002434/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2435 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2437 regofs = 0xfff;
2438 }
2439
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441}
2442
2443/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
2446 unsigned long regofs = 0;
2447
2448 if (cinfo->btype == BT_PICASSO) {
2449 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002450/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2451 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2453 regofs = 0xfff;
2454 }
2455
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457}
2458
2459/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 /* if we're just in "write value" mode, write back the */
2466 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467 vga_w(cinfo->regbase, VGA_ATT_IW,
2468 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469 }
2470 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2472 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473
2474 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002475 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476}
2477
2478/*** WHDR() - write into the Hidden DAC register ***/
2479/* as the HDR is the only extension register that requires special treatment
2480 * (the other extension registers are accessible just like the "ordinary"
2481 * registers of their functional group) here is a specialized routine for
2482 * accessing the HDR
2483 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002484static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485{
2486 unsigned char dummy;
2487
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002488 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002489 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 if (cinfo->btype == BT_PICASSO) {
2491 /* Klaus' hint for correct access to HDR on some boards */
2492 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002493 WGen(cinfo, VGA_PEL_MSK, 0x00);
2494 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496 dummy = RGen(cinfo, VGA_PEL_IW);
2497 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 }
2499 /* now do the usual stuff to access the HDR */
2500
Krzysztof Helt8503df62007-10-16 01:29:08 -07002501 dummy = RGen(cinfo, VGA_PEL_MSK);
2502 udelay(200);
2503 dummy = RGen(cinfo, VGA_PEL_MSK);
2504 udelay(200);
2505 dummy = RGen(cinfo, VGA_PEL_MSK);
2506 udelay(200);
2507 dummy = RGen(cinfo, VGA_PEL_MSK);
2508 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510 WGen(cinfo, VGA_PEL_MSK, val);
2511 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512
2513 if (cinfo->btype == BT_PICASSO) {
2514 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515 dummy = RGen(cinfo, VGA_PEL_IW);
2516 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517
2518 /* and at the end, restore the mask value */
2519 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520 WGen(cinfo, VGA_PEL_MSK, 0xff);
2521 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002522 }
2523}
2524
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002526static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002527{
2528#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002531 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532#endif
2533}
2534
2535/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537{
2538#ifdef CONFIG_ZORRO
2539 /* writing an arbitrary value to this one causes the monitor switcher */
2540 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544#endif
2545}
2546
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 unsigned char green, unsigned char blue)
2550{
2551 unsigned int data = VGA_PEL_D;
2552
2553 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555
2556 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002557 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002558 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 /* but DAC data register IS, at least for Picasso II */
2560 if (cinfo->btype == BT_PICASSO)
2561 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002562 vga_w(cinfo->regbase, data, red);
2563 vga_w(cinfo->regbase, data, green);
2564 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 vga_w(cinfo->regbase, data, blue);
2567 vga_w(cinfo->regbase, data, green);
2568 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 }
2570}
2571
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572#if 0
2573/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 unsigned char *green, unsigned char *blue)
2576{
2577 unsigned int data = VGA_PEL_D;
2578
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580
2581 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2582 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2583 if (cinfo->btype == BT_PICASSO)
2584 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002585 *red = vga_r(cinfo->regbase, data);
2586 *green = vga_r(cinfo->regbase, data);
2587 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002589 *blue = vga_r(cinfo->regbase, data);
2590 *green = vga_r(cinfo->regbase, data);
2591 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592 }
2593}
2594#endif
2595
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596/*******************************************************************
2597 cirrusfb_WaitBLT()
2598
2599 Wait for the BitBLT engine to complete a possible earlier job
2600*********************************************************************/
2601
2602/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002603static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002605 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002606 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607}
2608
2609/*******************************************************************
2610 cirrusfb_BitBLT()
2611
2612 perform accelerated "scrolling"
2613********************************************************************/
2614
Krzysztof Helt8343c892009-03-31 15:25:11 -07002615static void cirrusfb_set_blitter(u8 __iomem *regbase,
2616 u_short nwidth, u_short nheight,
2617 u_long nsrc, u_long ndest,
2618 u_short bltmode, u_short line_length)
2619
Linus Torvalds1da177e2005-04-16 15:20:36 -07002620{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002622 /* dest pitch low */
2623 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2624 /* dest pitch hi */
2625 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2626 /* source pitch low */
2627 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2628 /* source pitch hi */
2629 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630
2631 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002632 /* BLT width low */
2633 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2634 /* BLT width hi */
2635 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636
2637 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002638 /* BLT height low */
2639 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2640 /* BLT width hi */
2641 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002644 /* BLT dest low */
2645 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2646 /* BLT dest mid */
2647 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2648 /* BLT dest hi */
2649 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
2651 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002652 /* BLT src low */
2653 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2654 /* BLT src mid */
2655 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2656 /* BLT src hi */
2657 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
2659 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002660 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664
2665 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002666 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002667}
2668
2669/*******************************************************************
2670 cirrusfb_BitBLT()
2671
2672 perform accelerated "scrolling"
2673********************************************************************/
2674
2675static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2676 u_short curx, u_short cury,
2677 u_short destx, u_short desty,
2678 u_short width, u_short height,
2679 u_short line_length)
2680{
2681 u_short nwidth = width - 1;
2682 u_short nheight = height - 1;
2683 u_long nsrc, ndest;
2684 u_char bltmode;
2685
2686 bltmode = 0x00;
2687 /* if source adr < dest addr, do the Blt backwards */
2688 if (cury <= desty) {
2689 if (cury == desty) {
2690 /* if src and dest are on the same line, check x */
2691 if (curx < destx)
2692 bltmode |= 0x01;
2693 } else
2694 bltmode |= 0x01;
2695 }
2696 /* standard case: forward blitting */
2697 nsrc = (cury * line_length) + curx;
2698 ndest = (desty * line_length) + destx;
2699 if (bltmode) {
2700 /* this means start addresses are at the end,
2701 * counting backwards
2702 */
2703 nsrc += nheight * line_length + nwidth;
2704 ndest += nheight * line_length + nwidth;
2705 }
2706
2707 cirrusfb_WaitBLT(regbase);
2708
2709 cirrusfb_set_blitter(regbase, nwidth, nheight,
2710 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711}
2712
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713/*******************************************************************
2714 cirrusfb_RectFill()
2715
2716 perform accelerated rectangle fill
2717********************************************************************/
2718
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002721 u32 fg_color, u32 bg_color, u_short line_length,
2722 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002724 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725 u_char op;
2726
Krzysztof Helt8503df62007-10-16 01:29:08 -07002727 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729 /* This is a ColorExpand Blt, using the */
2730 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002731 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2732 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733
Krzysztof Helt9e848062009-03-31 15:25:11 -07002734 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002735 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002736 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2737 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2738 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002739 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002740 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002741 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2742 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002743 op = 0xa0;
2744 }
2745 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002746 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2747 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2748 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002750 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002751 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752}
2753
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754/**************************************************************************
2755 * bestclock() - determine closest possible clock lower(?) than the
2756 * desired pixel clock
2757 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002758static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002760 int n, d;
2761 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002762
Krzysztof Helt8503df62007-10-16 01:29:08 -07002763 assert(nom != NULL);
2764 assert(den != NULL);
2765 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766
2767 *nom = 0;
2768 *den = 0;
2769 *div = 0;
2770
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771 if (freq < 8000)
2772 freq = 8000;
2773
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002774 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775
2776 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002777 int s = 0;
2778
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002779 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002781 int temp = d;
2782
2783 if (temp > 31) {
2784 s = 1;
2785 temp >>= 1;
2786 }
2787 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002788 h = h > freq ? h - freq : freq - h;
2789 if (h < diff) {
2790 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002792 *den = temp;
2793 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 }
2795 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002796 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002798 if (d > 31) {
2799 s = 1;
2800 d >>= 1;
2801 }
2802 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002803 h = h > freq ? h - freq : freq - h;
2804 if (h < diff) {
2805 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002807 *den = d;
2808 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 }
2810 }
2811 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812}
2813
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814/* -------------------------------------------------------------------------
2815 *
2816 * debugging functions
2817 *
2818 * -------------------------------------------------------------------------
2819 */
2820
2821#ifdef CIRRUSFB_DEBUG
2822
2823/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 * cirrusfb_dbg_print_regs
2825 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2826 * @reg_class: type of registers to read: %CRT, or %SEQ
2827 *
2828 * DESCRIPTION:
2829 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2830 * old-style I/O ports are queried for information, otherwise MMIO is
2831 * used at the given @base address to query the information.
2832 */
2833
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002834static void cirrusfb_dbg_print_regs(struct fb_info *info,
2835 caddr_t regbase,
2836 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837{
2838 va_list list;
2839 unsigned char val = 0;
2840 unsigned reg;
2841 char *name;
2842
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002847 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848
2849 switch (reg_class) {
2850 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002851 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 break;
2853 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855 break;
2856 default:
2857 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002858 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 break;
2860 }
2861
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002862 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 }
2866
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868}
2869
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 * cirrusfb_dbg_reg_dump
2872 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2873 *
2874 * DESCRIPTION:
2875 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2876 * old-style I/O ports are queried for information, otherwise MMIO is
2877 * used at the given @base address to query the information.
2878 */
2879
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002880static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002881{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002882 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002884 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885 "CR00", 0x00,
2886 "CR01", 0x01,
2887 "CR02", 0x02,
2888 "CR03", 0x03,
2889 "CR04", 0x04,
2890 "CR05", 0x05,
2891 "CR06", 0x06,
2892 "CR07", 0x07,
2893 "CR08", 0x08,
2894 "CR09", 0x09,
2895 "CR0A", 0x0A,
2896 "CR0B", 0x0B,
2897 "CR0C", 0x0C,
2898 "CR0D", 0x0D,
2899 "CR0E", 0x0E,
2900 "CR0F", 0x0F,
2901 "CR10", 0x10,
2902 "CR11", 0x11,
2903 "CR12", 0x12,
2904 "CR13", 0x13,
2905 "CR14", 0x14,
2906 "CR15", 0x15,
2907 "CR16", 0x16,
2908 "CR17", 0x17,
2909 "CR18", 0x18,
2910 "CR22", 0x22,
2911 "CR24", 0x24,
2912 "CR26", 0x26,
2913 "CR2D", 0x2D,
2914 "CR2E", 0x2E,
2915 "CR2F", 0x2F,
2916 "CR30", 0x30,
2917 "CR31", 0x31,
2918 "CR32", 0x32,
2919 "CR33", 0x33,
2920 "CR34", 0x34,
2921 "CR35", 0x35,
2922 "CR36", 0x36,
2923 "CR37", 0x37,
2924 "CR38", 0x38,
2925 "CR39", 0x39,
2926 "CR3A", 0x3A,
2927 "CR3B", 0x3B,
2928 "CR3C", 0x3C,
2929 "CR3D", 0x3D,
2930 "CR3E", 0x3E,
2931 "CR3F", 0x3F,
2932 NULL);
2933
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002934 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002936 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002938 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 "SR00", 0x00,
2940 "SR01", 0x01,
2941 "SR02", 0x02,
2942 "SR03", 0x03,
2943 "SR04", 0x04,
2944 "SR08", 0x08,
2945 "SR09", 0x09,
2946 "SR0A", 0x0A,
2947 "SR0B", 0x0B,
2948 "SR0D", 0x0D,
2949 "SR10", 0x10,
2950 "SR11", 0x11,
2951 "SR12", 0x12,
2952 "SR13", 0x13,
2953 "SR14", 0x14,
2954 "SR15", 0x15,
2955 "SR16", 0x16,
2956 "SR17", 0x17,
2957 "SR18", 0x18,
2958 "SR19", 0x19,
2959 "SR1A", 0x1A,
2960 "SR1B", 0x1B,
2961 "SR1C", 0x1C,
2962 "SR1D", 0x1D,
2963 "SR1E", 0x1E,
2964 "SR1F", 0x1F,
2965 NULL);
2966
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002967 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968}
2969
2970#endif /* CIRRUSFB_DEBUG */
2971