blob: 9e52db7d33311c386910164b31ff73b8fd2f84e6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
105 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700106};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108/*
109 * per-board-type information, used for enumerating and abstracting
110 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700111 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 * use direct indexing on this array
113 * NOTE: '__initdata' cannot be used as some of this info
114 * is required at runtime. Maybe separate into an init-only and
115 * a run-time table?
116 */
117static const struct cirrusfb_board_info_rec {
118 char *name; /* ASCII name of chipset */
119 long maxclock[5]; /* maximum video clock */
120 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700121 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
122 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700123 /* construct bit 19 of screen start address */
124 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125
126 /* initial SR07 value, then for each mode */
127 unsigned char sr07;
128 unsigned char sr07_1bpp;
129 unsigned char sr07_1bpp_mux;
130 unsigned char sr07_8bpp;
131 unsigned char sr07_8bpp_mux;
132
133 unsigned char sr1f; /* SR1F VGA initial register value */
134} cirrusfb_board_info[] = {
135 [BT_SD64] = {
136 .name = "CL SD64",
137 .maxclock = {
138 /* guess */
139 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700140 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700142 .init_sr07 = true,
143 .init_sr1f = true,
144 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 .sr07 = 0xF0,
146 .sr07_1bpp = 0xF0,
147 .sr07_8bpp = 0xF1,
148 .sr1f = 0x20
149 },
150 [BT_PICCOLO] = {
151 .name = "CL Piccolo",
152 .maxclock = {
153 /* guess */
154 90000, 90000, 90000, 90000, 90000
155 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700156 .init_sr07 = true,
157 .init_sr1f = true,
158 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 .sr07 = 0x80,
160 .sr07_1bpp = 0x80,
161 .sr07_8bpp = 0x81,
162 .sr1f = 0x22
163 },
164 [BT_PICASSO] = {
165 .name = "CL Picasso",
166 .maxclock = {
167 /* guess */
168 90000, 90000, 90000, 90000, 90000
169 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700170 .init_sr07 = true,
171 .init_sr1f = true,
172 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 .sr07 = 0x20,
174 .sr07_1bpp = 0x20,
175 .sr07_8bpp = 0x21,
176 .sr1f = 0x22
177 },
178 [BT_SPECTRUM] = {
179 .name = "CL Spectrum",
180 .maxclock = {
181 /* guess */
182 90000, 90000, 90000, 90000, 90000
183 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700184 .init_sr07 = true,
185 .init_sr1f = true,
186 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 .sr07 = 0x80,
188 .sr07_1bpp = 0x80,
189 .sr07_8bpp = 0x81,
190 .sr1f = 0x22
191 },
192 [BT_PICASSO4] = {
193 .name = "CL Picasso4",
194 .maxclock = {
195 135100, 135100, 85500, 85500, 0
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = false,
199 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x20,
201 .sr07_1bpp = 0x20,
202 .sr07_8bpp = 0x21,
203 .sr1f = 0
204 },
205 [BT_ALPINE] = {
206 .name = "CL Alpine",
207 .maxclock = {
208 /* for the GD5430. GD5446 can do more... */
209 85500, 85500, 50000, 28500, 0
210 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700211 .init_sr07 = true,
212 .init_sr1f = true,
213 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 .sr07 = 0xA0,
215 .sr07_1bpp = 0xA1,
216 .sr07_1bpp_mux = 0xA7,
217 .sr07_8bpp = 0xA1,
218 .sr07_8bpp_mux = 0xA7,
219 .sr1f = 0x1C
220 },
221 [BT_GD5480] = {
222 .name = "CL GD5480",
223 .maxclock = {
224 135100, 200000, 200000, 135100, 135100
225 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700226 .init_sr07 = true,
227 .init_sr1f = true,
228 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 .sr07 = 0x10,
230 .sr07_1bpp = 0x11,
231 .sr07_8bpp = 0x11,
232 .sr1f = 0x1C
233 },
234 [BT_LAGUNA] = {
235 .name = "CL Laguna",
236 .maxclock = {
237 /* guess */
238 135100, 135100, 135100, 135100, 135100,
239 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700240 .init_sr07 = false,
241 .init_sr1f = false,
242 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 }
244};
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246#ifdef CONFIG_PCI
247#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000248 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700251 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
252 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
253 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
254 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
255 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
256 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
257 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
258 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
259 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
260 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
261 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 { 0, }
263};
264MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
265#undef CHIP
266#endif /* CONFIG_PCI */
267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268#ifdef CONFIG_ZORRO
269static const struct zorro_device_id cirrusfb_zorro_table[] = {
270 {
271 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
272 .driver_data = BT_SD64,
273 }, {
274 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
275 .driver_data = BT_PICCOLO,
276 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700277 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278 .driver_data = BT_PICASSO,
279 }, {
280 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
281 .driver_data = BT_SPECTRUM,
282 }, {
283 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
284 .driver_data = BT_PICASSO4,
285 },
286 { 0 }
287};
288
289static const struct {
290 zorro_id id2;
291 unsigned long size;
292} cirrusfb_zorro_table2[] = {
293 [BT_SD64] = {
294 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
295 .size = 0x400000
296 },
297 [BT_PICCOLO] = {
298 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
299 .size = 0x200000
300 },
301 [BT_PICASSO] = {
302 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
303 .size = 0x200000
304 },
305 [BT_SPECTRUM] = {
306 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
307 .size = 0x200000
308 },
309 [BT_PICASSO4] = {
310 .id2 = 0,
311 .size = 0x400000
312 }
313};
314#endif /* CONFIG_ZORRO */
315
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700317enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700318 CRT,
319 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700320};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700321#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323/* info about board */
324struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700326 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700327 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 unsigned char SFR; /* Shadow of special function register */
329
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700330 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700332 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700334 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335};
336
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700337static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700338static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340/****************************************************************************/
341/**** BEGIN PROTOTYPES ******************************************************/
342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700344static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
345 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700348static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700349static void switch_monitor(struct cirrusfb_info *cinfo, int on);
350static void WGen(const struct cirrusfb_info *cinfo,
351 int regnum, unsigned char val);
352static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
353static void AttrOn(const struct cirrusfb_info *cinfo);
354static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
355static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
356static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
357static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
358 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
361 unsigned char *red, unsigned char *green,
362 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700364static void cirrusfb_WaitBLT(u8 __iomem *regbase);
365static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
366 u_short curx, u_short cury,
367 u_short destx, u_short desty,
368 u_short width, u_short height,
369 u_short line_length);
370static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
371 u_short x, u_short y,
372 u_short width, u_short height,
373 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700375static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700378static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
379static void cirrusfb_dbg_print_regs(struct fb_info *info,
380 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700381 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382#endif /* CIRRUSFB_DEBUG */
383
384/*** END PROTOTYPES ********************************************************/
385/*****************************************************************************/
386/*** BEGIN Interface Used by the World ***************************************/
387
Krzysztof Helt8503df62007-10-16 01:29:08 -0700388static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
390/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700391static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392{
393 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700394 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 return 0;
396}
397
398/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700399static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
401 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 return 0;
404}
405
406/**** END Interface used by the World *************************************/
407/****************************************************************************/
408/**** BEGIN Hardware specific Routines **************************************/
409
Krzysztof Helt486ff382008-10-15 22:03:42 -0700410/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700411static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700413 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700414 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
Krzysztof Helt486ff382008-10-15 22:03:42 -0700416 /* Read MCLK value */
417 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700418 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700421 * should divide it by to get VCLK
422 */
423
424 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700425 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700426 return 1;
427 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700428 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 }
431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433}
434
435static int cirrusfb_check_var(struct fb_var_screeninfo *var,
436 struct fb_info *info)
437{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700438 int yres;
439 /* memory size in pixels */
440 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700443 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 var->red.offset = 0;
445 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700446 var->green = var->red;
447 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 break;
449
450 case 8:
451 var->red.offset = 0;
452 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700453 var->green = var->red;
454 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 break;
456
457 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700458 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 var->red.offset = 2;
460 var->green.offset = -3;
461 var->blue.offset = 8;
462 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700463 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 var->green.offset = 5;
465 var->blue.offset = 0;
466 }
467 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700468 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 var->blue.length = 5;
470 break;
471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700473 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 var->red.offset = 8;
475 var->green.offset = 16;
476 var->blue.offset = 24;
477 } else {
478 var->red.offset = 16;
479 var->green.offset = 8;
480 var->blue.offset = 0;
481 }
482 var->red.length = 8;
483 var->green.length = 8;
484 var->blue.length = 8;
485 break;
486
487 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700488 dev_dbg(info->device,
489 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700490 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 /* should never occur */
492 break;
493 }
494
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700495 if (var->xres_virtual < var->xres)
496 var->xres_virtual = var->xres;
497 /* use highest possible virtual resolution */
498 if (var->yres_virtual == -1) {
499 var->yres_virtual = pixels / var->xres_virtual;
500
501 dev_info(info->device,
502 "virtual resolution set to maximum of %dx%d\n",
503 var->xres_virtual, var->yres_virtual);
504 }
505 if (var->yres_virtual < var->yres)
506 var->yres_virtual = var->yres;
507
508 if (var->xres_virtual * var->yres_virtual > pixels) {
509 dev_err(info->device, "mode %dx%dx%d rejected... "
510 "virtual resolution too high to fit into video memory!\n",
511 var->xres_virtual, var->yres_virtual,
512 var->bits_per_pixel);
513 return -EINVAL;
514 }
515
516
517 if (var->xoffset < 0)
518 var->xoffset = 0;
519 if (var->yoffset < 0)
520 var->yoffset = 0;
521
522 /* truncate xoffset and yoffset to maximum if too high */
523 if (var->xoffset > var->xres_virtual - var->xres)
524 var->xoffset = var->xres_virtual - var->xres - 1;
525 if (var->yoffset > var->yres_virtual - var->yres)
526 var->yoffset = var->yres_virtual - var->yres - 1;
527
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 var->red.msb_right =
529 var->green.msb_right =
530 var->blue.msb_right =
531 var->transp.offset =
532 var->transp.length =
533 var->transp.msb_right = 0;
534
535 yres = var->yres;
536 if (var->vmode & FB_VMODE_DOUBLE)
537 yres *= 2;
538 else if (var->vmode & FB_VMODE_INTERLACED)
539 yres = (yres + 1) / 2;
540
541 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700542 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700543 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 return -EINVAL;
545 }
546
547 return 0;
548}
549
Krzysztof Helt8503df62007-10-16 01:29:08 -0700550static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700551 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
553 long freq;
554 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700555 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557
Krzysztof Helt8503df62007-10-16 01:29:08 -0700558 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700560 info->fix.line_length = var->xres_virtual / 8;
561 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 break;
563
564 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700565 info->fix.line_length = var->xres_virtual;
566 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 break;
568
569 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700571 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700572 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 break;
574
575 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700576 dev_dbg(info->device,
577 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700578 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 /* should never occur */
580 break;
581 }
582
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700583 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700586 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700588 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700591 cinfo->multiplexing = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593 /* If the frequency is greater than we can support, we might be able
594 * to use multiplexing for the video mode */
595 if (freq > maxclock) {
596 switch (cinfo->btype) {
597 case BT_ALPINE:
598 case BT_GD5480:
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700599 cinfo->multiplexing = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 break;
601
602 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700603 dev_err(info->device,
604 "Frequency greater than maxclock (%ld kHz)\n",
605 maxclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return -EINVAL;
607 }
608 }
609#if 0
610 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
611 * the VCLK is double the pixel clock. */
612 switch (var->bits_per_pixel) {
613 case 16:
614 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700615 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700616 /* Xbh has this type of clock for 32-bit */
617 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 break;
619 }
620#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 return 0;
622}
623
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700624static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700626 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700627 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700628
Krzysztof Helt8503df62007-10-16 01:29:08 -0700629 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700630 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
Krzysztof Helt486ff382008-10-15 22:03:42 -0700632 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700633 dev_dbg(info->device, "Set %s as pixclock source.\n",
634 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700635 old1f |= 0x40;
636 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
637 if (div == 2)
638 old1e |= 1;
639
640 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700642 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
645/*************************************************************************
646 cirrusfb_set_par_foo()
647
648 actually writes the values for a new video mode into the hardware,
649**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700650static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
652 struct cirrusfb_info *cinfo = info->par;
653 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 u8 __iomem *regbase = cinfo->regbase;
655 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700656 int err;
657 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700659 int hdispend, hsyncstart, hsyncend, htotal;
660 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700661 long freq;
662 int nom, den, div;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700663 unsigned int control, format, threshold;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700665 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700667 dev_dbg(info->device, "pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700669 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700671 err = cirrusfb_decode_var(var, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700672 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700674 dev_dbg(info->device, "mode change aborted. invalid var.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 return -EINVAL;
676 }
677
678 bi = &cirrusfb_board_info[cinfo->btype];
679
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700680 hsyncstart = var->xres + var->right_margin;
681 hsyncend = hsyncstart + var->hsync_len;
682 htotal = (hsyncend + var->left_margin) / 8 - 5;
683 hdispend = var->xres / 8 - 1;
684 hsyncstart = hsyncstart / 8 + 1;
685 hsyncend = hsyncend / 8 + 1;
686
687 yres = var->yres;
688 vsyncstart = yres + var->lower_margin;
689 vsyncend = vsyncstart + var->vsync_len;
690 vtotal = vsyncend + var->upper_margin;
691 vdispend = yres - 1;
692
693 if (var->vmode & FB_VMODE_DOUBLE) {
694 yres *= 2;
695 vsyncstart *= 2;
696 vsyncend *= 2;
697 vtotal *= 2;
698 } else if (var->vmode & FB_VMODE_INTERLACED) {
699 yres = (yres + 1) / 2;
700 vsyncstart = (vsyncstart + 1) / 2;
701 vsyncend = (vsyncend + 1) / 2;
702 vtotal = (vtotal + 1) / 2;
703 }
704
705 vtotal -= 2;
706 vsyncstart -= 1;
707 vsyncend -= 1;
708
709 if (yres >= 1024) {
710 vtotal /= 2;
711 vsyncstart /= 2;
712 vsyncend /= 2;
713 vdispend /= 2;
714 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700715 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700716 htotal /= 2;
717 hsyncstart /= 2;
718 hsyncend /= 2;
719 hdispend /= 2;
720 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700722 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723
724 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700725 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700726 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700728 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700729 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700731 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700732 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Krzysztof Helt8503df62007-10-16 01:29:08 -0700734 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700735 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700736 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700737 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700739 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700740 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 tmp = hsyncend % 32;
743 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700745 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700746 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700748 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700749 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700766 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700767 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
769 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700770 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 tmp |= 0x20;
772 if (var->vmode & FB_VMODE_DOUBLE)
773 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700775 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700780 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700783 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700789 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700792 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700793 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
795 tmp = 0;
796 if (var->vmode & FB_VMODE_INTERLACED)
797 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 tmp |= 128;
806
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700807 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700808 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700810 freq = PICOS2KHZ(var->pixclock);
811 bestclock(freq, &nom, &den, &div);
812
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700813 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
814 freq, nom, den, div);
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 /* set VCLK0 */
817 /* hardware RefClock: 14.31818 MHz */
818 /* formula: VClk = (OSC * N) / (D * (1+P)) */
819 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
820
Krzysztof Helt486ff382008-10-15 22:03:42 -0700821 if (cinfo->btype == BT_ALPINE) {
822 /* if freq is close to mclk or mclk/2 select mclk
823 * as clock source
824 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700825 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700826 if (divMCLK) {
827 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700828 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700829 }
830 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700831 if (cinfo->btype == BT_LAGUNA) {
832 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
833 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
834 unsigned short tile_control;
835
836 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
837 fb_writew(tile_control & ~0x80, cinfo->laguna_mmio + 0x2c4);
838
839 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
840 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
841 control = fb_readw(cinfo->laguna_mmio + 0x402);
842 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
843 control &= ~0x6800;
844 format = 0;
845 threshold &= 0xffe0;
846 threshold &= 0x3fbf;
847 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700848 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700849 tmp = den << 1;
850 if (div != 0)
851 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700852 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
853 if ((cinfo->btype == BT_SD64) ||
854 (cinfo->btype == BT_ALPINE) ||
855 (cinfo->btype == BT_GD5480))
856 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700858 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
859 /* Laguna chipset has reversed clock registers */
860 if (cinfo->btype == BT_LAGUNA) {
861 vga_wseq(regbase, CL_SEQRE, tmp);
862 vga_wseq(regbase, CL_SEQR1E, nom);
863 } else {
864 vga_wseq(regbase, CL_SEQRB, nom);
865 vga_wseq(regbase, CL_SEQR1B, tmp);
866 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700867 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700869 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700871 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 else
873 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
874 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700875 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Krzysztof Helt8503df62007-10-16 01:29:08 -0700877/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
878 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879
880 /* don't know if it would hurt to also program this if no interlaced */
881 /* mode is used, but I feel better this way.. :-) */
882 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700883 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700885 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
Krzysztof Helt8503df62007-10-16 01:29:08 -0700887 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700890 /* enable display memory & CRTC I/O address for color mode */
891 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
893 tmp |= 0x40;
894 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
895 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Krzysztof Helt8503df62007-10-16 01:29:08 -0700898 /* Screen A Preset Row-Scan register */
899 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
900 /* text cursor on and start line */
901 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
902 /* text cursor end line */
903 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905 /******************************************************
906 *
907 * 1 bpp
908 *
909 */
910
911 /* programming for different color depths */
912 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700913 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700914 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 /* SR07 */
917 switch (cinfo->btype) {
918 case BT_SD64:
919 case BT_PICCOLO:
920 case BT_PICASSO:
921 case BT_SPECTRUM:
922 case BT_PICASSO4:
923 case BT_ALPINE:
924 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700926 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 bi->sr07_1bpp_mux : bi->sr07_1bpp);
928 break;
929
930 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 vga_wseq(regbase, CL_SEQR7,
932 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 break;
934
935 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700936 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 break;
938 }
939
940 /* Extended Sequencer Mode */
941 switch (cinfo->btype) {
942 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 /* setting the SEQRF on SD64 is not necessary
944 * (only during init)
945 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 /* MCLK select */
947 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 break;
949
950 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700951 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 /* ### ueberall 0x22? */
953 /* ##vorher 1c MCLK select */
954 vga_wseq(regbase, CL_SEQR1F, 0x22);
955 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
956 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 break;
958
959 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700960 /* ##vorher 22 MCLK select */
961 vga_wseq(regbase, CL_SEQR1F, 0x22);
962 /* ## vorher d0 avoid FIFO underruns..? */
963 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 break;
965
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 case BT_PICASSO4:
967 case BT_ALPINE:
968 case BT_GD5480:
969 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 /* do nothing */
971 break;
972
973 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700974 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 break;
976 }
977
Krzysztof Helt8503df62007-10-16 01:29:08 -0700978 /* pixel mask: pass-through for first plane */
979 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700980 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700981 /* hidden dac reg: 1280x1024 */
982 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 /* hidden dac: nothing */
985 WHDR(cinfo, 0);
986 /* memory mode: odd/even, ext. memory */
987 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
988 /* plane mask: only write to first plane */
989 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 }
991
992 /******************************************************
993 *
994 * 8 bpp
995 *
996 */
997
998 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700999 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 switch (cinfo->btype) {
1001 case BT_SD64:
1002 case BT_PICCOLO:
1003 case BT_PICASSO:
1004 case BT_SPECTRUM:
1005 case BT_PICASSO4:
1006 case BT_ALPINE:
1007 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001008 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001009 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1011 break;
1012
1013 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001014 vga_wseq(regbase, CL_SEQR7,
1015 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001016 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 break;
1018
1019 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001020 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 break;
1022 }
1023
1024 switch (cinfo->btype) {
1025 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001026 /* MCLK select */
1027 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 break;
1029
1030 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001033 /* ### vorher 1c MCLK select */
1034 vga_wseq(regbase, CL_SEQR1F, 0x22);
1035 /* Fast Page-Mode writes */
1036 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 break;
1038
1039 case BT_PICASSO4:
1040#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001041 /* ### INCOMPLETE!! */
1042 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001044/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 break;
1046
1047 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 /* We already set SRF and SR1F */
1049 break;
1050
1051 case BT_GD5480:
1052 case BT_LAGUNA:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 /* do nothing */
1054 break;
1055
1056 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001057 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058 break;
1059 }
1060
Krzysztof Helt8503df62007-10-16 01:29:08 -07001061 /* mode register: 256 color mode */
1062 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001063 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 /* hidden dac reg: 1280x1024 */
1065 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* hidden dac: nothing */
1068 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 }
1070
1071 /******************************************************
1072 *
1073 * 16 bpp
1074 *
1075 */
1076
1077 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001078 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 switch (cinfo->btype) {
1080 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001081 /* Extended Sequencer Mode: 256c col. mode */
1082 vga_wseq(regbase, CL_SEQR7, 0xf7);
1083 /* MCLK select */
1084 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 break;
1086
1087 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001088 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001089 vga_wseq(regbase, CL_SEQR7, 0x87);
1090 /* Fast Page-Mode writes */
1091 vga_wseq(regbase, CL_SEQRF, 0xb0);
1092 /* MCLK select */
1093 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 break;
1095
1096 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001097 vga_wseq(regbase, CL_SEQR7, 0x27);
1098 /* Fast Page-Mode writes */
1099 vga_wseq(regbase, CL_SEQRF, 0xb0);
1100 /* MCLK select */
1101 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 break;
1103
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001105 vga_wseq(regbase, CL_SEQR7, 0x27);
1106/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 break;
1108
1109 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001110 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 break;
1112
1113 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001114 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 /* We already set SRF and SR1F */
1116 break;
1117
1118 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001119 vga_wseq(regbase, CL_SEQR7,
1120 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001121 control |= 0x2000;
1122 format |= 0x1400;
1123 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 break;
1125
1126 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001127 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 break;
1129 }
1130
Krzysztof Helt8503df62007-10-16 01:29:08 -07001131 /* mode register: 256 color mode */
1132 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001134 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135#elif defined(CONFIG_ZORRO)
1136 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 }
1140
1141 /******************************************************
1142 *
1143 * 32 bpp
1144 *
1145 */
1146
1147 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001148 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 switch (cinfo->btype) {
1150 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 /* Extended Sequencer Mode: 256c col. mode */
1152 vga_wseq(regbase, CL_SEQR7, 0xf9);
1153 /* MCLK select */
1154 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
1157 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001158 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 vga_wseq(regbase, CL_SEQR7, 0x85);
1160 /* Fast Page-Mode writes */
1161 vga_wseq(regbase, CL_SEQRF, 0xb0);
1162 /* MCLK select */
1163 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165
1166 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 vga_wseq(regbase, CL_SEQR7, 0x25);
1168 /* Fast Page-Mode writes */
1169 vga_wseq(regbase, CL_SEQRF, 0xb0);
1170 /* MCLK select */
1171 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 break;
1173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 vga_wseq(regbase, CL_SEQR7, 0x25);
1176/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178
1179 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 break;
1182
1183 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 /* We already set SRF and SR1F */
1186 break;
1187
1188 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 vga_wseq(regbase, CL_SEQR7,
1190 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001191 control |= 0x6000;
1192 format |= 0x3400;
1193 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 break;
1195
1196 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001197 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 break;
1199 }
1200
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 /* mode register: 256 color mode */
1202 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001203 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1204 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 }
1206
1207 /******************************************************
1208 *
1209 * unknown/unsupported bpp
1210 *
1211 */
1212
Krzysztof Helt8503df62007-10-16 01:29:08 -07001213 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001214 dev_err(info->device,
1215 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Krzysztof Helt6683e012009-03-31 15:25:06 -07001218 pitch = info->fix.line_length >> 3;
1219 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001221 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 tmp |= 0x10; /* offset overflow bit */
1223
Krzysztof Helt8503df62007-10-16 01:29:08 -07001224 /* screen start addr #16-18, fastpagemode cycles */
1225 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001227 /* screen start address bit 19 */
1228 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001229 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001231 if (cinfo->btype == BT_LAGUNA ||
1232 cinfo->btype == BT_GD5480) {
1233
1234 tmp = 0;
1235 if ((htotal + 5) & 256)
1236 tmp |= 128;
1237 if (hdispend & 256)
1238 tmp |= 64;
1239 if (hsyncstart & 256)
1240 tmp |= 48;
1241 if (vtotal & 1024)
1242 tmp |= 8;
1243 if (vdispend & 1024)
1244 tmp |= 4;
1245 if (vsyncstart & 1024)
1246 tmp |= 3;
1247
1248 vga_wcrt(regbase, CL_CRT1E, tmp);
1249 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1250 }
1251
1252
Krzysztof Helt8503df62007-10-16 01:29:08 -07001253 /* pixel panning */
1254 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255
1256 /* [ EGS: SetOffset(); ] */
1257 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001260 if (cinfo->btype == BT_LAGUNA) {
1261 /* no tiles */
1262 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1263 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1264 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1265 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 /* finally, turn on everything - turn off "FullBandwidth" bit */
1267 /* also, set "DotClock%2" bit where requested */
1268 tmp = 0x01;
1269
1270/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1271 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1272 tmp |= 0x08;
1273*/
1274
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001276 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001279 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
1281#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001282 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283#endif
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 return 0;
1286}
1287
1288/* for some reason incomprehensible to me, cirrusfb requires that you write
1289 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001292 cirrusfb_set_par_foo(info);
1293 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294}
1295
Krzysztof Helt8503df62007-10-16 01:29:08 -07001296static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1297 unsigned blue, unsigned transp,
1298 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
1300 struct cirrusfb_info *cinfo = info->par;
1301
1302 if (regno > 255)
1303 return -EINVAL;
1304
1305 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1306 u32 v;
1307 red >>= (16 - info->var.red.length);
1308 green >>= (16 - info->var.green.length);
1309 blue >>= (16 - info->var.blue.length);
1310
Krzysztof Helt8503df62007-10-16 01:29:08 -07001311 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 return 1;
1313 v = (red << info->var.red.offset) |
1314 (green << info->var.green.offset) |
1315 (blue << info->var.blue.offset);
1316
Krzysztof Helt060b6002007-10-16 01:29:13 -07001317 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 return 0;
1319 }
1320
Krzysztof Helt8503df62007-10-16 01:29:08 -07001321 if (info->var.bits_per_pixel == 8)
1322 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 return 0;
1325
1326}
1327
1328/*************************************************************************
1329 cirrusfb_pan_display()
1330
1331 performs display panning - provided hardware permits this
1332**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001333static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1334 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335{
1336 int xoffset = 0;
1337 int yoffset = 0;
1338 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001339 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 struct cirrusfb_info *cinfo = info->par;
1341
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001342 dev_dbg(info->device,
1343 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345 /* no range checks for xoffset and yoffset, */
1346 /* as fb_pan_display has already done this */
1347 if (var->vmode & FB_VMODE_YWRAP)
1348 return -EINVAL;
1349
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1351 yoffset = var->yoffset;
1352
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001353 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
1355 if (info->var.bits_per_pixel == 1) {
1356 /* base is already correct */
1357 xpix = (unsigned char) (var->xoffset % 8);
1358 } else {
1359 base /= 4;
1360 xpix = (unsigned char) ((xoffset % 4) * 2);
1361 }
1362
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1367 (unsigned char) (base & 0xff));
1368 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1369 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001371 /* 0xf2 is %11110010, exclude tmp bits */
1372 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373 /* construct bits 16, 17 and 18 of screen start address */
1374 if (base & 0x10000)
1375 tmp |= 0x01;
1376 if (base & 0x20000)
1377 tmp |= 0x04;
1378 if (base & 0x40000)
1379 tmp |= 0x08;
1380
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001381 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382
1383 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001384 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
1385 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D) & ~0x80;
1386 tmp |= (base >> 12) & 0x80;
1387 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 /* write pixel panning value to AR33; this does not quite work in 8bpp
1391 *
1392 * ### Piccolo..? Will this work?
1393 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Krzysztof Helt8503df62007-10-16 01:29:08 -07001399 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400}
1401
Krzysztof Helt8503df62007-10-16 01:29:08 -07001402static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
1404 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1406 * then the caller blanks by setting the CLUT (Color Look Up Table)
1407 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1408 * failed due to e.g. a video mode which doesn't support it.
1409 * Implements VESA suspend and powerdown modes on hardware that
1410 * supports disabling hsync/vsync:
1411 * blank_mode == 2: suspend vsync
1412 * blank_mode == 3: suspend hsync
1413 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 */
1415 unsigned char val;
1416 struct cirrusfb_info *cinfo = info->par;
1417 int current_mode = cinfo->blank_mode;
1418
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001419 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
1421 if (info->state != FBINFO_STATE_RUNNING ||
1422 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001423 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 return 0;
1425 }
1426
1427 /* Undo current */
1428 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001430 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001431 val = 0;
1432 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001433 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001434 val = 0x20;
1435
1436 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1437 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
1439 switch (blank_mode) {
1440 case FB_BLANK_UNBLANK:
1441 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001442 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 break;
1444 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001445 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 break;
1447 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001448 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 break;
1450 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001451 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 break;
1453 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001454 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 return 1;
1456 }
1457
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001458 vga_wgfx(cinfo->regbase, CL_GRE, val);
1459
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001461 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 /* Let fbcon do a soft blank for us */
1464 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1465}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001466
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467/**** END Hardware specific Routines **************************************/
1468/****************************************************************************/
1469/**** BEGIN Internal Routines ***********************************************/
1470
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001471static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001473 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 const struct cirrusfb_board_info_rec *bi;
1475
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
1478 bi = &cirrusfb_board_info[cinfo->btype];
1479
1480 /* reset board globally */
1481 switch (cinfo->btype) {
1482 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001483 WSFR(cinfo, 0x01);
1484 udelay(500);
1485 WSFR(cinfo, 0x51);
1486 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 break;
1488 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001489 WSFR2(cinfo, 0xff);
1490 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 break;
1492 case BT_SD64:
1493 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001494 WSFR(cinfo, 0x1f);
1495 udelay(500);
1496 WSFR(cinfo, 0x4f);
1497 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 break;
1499 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500 /* disable flickerfixer */
1501 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1502 mdelay(100);
1503 /* from Klaus' NetBSD driver: */
1504 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1505 /* put blitter into 542x compat */
1506 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1507 /* mode */
1508 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 break;
1510
1511 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 /* from Klaus' NetBSD driver: */
1513 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 break;
1515
1516 case BT_ALPINE:
1517 /* Nothing to do to reset the board. */
1518 break;
1519
1520 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001521 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 break;
1523 }
1524
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001525 /* make sure RAM size set by this point */
1526 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527
1528 /* the P4 is not fully initialized here; I rely on it having been */
1529 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
1532 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001533 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1534 WGen(cinfo, CL_POS102, 0x01);
1535 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 /* reset sequencer logic */
1541 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 /* FullBandwidth (video off) and 8/9 dot clock */
1544 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1545 /* polarity (-/-), disable access to display memory,
1546 * VGA_CRTC_START_HI base address: color
1547 */
1548 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 /* "magic cookie" - doesn't make any sense to me.. */
1551/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1552 /* unlock all extension registers */
1553 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 /* reset blitter */
1556 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557
1558 switch (cinfo->btype) {
1559 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 break;
1562 case BT_ALPINE:
1563 break;
1564 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566 break;
1567 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001568 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1569 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570 break;
1571 }
1572 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001573 /* plane mask: nothing */
1574 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1575 /* character map select: doesn't even matter in gx mode */
1576 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001577 /* memory mode: chain4, ext. memory */
1578 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
1580 /* controller-internal base address of video memory */
1581 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001582 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
Krzysztof Helt8503df62007-10-16 01:29:08 -07001584 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1585 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1588 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1589 /* graphics cursor Y position (..."... ) */
1590 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1591 /* graphics cursor attributes */
1592 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1593 /* graphics cursor pattern address */
1594 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595
1596 /* writing these on a P4 might give problems.. */
1597 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001598 /* configuration readback and ext. color */
1599 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1600 /* signature generator */
1601 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 }
1603
1604 /* MCLK select etc. */
1605 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001606 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607
Krzysztof Helt8503df62007-10-16 01:29:08 -07001608 /* Screen A preset row scan: none */
1609 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1610 /* Text cursor start: disable text cursor */
1611 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1612 /* Text cursor end: - */
1613 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1614 /* Screen start address high: 0 */
1615 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1616 /* Screen start address low: 0 */
1617 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1618 /* text cursor location high: 0 */
1619 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1620 /* text cursor location low: 0 */
1621 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* Underline Row scanline: - */
1624 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1625 /* mode control: timing enable, byte mode, no compat modes */
1626 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1627 /* Line Compare: not needed */
1628 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 /* ext. display controls: ext.adr. wrap */
1631 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632
Krzysztof Helt8503df62007-10-16 01:29:08 -07001633 /* Set/Reset registes: - */
1634 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1635 /* Set/Reset enable: - */
1636 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1637 /* Color Compare: - */
1638 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1639 /* Data Rotate: - */
1640 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1641 /* Read Map Select: - */
1642 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1643 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1644 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1645 /* Miscellaneous: memory map base address, graphics mode */
1646 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1647 /* Color Don't care: involve all planes */
1648 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1649 /* Bit Mask: no mask at all */
1650 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001652 /* (5434 can't have bit 3 set for bitblt) */
1653 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001655 /* Graphics controller mode extensions: finer granularity,
1656 * 8byte data latches
1657 */
1658 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659
Krzysztof Helt8503df62007-10-16 01:29:08 -07001660 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1661 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1662 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1663 /* Background color byte 1: - */
1664 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1665 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 /* Attribute Controller palette registers: "identity mapping" */
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1675 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1676 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1677 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1678 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1679 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1680 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1681 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1682 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1683 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Krzysztof Helt8503df62007-10-16 01:29:08 -07001685 /* Attribute Controller mode: graphics mode */
1686 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1687 /* Overscan color reg.: reg. 0 */
1688 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1689 /* Color Plane enable: Enable all 4 planes */
1690 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 /* Color Select: - */
1692 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695
1696 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 /* polarity (-/-), enable display mem,
1698 * VGA_CRTC_START_HI i/o base = color
1699 */
1700 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 /* BLT Start/status: Blitter reset */
1703 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1704 /* - " - : "end-of-reset" */
1705 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
1707 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001708 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 return;
1710}
1711
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713{
1714#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1715 static int IsOn = 0; /* XXX not ok for multiple boards */
1716
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 if (cinfo->btype == BT_PICASSO4)
1718 return; /* nothing to switch */
1719 if (cinfo->btype == BT_ALPINE)
1720 return; /* nothing to switch */
1721 if (cinfo->btype == BT_GD5480)
1722 return; /* nothing to switch */
1723 if (cinfo->btype == BT_PICASSO) {
1724 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001725 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 return;
1727 }
1728 if (on) {
1729 switch (cinfo->btype) {
1730 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001731 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 break;
1733 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001734 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 break;
1736 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001737 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 break;
1739 default: /* do nothing */ break;
1740 }
1741 } else {
1742 switch (cinfo->btype) {
1743 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001744 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 break;
1746 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001747 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 break;
1749 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001751 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001752 default: /* do nothing */
1753 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 }
1755 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756#endif /* CONFIG_ZORRO */
1757}
1758
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759/******************************************/
1760/* Linux 2.6-style accelerated functions */
1761/******************************************/
1762
Krzysztof Helt8503df62007-10-16 01:29:08 -07001763static void cirrusfb_fillrect(struct fb_info *info,
1764 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766 struct fb_fillrect modded;
1767 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001768 struct cirrusfb_info *cinfo = info->par;
1769 int m = info->var.bits_per_pixel;
1770 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1771 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772
1773 if (info->state != FBINFO_STATE_RUNNING)
1774 return;
1775 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1776 cfb_fillrect(info, region);
1777 return;
1778 }
1779
1780 vxres = info->var.xres_virtual;
1781 vyres = info->var.yres_virtual;
1782
1783 memcpy(&modded, region, sizeof(struct fb_fillrect));
1784
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 modded.dx >= vxres || modded.dy >= vyres)
1787 return;
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 if (modded.dx + modded.width > vxres)
1790 modded.width = vxres - modded.dx;
1791 if (modded.dy + modded.height > vyres)
1792 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
Krzysztof Helt060b6002007-10-16 01:29:13 -07001794 cirrusfb_RectFill(cinfo->regbase,
1795 info->var.bits_per_pixel,
1796 (region->dx * m) / 8, region->dy,
1797 (region->width * m) / 8, region->height,
1798 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001799 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800}
1801
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802static void cirrusfb_copyarea(struct fb_info *info,
1803 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 struct fb_copyarea modded;
1806 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001807 struct cirrusfb_info *cinfo = info->par;
1808 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
1810 if (info->state != FBINFO_STATE_RUNNING)
1811 return;
1812 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1813 cfb_copyarea(info, area);
1814 return;
1815 }
1816
1817 vxres = info->var.xres_virtual;
1818 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001819 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820
Krzysztof Helt8503df62007-10-16 01:29:08 -07001821 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 modded.sx >= vxres || modded.sy >= vyres ||
1823 modded.dx >= vxres || modded.dy >= vyres)
1824 return;
1825
Krzysztof Helt8503df62007-10-16 01:29:08 -07001826 if (modded.sx + modded.width > vxres)
1827 modded.width = vxres - modded.sx;
1828 if (modded.dx + modded.width > vxres)
1829 modded.width = vxres - modded.dx;
1830 if (modded.sy + modded.height > vyres)
1831 modded.height = vyres - modded.sy;
1832 if (modded.dy + modded.height > vyres)
1833 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834
Krzysztof Helt060b6002007-10-16 01:29:13 -07001835 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1836 (area->sx * m) / 8, area->sy,
1837 (area->dx * m) / 8, area->dy,
1838 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001839 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001840
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841}
1842
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843static void cirrusfb_imageblit(struct fb_info *info,
1844 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845{
1846 struct cirrusfb_info *cinfo = info->par;
1847
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 cfb_imageblit(info, image);
1850}
1851
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852#ifdef CONFIG_PPC_PREP
1853#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1854#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001855static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857 *display = PREP_VIDEO_BASE;
1858 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859}
1860
1861#endif /* CONFIG_PPC_PREP */
1862
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001864static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865
1866/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1867 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1868 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1869 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001870static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1871 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872{
1873 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001874 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001876 if (cinfo->btype == BT_LAGUNA) {
1877 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1878
1879 mem = ((SR14 & 7) + 1) << 20;
1880 } else {
1881 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1882 switch ((SRF & 0x18)) {
1883 case 0x08:
1884 mem = 512 * 1024;
1885 break;
1886 case 0x10:
1887 mem = 1024 * 1024;
1888 break;
1889 /* 64-bit DRAM data bus width; assume 2MB.
1890 * Also indicates 2MB memory on the 5430.
1891 */
1892 case 0x18:
1893 mem = 2048 * 1024;
1894 break;
1895 default:
1896 dev_warn(info->device, "Unknown memory size!\n");
1897 mem = 1024 * 1024;
1898 }
1899 /* If DRAM bank switching is enabled, there must be
1900 * twice as much memory installed. (4MB on the 5434)
1901 */
1902 if (SRF & 0x80)
1903 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001905
Linus Torvalds1da177e2005-04-16 15:20:36 -07001906 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 return mem;
1908}
1909
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910static void get_pci_addrs(const struct pci_dev *pdev,
1911 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001913 assert(pdev != NULL);
1914 assert(display != NULL);
1915 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 *display = 0;
1918 *registers = 0;
1919
1920 /* This is a best-guess for now */
1921
1922 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1923 *display = pci_resource_start(pdev, 1);
1924 *registers = pci_resource_start(pdev, 0);
1925 } else {
1926 *display = pci_resource_start(pdev, 0);
1927 *registers = pci_resource_start(pdev, 1);
1928 }
1929
Krzysztof Helt8503df62007-10-16 01:29:08 -07001930 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001931}
1932
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001933static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001935 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001936 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001938 if (cinfo->laguna_mmio == NULL)
1939 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001940 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001941#if 0 /* if system didn't claim this region, we would... */
1942 release_mem_region(0xA0000, 65535);
1943#endif
1944 if (release_io_ports)
1945 release_region(0x3C0, 32);
1946 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001947}
1948#endif /* CONFIG_PCI */
1949
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001951static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952{
Al Virod91f5bb2007-10-17 00:27:18 +01001953 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001954 struct zorro_dev *zdev = to_zorro_dev(info->device);
1955
1956 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957
1958 if (cinfo->btype == BT_PICASSO4) {
1959 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001960 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001961 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001963 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001964 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966}
1967#endif /* CONFIG_ZORRO */
1968
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001969/* function table of the above functions */
1970static struct fb_ops cirrusfb_ops = {
1971 .owner = THIS_MODULE,
1972 .fb_open = cirrusfb_open,
1973 .fb_release = cirrusfb_release,
1974 .fb_setcolreg = cirrusfb_setcolreg,
1975 .fb_check_var = cirrusfb_check_var,
1976 .fb_set_par = cirrusfb_set_par,
1977 .fb_pan_display = cirrusfb_pan_display,
1978 .fb_blank = cirrusfb_blank,
1979 .fb_fillrect = cirrusfb_fillrect,
1980 .fb_copyarea = cirrusfb_copyarea,
1981 .fb_imageblit = cirrusfb_imageblit,
1982};
1983
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001984static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001986 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 struct fb_var_screeninfo *var = &info->var;
1988
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 info->pseudo_palette = cinfo->pseudo_palette;
1990 info->flags = FBINFO_DEFAULT
1991 | FBINFO_HWACCEL_XPAN
1992 | FBINFO_HWACCEL_YPAN
1993 | FBINFO_HWACCEL_FILLRECT
1994 | FBINFO_HWACCEL_COPYAREA;
1995 if (noaccel)
1996 info->flags |= FBINFO_HWACCEL_DISABLED;
1997 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 if (cinfo->btype == BT_GD5480) {
1999 if (var->bits_per_pixel == 16)
2000 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002001 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 info->screen_base += 2 * MB_;
2003 }
2004
2005 /* Fill fix common fields */
2006 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2007 sizeof(info->fix.id));
2008
2009 /* monochrome: only 1 memory plane */
2010 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002011 info->fix.smem_len = info->screen_size;
2012 if (var->bits_per_pixel == 1)
2013 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 info->fix.xpanstep = 1;
2016 info->fix.ypanstep = 1;
2017 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
2019 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 info->fix.mmio_len = 0;
2021 info->fix.accel = FB_ACCEL_NONE;
2022
2023 fb_alloc_cmap(&info->cmap, 256, 0);
2024
2025 return 0;
2026}
2027
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002028static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002030 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032
2033 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002034 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002036 /* set all the vital stuff */
2037 cirrusfb_set_fbinfo(info);
2038
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002039 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002041 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2042 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002043 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002044 err = -EINVAL;
2045 goto err_dealloc_cmap;
2046 }
2047
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 info->var.activate = FB_ACTIVATE_NOW;
2049
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002050 err = cirrusfb_decode_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051 if (err < 0) {
2052 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002053 dev_dbg(info->device,
2054 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002055 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 }
2057
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 err = register_framebuffer(info);
2059 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002060 dev_err(info->device,
2061 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 goto err_dealloc_cmap;
2063 }
2064
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065 return 0;
2066
2067err_dealloc_cmap:
2068 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002069 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002070 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 return err;
2072}
2073
Krzysztof Helt8503df62007-10-16 01:29:08 -07002074static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075{
2076 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
Krzysztof Helt8503df62007-10-16 01:29:08 -07002078 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002079 unregister_framebuffer(info);
2080 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002081 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002082 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002083 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084}
2085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002087static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2088 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089{
2090 struct cirrusfb_info *cinfo;
2091 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002092 unsigned long board_addr, board_size;
2093 int ret;
2094
2095 ret = pci_enable_device(pdev);
2096 if (ret < 0) {
2097 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2098 goto err_out;
2099 }
2100
2101 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2102 if (!info) {
2103 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2104 ret = -ENOMEM;
2105 goto err_disable;
2106 }
2107
2108 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002109 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002111 dev_dbg(info->device,
2112 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002113 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002114 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2115 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116
Krzysztof Helt8503df62007-10-16 01:29:08 -07002117 if (isPReP) {
2118 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002120 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002123 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002125 dev_dbg(info->device,
2126 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002127 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 /* FIXME: this forces VGA. alternatives? */
2129 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002130 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 }
2132
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002133 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002134 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002136 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002137 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138
2139 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002140 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002141 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2142 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 goto err_release_fb;
2144 }
2145#if 0 /* if the system didn't claim this region, we would... */
2146 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002147 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2148 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 ret = -EBUSY;
2150 goto err_release_regions;
2151 }
2152#endif
2153 if (request_region(0x3C0, 32, "cirrusfb"))
2154 release_io_ports = 1;
2155
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002156 info->screen_base = ioremap(board_addr, board_size);
2157 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 ret = -EIO;
2159 goto err_release_legacy;
2160 }
2161
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002162 info->fix.smem_start = board_addr;
2163 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 cinfo->unmap = cirrusfb_pci_unmap;
2165
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_info(info->device,
2167 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2168 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169 pci_set_drvdata(pdev, info);
2170
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002171 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002172 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002173 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002174 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175
2176err_release_legacy:
2177 if (release_io_ports)
2178 release_region(0x3C0, 32);
2179#if 0
2180 release_mem_region(0xA0000, 65535);
2181err_release_regions:
2182#endif
2183 pci_release_regions(pdev);
2184err_release_fb:
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002185 if (cinfo->laguna_mmio == NULL)
2186 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 framebuffer_release(info);
2188err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189err_out:
2190 return ret;
2191}
2192
Krzysztof Helt8503df62007-10-16 01:29:08 -07002193static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194{
2195 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196
Krzysztof Helt8503df62007-10-16 01:29:08 -07002197 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198}
2199
2200static struct pci_driver cirrusfb_pci_driver = {
2201 .name = "cirrusfb",
2202 .id_table = cirrusfb_pci_table,
2203 .probe = cirrusfb_pci_register,
2204 .remove = __devexit_p(cirrusfb_pci_unregister),
2205#ifdef CONFIG_PM
2206#if 0
2207 .suspend = cirrusfb_pci_suspend,
2208 .resume = cirrusfb_pci_resume,
2209#endif
2210#endif
2211};
2212#endif /* CONFIG_PCI */
2213
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002215static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2216 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217{
2218 struct cirrusfb_info *cinfo;
2219 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002220 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 struct zorro_dev *z2 = NULL;
2222 unsigned long board_addr, board_size, size;
2223 int ret;
2224
2225 btype = ent->driver_data;
2226 if (cirrusfb_zorro_table2[btype].id2)
2227 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2228 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229
2230 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2231 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002232 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 ret = -ENOMEM;
2234 goto err_out;
2235 }
2236
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002237 dev_info(info->device, "%s board detected\n",
2238 cirrusfb_board_info[btype].name);
2239
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 cinfo->btype = btype;
2242
Al Viro36ea96a2007-10-27 19:46:58 +01002243 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002244 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 board_addr = zorro_resource_start(z);
2247 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002248 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002251 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2252 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253 ret = -EBUSY;
2254 goto err_release_fb;
2255 }
2256
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 ret = -EIO;
2258
2259 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002260 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
2262 /* To be precise, for the P4 this is not the */
2263 /* begin of the board, but the begin of RAM. */
2264 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2265 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002266 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 if (!cinfo->regbase)
2268 goto err_release_region;
2269
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002270 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002271 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002273 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 info->fix.smem_start = board_addr + 16777216;
2276 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2277 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 goto err_unmap_regbase;
2279 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002280 dev_info(info->device, " REG at $%lx\n",
2281 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002283 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002285 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002287 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2288 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 goto err_release_region;
2290
2291 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002292 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002293 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002295 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002296 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 }
2298 cinfo->unmap = cirrusfb_zorro_unmap;
2299
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002300 dev_info(info->device,
2301 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2302 board_size / MB_, board_addr);
2303
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 zorro_set_drvdata(z, info);
2305
Al Virod91f5bb2007-10-17 00:27:18 +01002306 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002307 if (ret) {
2308 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002310 iounmap(cinfo->regbase - 0x600000);
2311 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002312 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002313 }
2314 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315
2316err_unmap_regbase:
2317 /* Parental advisory: explicit hack */
2318 iounmap(cinfo->regbase - 0x600000);
2319err_release_region:
2320 release_region(board_addr, board_size);
2321err_release_fb:
2322 framebuffer_release(info);
2323err_out:
2324 return ret;
2325}
2326
2327void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2328{
2329 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330
Krzysztof Helt8503df62007-10-16 01:29:08 -07002331 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332}
2333
2334static struct zorro_driver cirrusfb_zorro_driver = {
2335 .name = "cirrusfb",
2336 .id_table = cirrusfb_zorro_table,
2337 .probe = cirrusfb_zorro_register,
2338 .remove = __devexit_p(cirrusfb_zorro_unregister),
2339};
2340#endif /* CONFIG_ZORRO */
2341
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002343static int __init cirrusfb_setup(char *options)
2344{
Vlada Pericee119402008-11-19 15:36:45 -08002345 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
Linus Torvalds1da177e2005-04-16 15:20:36 -07002347 if (!options || !*options)
2348 return 0;
2349
Krzysztof Helt8503df62007-10-16 01:29:08 -07002350 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002351 if (!*this_opt)
2352 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 if (!strcmp(this_opt, "noaccel"))
2355 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002356 else if (!strncmp(this_opt, "mode:", 5))
2357 mode_option = this_opt + 5;
2358 else
2359 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002360 }
2361 return 0;
2362}
2363#endif
2364
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 /*
2366 * Modularization
2367 */
2368
2369MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2370MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2371MODULE_LICENSE("GPL");
2372
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002373static int __init cirrusfb_init(void)
2374{
2375 int error = 0;
2376
2377#ifndef MODULE
2378 char *option = NULL;
2379
2380 if (fb_get_options("cirrusfb", &option))
2381 return -ENODEV;
2382 cirrusfb_setup(option);
2383#endif
2384
2385#ifdef CONFIG_ZORRO
2386 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2387#endif
2388#ifdef CONFIG_PCI
2389 error |= pci_register_driver(&cirrusfb_pci_driver);
2390#endif
2391 return error;
2392}
2393
Krzysztof Helt8503df62007-10-16 01:29:08 -07002394static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395{
2396#ifdef CONFIG_PCI
2397 pci_unregister_driver(&cirrusfb_pci_driver);
2398#endif
2399#ifdef CONFIG_ZORRO
2400 zorro_unregister_driver(&cirrusfb_zorro_driver);
2401#endif
2402}
2403
2404module_init(cirrusfb_init);
2405
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002406module_param(mode_option, charp, 0);
2407MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002408module_param(noaccel, bool, 0);
2409MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002410
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411#ifdef MODULE
2412module_exit(cirrusfb_exit);
2413#endif
2414
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415/**********************************************************************/
2416/* about the following functions - I have used the same names for the */
2417/* functions as Markus Wild did in his Retina driver for NetBSD as */
2418/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002419/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420/**********************************************************************/
2421
2422/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002423static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002424 int regnum, unsigned char val)
2425{
2426 unsigned long regofs = 0;
2427
2428 if (cinfo->btype == BT_PICASSO) {
2429 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002430/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2431 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2433 regofs = 0xfff;
2434 }
2435
Krzysztof Helt8503df62007-10-16 01:29:08 -07002436 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002437}
2438
2439/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441{
2442 unsigned long regofs = 0;
2443
2444 if (cinfo->btype == BT_PICASSO) {
2445 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2447 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2449 regofs = 0xfff;
2450 }
2451
Krzysztof Helt8503df62007-10-16 01:29:08 -07002452 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453}
2454
2455/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002458 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 /* if we're just in "write value" mode, write back the */
2462 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463 vga_w(cinfo->regbase, VGA_ATT_IW,
2464 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465 }
2466 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2468 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469
2470 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472}
2473
2474/*** WHDR() - write into the Hidden DAC register ***/
2475/* as the HDR is the only extension register that requires special treatment
2476 * (the other extension registers are accessible just like the "ordinary"
2477 * registers of their functional group) here is a specialized routine for
2478 * accessing the HDR
2479 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481{
2482 unsigned char dummy;
2483
2484 if (cinfo->btype == BT_PICASSO) {
2485 /* Klaus' hint for correct access to HDR on some boards */
2486 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002487 WGen(cinfo, VGA_PEL_MSK, 0x00);
2488 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002490 dummy = RGen(cinfo, VGA_PEL_IW);
2491 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 }
2493 /* now do the usual stuff to access the HDR */
2494
Krzysztof Helt8503df62007-10-16 01:29:08 -07002495 dummy = RGen(cinfo, VGA_PEL_MSK);
2496 udelay(200);
2497 dummy = RGen(cinfo, VGA_PEL_MSK);
2498 udelay(200);
2499 dummy = RGen(cinfo, VGA_PEL_MSK);
2500 udelay(200);
2501 dummy = RGen(cinfo, VGA_PEL_MSK);
2502 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504 WGen(cinfo, VGA_PEL_MSK, val);
2505 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506
2507 if (cinfo->btype == BT_PICASSO) {
2508 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002509 dummy = RGen(cinfo, VGA_PEL_IW);
2510 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511
2512 /* and at the end, restore the mask value */
2513 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514 WGen(cinfo, VGA_PEL_MSK, 0xff);
2515 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516 }
2517}
2518
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521{
2522#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002523 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002525 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526#endif
2527}
2528
2529/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531{
2532#ifdef CONFIG_ZORRO
2533 /* writing an arbitrary value to this one causes the monitor switcher */
2534 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538#endif
2539}
2540
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002542static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 unsigned char green, unsigned char blue)
2544{
2545 unsigned int data = VGA_PEL_D;
2546
2547 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
2550 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2551 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2552 /* but DAC data register IS, at least for Picasso II */
2553 if (cinfo->btype == BT_PICASSO)
2554 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555 vga_w(cinfo->regbase, data, red);
2556 vga_w(cinfo->regbase, data, green);
2557 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 vga_w(cinfo->regbase, data, blue);
2560 vga_w(cinfo->regbase, data, green);
2561 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 }
2563}
2564
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565#if 0
2566/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 unsigned char *green, unsigned char *blue)
2569{
2570 unsigned int data = VGA_PEL_D;
2571
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573
2574 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2575 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2576 if (cinfo->btype == BT_PICASSO)
2577 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 *red = vga_r(cinfo->regbase, data);
2579 *green = vga_r(cinfo->regbase, data);
2580 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 *blue = vga_r(cinfo->regbase, data);
2583 *green = vga_r(cinfo->regbase, data);
2584 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 }
2586}
2587#endif
2588
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589/*******************************************************************
2590 cirrusfb_WaitBLT()
2591
2592 Wait for the BitBLT engine to complete a possible earlier job
2593*********************************************************************/
2594
2595/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002596static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597{
2598 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002599 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002600 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601}
2602
2603/*******************************************************************
2604 cirrusfb_BitBLT()
2605
2606 perform accelerated "scrolling"
2607********************************************************************/
2608
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2610 u_short curx, u_short cury,
2611 u_short destx, u_short desty,
2612 u_short width, u_short height,
2613 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614{
2615 u_short nwidth, nheight;
2616 u_long nsrc, ndest;
2617 u_char bltmode;
2618
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619 nwidth = width - 1;
2620 nheight = height - 1;
2621
2622 bltmode = 0x00;
2623 /* if source adr < dest addr, do the Blt backwards */
2624 if (cury <= desty) {
2625 if (cury == desty) {
2626 /* if src and dest are on the same line, check x */
2627 if (curx < destx)
2628 bltmode |= 0x01;
2629 } else
2630 bltmode |= 0x01;
2631 }
2632 if (!bltmode) {
2633 /* standard case: forward blitting */
2634 nsrc = (cury * line_length) + curx;
2635 ndest = (desty * line_length) + destx;
2636 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002637 /* this means start addresses are at the end,
2638 * counting backwards
2639 */
2640 nsrc = cury * line_length + curx +
2641 nheight * line_length + nwidth;
2642 ndest = desty * line_length + destx +
2643 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 }
2645
2646 /*
2647 run-down of registers to be programmed:
2648 destination pitch
2649 source pitch
2650 BLT width/height
2651 source start
2652 destination start
2653 BLT mode
2654 BLT ROP
2655 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2656 start/stop
2657 */
2658
Krzysztof Helt8503df62007-10-16 01:29:08 -07002659 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660
2661 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002662 /* dest pitch low */
2663 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2664 /* dest pitch hi */
2665 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2666 /* source pitch low */
2667 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2668 /* source pitch hi */
2669 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002672 /* BLT width low */
2673 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2674 /* BLT width hi */
2675 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676
2677 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002678 /* BLT height low */
2679 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2680 /* BLT width hi */
2681 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682
2683 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002684 /* BLT dest low */
2685 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2686 /* BLT dest mid */
2687 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2688 /* BLT dest hi */
2689 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690
2691 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002692 /* BLT src low */
2693 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2694 /* BLT src mid */
2695 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2696 /* BLT src hi */
2697 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698
2699 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002700 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701
2702 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002703 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704
2705 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707}
2708
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709/*******************************************************************
2710 cirrusfb_RectFill()
2711
2712 perform accelerated rectangle fill
2713********************************************************************/
2714
Krzysztof Helt8503df62007-10-16 01:29:08 -07002715static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002716 u_short x, u_short y, u_short width, u_short height,
2717 u_char color, u_short line_length)
2718{
2719 u_short nwidth, nheight;
2720 u_long ndest;
2721 u_char op;
2722
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 nwidth = width - 1;
2724 nheight = height - 1;
2725
2726 ndest = (y * line_length) + x;
2727
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002729
2730 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002731 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2732 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2733 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2734 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735
2736 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002737 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2738 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002739
2740 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002741 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2742 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743
2744 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002745 /* BLT dest low */
2746 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2747 /* BLT dest mid */
2748 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2749 /* BLT dest hi */
2750 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
2752 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002753 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2754 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2755 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756
2757 /* This is a ColorExpand Blt, using the */
2758 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002759 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2760 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761
2762 op = 0xc0;
2763 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002764 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2765 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 op = 0x50;
2767 op = 0xd0;
2768 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002769 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2770 vga_wgfx(regbase, CL_GR11, color); /* background color */
2771 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2772 vga_wgfx(regbase, CL_GR13, color); /* background color */
2773 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2774 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 op = 0x50;
2776 op = 0xf0;
2777 }
2778 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002779 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780
2781 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002782 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783
2784 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002785 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786}
2787
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788/**************************************************************************
2789 * bestclock() - determine closest possible clock lower(?) than the
2790 * desired pixel clock
2791 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002792static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002794 int n, d;
2795 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796
Krzysztof Helt8503df62007-10-16 01:29:08 -07002797 assert(nom != NULL);
2798 assert(den != NULL);
2799 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 *nom = 0;
2802 *den = 0;
2803 *div = 0;
2804
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 if (freq < 8000)
2806 freq = 8000;
2807
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002808 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809
2810 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002811 int s = 0;
2812
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002813 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002815 int temp = d;
2816
2817 if (temp > 31) {
2818 s = 1;
2819 temp >>= 1;
2820 }
2821 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002822 h = h > freq ? h - freq : freq - h;
2823 if (h < diff) {
2824 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002826 *den = temp;
2827 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 }
2829 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002830 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002832 if (d > 31) {
2833 s = 1;
2834 d >>= 1;
2835 }
2836 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002837 h = h > freq ? h - freq : freq - h;
2838 if (h < diff) {
2839 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002841 *den = d;
2842 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843 }
2844 }
2845 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002846}
2847
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848/* -------------------------------------------------------------------------
2849 *
2850 * debugging functions
2851 *
2852 * -------------------------------------------------------------------------
2853 */
2854
2855#ifdef CIRRUSFB_DEBUG
2856
2857/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 * cirrusfb_dbg_print_regs
2859 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2860 * @reg_class: type of registers to read: %CRT, or %SEQ
2861 *
2862 * DESCRIPTION:
2863 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2864 * old-style I/O ports are queried for information, otherwise MMIO is
2865 * used at the given @base address to query the information.
2866 */
2867
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002868static void cirrusfb_dbg_print_regs(struct fb_info *info,
2869 caddr_t regbase,
2870 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871{
2872 va_list list;
2873 unsigned char val = 0;
2874 unsigned reg;
2875 char *name;
2876
Krzysztof Helt8503df62007-10-16 01:29:08 -07002877 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002881 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882
2883 switch (reg_class) {
2884 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002885 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886 break;
2887 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002888 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 break;
2890 default:
2891 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002892 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 break;
2894 }
2895
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002896 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897
Krzysztof Helt8503df62007-10-16 01:29:08 -07002898 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002899 }
2900
Krzysztof Helt8503df62007-10-16 01:29:08 -07002901 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902}
2903
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905 * cirrusfb_dbg_reg_dump
2906 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2907 *
2908 * DESCRIPTION:
2909 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2910 * old-style I/O ports are queried for information, otherwise MMIO is
2911 * used at the given @base address to query the information.
2912 */
2913
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002914static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002916 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002918 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002919 "CR00", 0x00,
2920 "CR01", 0x01,
2921 "CR02", 0x02,
2922 "CR03", 0x03,
2923 "CR04", 0x04,
2924 "CR05", 0x05,
2925 "CR06", 0x06,
2926 "CR07", 0x07,
2927 "CR08", 0x08,
2928 "CR09", 0x09,
2929 "CR0A", 0x0A,
2930 "CR0B", 0x0B,
2931 "CR0C", 0x0C,
2932 "CR0D", 0x0D,
2933 "CR0E", 0x0E,
2934 "CR0F", 0x0F,
2935 "CR10", 0x10,
2936 "CR11", 0x11,
2937 "CR12", 0x12,
2938 "CR13", 0x13,
2939 "CR14", 0x14,
2940 "CR15", 0x15,
2941 "CR16", 0x16,
2942 "CR17", 0x17,
2943 "CR18", 0x18,
2944 "CR22", 0x22,
2945 "CR24", 0x24,
2946 "CR26", 0x26,
2947 "CR2D", 0x2D,
2948 "CR2E", 0x2E,
2949 "CR2F", 0x2F,
2950 "CR30", 0x30,
2951 "CR31", 0x31,
2952 "CR32", 0x32,
2953 "CR33", 0x33,
2954 "CR34", 0x34,
2955 "CR35", 0x35,
2956 "CR36", 0x36,
2957 "CR37", 0x37,
2958 "CR38", 0x38,
2959 "CR39", 0x39,
2960 "CR3A", 0x3A,
2961 "CR3B", 0x3B,
2962 "CR3C", 0x3C,
2963 "CR3D", 0x3D,
2964 "CR3E", 0x3E,
2965 "CR3F", 0x3F,
2966 NULL);
2967
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002968 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002970 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002972 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002973 "SR00", 0x00,
2974 "SR01", 0x01,
2975 "SR02", 0x02,
2976 "SR03", 0x03,
2977 "SR04", 0x04,
2978 "SR08", 0x08,
2979 "SR09", 0x09,
2980 "SR0A", 0x0A,
2981 "SR0B", 0x0B,
2982 "SR0D", 0x0D,
2983 "SR10", 0x10,
2984 "SR11", 0x11,
2985 "SR12", 0x12,
2986 "SR13", 0x13,
2987 "SR14", 0x14,
2988 "SR15", 0x15,
2989 "SR16", 0x16,
2990 "SR17", 0x17,
2991 "SR18", 0x18,
2992 "SR19", 0x19,
2993 "SR1A", 0x1A,
2994 "SR1B", 0x1B,
2995 "SR1C", 0x1C,
2996 "SR1D", 0x1D,
2997 "SR1E", 0x1E,
2998 "SR1F", 0x1F,
2999 NULL);
3000
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07003001 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002}
3003
3004#endif /* CIRRUSFB_DEBUG */
3005