blob: cac4e2b0cbae03f8e54b0625277dcec6d86399ee [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,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
149 .sr1f = 0x20
150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 .sr07 = 0x20,
202 .sr07_1bpp = 0x20,
203 .sr07_8bpp = 0x21,
204 .sr1f = 0
205 },
206 [BT_ALPINE] = {
207 .name = "CL Alpine",
208 .maxclock = {
209 /* for the GD5430. GD5446 can do more... */
210 85500, 85500, 50000, 28500, 0
211 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700212 .init_sr07 = true,
213 .init_sr1f = true,
214 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .sr07 = 0xA0,
216 .sr07_1bpp = 0xA1,
217 .sr07_1bpp_mux = 0xA7,
218 .sr07_8bpp = 0xA1,
219 .sr07_8bpp_mux = 0xA7,
220 .sr1f = 0x1C
221 },
222 [BT_GD5480] = {
223 .name = "CL GD5480",
224 .maxclock = {
225 135100, 200000, 200000, 135100, 135100
226 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700227 .init_sr07 = true,
228 .init_sr1f = true,
229 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 .sr07 = 0x10,
231 .sr07_1bpp = 0x11,
232 .sr07_8bpp = 0x11,
233 .sr1f = 0x1C
234 },
235 [BT_LAGUNA] = {
236 .name = "CL Laguna",
237 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700238 /* taken from X11 code */
239 170000, 170000, 170000, 170000, 135100,
240 },
241 .init_sr07 = false,
242 .init_sr1f = false,
243 .scrn_start_bit19 = true,
244 },
245 [BT_LAGUNAB] = {
246 .name = "CL Laguna AGP",
247 .maxclock = {
248 /* taken from X11 code */
249 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700251 .init_sr07 = false,
252 .init_sr1f = false,
253 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 }
255};
256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257#ifdef CONFIG_PCI
258#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000259 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700262 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
263 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
264 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
266 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
268 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
269 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
270 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700272 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 { 0, }
274};
275MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
276#undef CHIP
277#endif /* CONFIG_PCI */
278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279#ifdef CONFIG_ZORRO
280static const struct zorro_device_id cirrusfb_zorro_table[] = {
281 {
282 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
283 .driver_data = BT_SD64,
284 }, {
285 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
286 .driver_data = BT_PICCOLO,
287 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700288 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 .driver_data = BT_PICASSO,
290 }, {
291 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
292 .driver_data = BT_SPECTRUM,
293 }, {
294 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
295 .driver_data = BT_PICASSO4,
296 },
297 { 0 }
298};
299
300static const struct {
301 zorro_id id2;
302 unsigned long size;
303} cirrusfb_zorro_table2[] = {
304 [BT_SD64] = {
305 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
306 .size = 0x400000
307 },
308 [BT_PICCOLO] = {
309 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
310 .size = 0x200000
311 },
312 [BT_PICASSO] = {
313 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
314 .size = 0x200000
315 },
316 [BT_SPECTRUM] = {
317 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
318 .size = 0x200000
319 },
320 [BT_PICASSO4] = {
321 .id2 = 0,
322 .size = 0x400000
323 }
324};
325#endif /* CONFIG_ZORRO */
326
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700328enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700329 CRT,
330 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700331};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700332#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334/* info about board */
335struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700337 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700338 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 unsigned char SFR; /* Shadow of special function register */
340
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700341 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700343 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700345 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346};
347
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700348static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700349static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351/****************************************************************************/
352/**** BEGIN PROTOTYPES ******************************************************/
353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700355static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
356 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700359static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static void switch_monitor(struct cirrusfb_info *cinfo, int on);
361static void WGen(const struct cirrusfb_info *cinfo,
362 int regnum, unsigned char val);
363static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
364static void AttrOn(const struct cirrusfb_info *cinfo);
365static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
366static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
367static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
368static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
369 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700371static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
372 unsigned char *red, unsigned char *green,
373 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static void cirrusfb_WaitBLT(u8 __iomem *regbase);
376static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
377 u_short curx, u_short cury,
378 u_short destx, u_short desty,
379 u_short width, u_short height,
380 u_short line_length);
381static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
382 u_short x, u_short y,
383 u_short width, u_short height,
384 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700386static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
388#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700389static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
390static void cirrusfb_dbg_print_regs(struct fb_info *info,
391 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700392 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393#endif /* CIRRUSFB_DEBUG */
394
395/*** END PROTOTYPES ********************************************************/
396/*****************************************************************************/
397/*** BEGIN Interface Used by the World ***************************************/
398
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700399static inline int is_laguna(const struct cirrusfb_info *cinfo)
400{
401 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
402}
403
Krzysztof Helt8503df62007-10-16 01:29:08 -0700404static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 return 0;
412}
413
414/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700415static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
417 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 return 0;
420}
421
422/**** END Interface used by the World *************************************/
423/****************************************************************************/
424/**** BEGIN Hardware specific Routines **************************************/
425
Krzysztof Helt486ff382008-10-15 22:03:42 -0700426/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700427static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700429 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700430 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432 /* Read MCLK value */
433 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700434 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700437 * should divide it by to get VCLK
438 */
439
440 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700441 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700442 return 1;
443 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 }
447
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
Krzysztof Helt99a45842009-03-31 15:25:09 -0700451static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
452 struct fb_info *info)
453{
454 long freq;
455 long maxclock;
456 struct cirrusfb_info *cinfo = info->par;
457 unsigned maxclockidx = var->bits_per_pixel >> 3;
458
459 /* convert from ps to kHz */
460 freq = PICOS2KHZ(var->pixclock);
461
462 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
463
464 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
465 cinfo->multiplexing = 0;
466
467 /* If the frequency is greater than we can support, we might be able
468 * to use multiplexing for the video mode */
469 if (freq > maxclock) {
470 switch (cinfo->btype) {
471 case BT_ALPINE:
472 case BT_GD5480:
473 cinfo->multiplexing = 1;
474 break;
475
476 default:
477 dev_err(info->device,
478 "Frequency greater than maxclock (%ld kHz)\n",
479 maxclock);
480 return -EINVAL;
481 }
482 }
483#if 0
484 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
485 * the VCLK is double the pixel clock. */
486 switch (var->bits_per_pixel) {
487 case 16:
488 case 32:
489 if (var->xres <= 800)
490 /* Xbh has this type of clock for 32-bit */
491 freq /= 2;
492 break;
493 }
494#endif
495 return 0;
496}
497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498static int cirrusfb_check_var(struct fb_var_screeninfo *var,
499 struct fb_info *info)
500{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700501 int yres;
502 /* memory size in pixels */
503 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700506 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 var->red.offset = 0;
508 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700509 var->green = var->red;
510 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 break;
512
513 case 8:
514 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700515 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700516 var->green = var->red;
517 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 break;
519
520 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700521 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 var->red.offset = 2;
523 var->green.offset = -3;
524 var->blue.offset = 8;
525 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700526 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 var->green.offset = 5;
528 var->blue.offset = 0;
529 }
530 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700531 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 var->blue.length = 5;
533 break;
534
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700536 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 var->red.offset = 8;
538 var->green.offset = 16;
539 var->blue.offset = 24;
540 } else {
541 var->red.offset = 16;
542 var->green.offset = 8;
543 var->blue.offset = 0;
544 }
545 var->red.length = 8;
546 var->green.length = 8;
547 var->blue.length = 8;
548 break;
549
550 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700551 dev_dbg(info->device,
552 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700553 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 /* should never occur */
555 break;
556 }
557
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700558 if (var->xres_virtual < var->xres)
559 var->xres_virtual = var->xres;
560 /* use highest possible virtual resolution */
561 if (var->yres_virtual == -1) {
562 var->yres_virtual = pixels / var->xres_virtual;
563
564 dev_info(info->device,
565 "virtual resolution set to maximum of %dx%d\n",
566 var->xres_virtual, var->yres_virtual);
567 }
568 if (var->yres_virtual < var->yres)
569 var->yres_virtual = var->yres;
570
571 if (var->xres_virtual * var->yres_virtual > pixels) {
572 dev_err(info->device, "mode %dx%dx%d rejected... "
573 "virtual resolution too high to fit into video memory!\n",
574 var->xres_virtual, var->yres_virtual,
575 var->bits_per_pixel);
576 return -EINVAL;
577 }
578
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700579 if (var->xoffset < 0)
580 var->xoffset = 0;
581 if (var->yoffset < 0)
582 var->yoffset = 0;
583
584 /* truncate xoffset and yoffset to maximum if too high */
585 if (var->xoffset > var->xres_virtual - var->xres)
586 var->xoffset = var->xres_virtual - var->xres - 1;
587 if (var->yoffset > var->yres_virtual - var->yres)
588 var->yoffset = var->yres_virtual - var->yres - 1;
589
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 var->red.msb_right =
591 var->green.msb_right =
592 var->blue.msb_right =
593 var->transp.offset =
594 var->transp.length =
595 var->transp.msb_right = 0;
596
597 yres = var->yres;
598 if (var->vmode & FB_VMODE_DOUBLE)
599 yres *= 2;
600 else if (var->vmode & FB_VMODE_INTERLACED)
601 yres = (yres + 1) / 2;
602
603 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700604 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700605 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return -EINVAL;
607 }
608
Krzysztof Helt99a45842009-03-31 15:25:09 -0700609 if (cirrusfb_check_pixclock(var, info))
610 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return 0;
613}
614
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700615static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700617 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700618 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700619
Krzysztof Helt8503df62007-10-16 01:29:08 -0700620 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700621 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Krzysztof Helt486ff382008-10-15 22:03:42 -0700623 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700624 dev_dbg(info->device, "Set %s as pixclock source.\n",
625 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700626 old1f |= 0x40;
627 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
628 if (div == 2)
629 old1e |= 1;
630
631 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700633 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
636/*************************************************************************
637 cirrusfb_set_par_foo()
638
639 actually writes the values for a new video mode into the hardware,
640**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700641static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642{
643 struct cirrusfb_info *cinfo = info->par;
644 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 u8 __iomem *regbase = cinfo->regbase;
646 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700647 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700649 int hdispend, hsyncstart, hsyncend, htotal;
650 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700651 long freq;
652 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700653 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700655 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700657
658 switch (var->bits_per_pixel) {
659 case 1:
660 info->fix.line_length = var->xres_virtual / 8;
661 info->fix.visual = FB_VISUAL_MONO10;
662 break;
663
664 case 8:
665 info->fix.line_length = var->xres_virtual;
666 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
667 break;
668
669 case 16:
670 case 32:
671 info->fix.line_length = var->xres_virtual *
672 var->bits_per_pixel >> 3;
673 info->fix.visual = FB_VISUAL_TRUECOLOR;
674 break;
675 }
676 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700678 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 bi = &cirrusfb_board_info[cinfo->btype];
681
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700682 hsyncstart = var->xres + var->right_margin;
683 hsyncend = hsyncstart + var->hsync_len;
684 htotal = (hsyncend + var->left_margin) / 8 - 5;
685 hdispend = var->xres / 8 - 1;
686 hsyncstart = hsyncstart / 8 + 1;
687 hsyncend = hsyncend / 8 + 1;
688
689 yres = var->yres;
690 vsyncstart = yres + var->lower_margin;
691 vsyncend = vsyncstart + var->vsync_len;
692 vtotal = vsyncend + var->upper_margin;
693 vdispend = yres - 1;
694
695 if (var->vmode & FB_VMODE_DOUBLE) {
696 yres *= 2;
697 vsyncstart *= 2;
698 vsyncend *= 2;
699 vtotal *= 2;
700 } else if (var->vmode & FB_VMODE_INTERLACED) {
701 yres = (yres + 1) / 2;
702 vsyncstart = (vsyncstart + 1) / 2;
703 vsyncend = (vsyncend + 1) / 2;
704 vtotal = (vtotal + 1) / 2;
705 }
706
707 vtotal -= 2;
708 vsyncstart -= 1;
709 vsyncend -= 1;
710
711 if (yres >= 1024) {
712 vtotal /= 2;
713 vsyncstart /= 2;
714 vsyncend /= 2;
715 vdispend /= 2;
716 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700717 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 htotal /= 2;
719 hsyncstart /= 2;
720 hsyncend /= 2;
721 hdispend /= 2;
722 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700724 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
726 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700727 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700728 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700730 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700731 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700733 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700734 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Krzysztof Helt8503df62007-10-16 01:29:08 -0700736 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700737 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700738 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700739 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700741 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700744 tmp = hsyncend % 32;
745 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700747 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700748 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700751 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700768 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700769 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
771 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700772 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 tmp |= 0x20;
774 if (var->vmode & FB_VMODE_DOUBLE)
775 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700776 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700777 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700782 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700785 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700791 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700795 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 tmp = 0;
798 if (var->vmode & FB_VMODE_INTERLACED)
799 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 tmp |= 128;
808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700810 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700812 freq = PICOS2KHZ(var->pixclock);
813 bestclock(freq, &nom, &den, &div);
814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
816 freq, nom, den, div);
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 /* set VCLK0 */
819 /* hardware RefClock: 14.31818 MHz */
820 /* formula: VClk = (OSC * N) / (D * (1+P)) */
821 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
822
Krzysztof Helt486ff382008-10-15 22:03:42 -0700823 if (cinfo->btype == BT_ALPINE) {
824 /* if freq is close to mclk or mclk/2 select mclk
825 * as clock source
826 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700827 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700828 if (divMCLK) {
829 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700830 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700831 }
832 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700833 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700834 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
835 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
836 unsigned short tile_control;
837
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700838 if (cinfo->btype == BT_LAGUNAB) {
839 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
840 tile_control &= ~0x80;
841 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
842 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700843
844 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
845 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
846 control = fb_readw(cinfo->laguna_mmio + 0x402);
847 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
848 control &= ~0x6800;
849 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700850 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700851 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700852 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700853 tmp = den << 1;
854 if (div != 0)
855 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700856 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
857 if ((cinfo->btype == BT_SD64) ||
858 (cinfo->btype == BT_ALPINE) ||
859 (cinfo->btype == BT_GD5480))
860 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700862 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
863 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700864 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700865 vga_wseq(regbase, CL_SEQRE, tmp);
866 vga_wseq(regbase, CL_SEQR1E, nom);
867 } else {
868 vga_wseq(regbase, CL_SEQRB, nom);
869 vga_wseq(regbase, CL_SEQR1B, tmp);
870 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700871 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700873 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700875 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 else
877 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
878 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700879 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 /* don't know if it would hurt to also program this if no interlaced */
882 /* mode is used, but I feel better this way.. :-) */
883 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700884 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700886 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700889 /* enable display memory & CRTC I/O address for color mode */
890 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
892 tmp |= 0x40;
893 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
894 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700895 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700896 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700897 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt8503df62007-10-16 01:29:08 -0700899 /* text cursor on and start line */
900 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
901 /* text cursor end line */
902 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904 /******************************************************
905 *
906 * 1 bpp
907 *
908 */
909
910 /* programming for different color depths */
911 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700912 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
915 /* SR07 */
916 switch (cinfo->btype) {
917 case BT_SD64:
918 case BT_PICCOLO:
919 case BT_PICASSO:
920 case BT_SPECTRUM:
921 case BT_PICASSO4:
922 case BT_ALPINE:
923 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700924 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700925 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 bi->sr07_1bpp_mux : bi->sr07_1bpp);
927 break;
928
929 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700930 case BT_LAGUNAB:
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:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700970 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* do nothing */
972 break;
973
974 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700975 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 break;
977 }
978
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* pixel mask: pass-through for first plane */
980 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700981 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 /* hidden dac reg: 1280x1024 */
983 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 /* hidden dac: nothing */
986 WHDR(cinfo, 0);
987 /* memory mode: odd/even, ext. memory */
988 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
989 /* plane mask: only write to first plane */
990 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 }
992
993 /******************************************************
994 *
995 * 8 bpp
996 *
997 */
998
999 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001000 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 switch (cinfo->btype) {
1002 case BT_SD64:
1003 case BT_PICCOLO:
1004 case BT_PICASSO:
1005 case BT_SPECTRUM:
1006 case BT_PICASSO4:
1007 case BT_ALPINE:
1008 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001010 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1012 break;
1013
1014 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001015 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001016 vga_wseq(regbase, CL_SEQR7,
1017 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001018 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 break;
1020
1021 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001022 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 break;
1024 }
1025
1026 switch (cinfo->btype) {
1027 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001028 /* MCLK select */
1029 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 break;
1031
1032 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001035 /* ### vorher 1c MCLK select */
1036 vga_wseq(regbase, CL_SEQR1F, 0x22);
1037 /* Fast Page-Mode writes */
1038 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 break;
1040
1041 case BT_PICASSO4:
1042#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 /* ### INCOMPLETE!! */
1044 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 break;
1048
1049 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 /* We already set SRF and SR1F */
1051 break;
1052
1053 case BT_GD5480:
1054 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001055 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 /* do nothing */
1057 break;
1058
1059 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001060 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 break;
1062 }
1063
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 /* mode register: 256 color mode */
1065 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001066 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* hidden dac reg: 1280x1024 */
1068 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* hidden dac: nothing */
1071 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 }
1073
1074 /******************************************************
1075 *
1076 * 16 bpp
1077 *
1078 */
1079
1080 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001081 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 switch (cinfo->btype) {
1083 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 /* Extended Sequencer Mode: 256c col. mode */
1085 vga_wseq(regbase, CL_SEQR7, 0xf7);
1086 /* MCLK select */
1087 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 break;
1089
1090 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001091 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wseq(regbase, CL_SEQR7, 0x87);
1093 /* Fast Page-Mode writes */
1094 vga_wseq(regbase, CL_SEQRF, 0xb0);
1095 /* MCLK select */
1096 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 break;
1098
1099 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001100 vga_wseq(regbase, CL_SEQR7, 0x27);
1101 /* Fast Page-Mode writes */
1102 vga_wseq(regbase, CL_SEQRF, 0xb0);
1103 /* MCLK select */
1104 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 vga_wseq(regbase, CL_SEQR7, 0x27);
1109/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 break;
1111
1112 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001113 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 break;
1115
1116 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 /* We already set SRF and SR1F */
1119 break;
1120
1121 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001122 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001123 vga_wseq(regbase, CL_SEQR7,
1124 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001125 control |= 0x2000;
1126 format |= 0x1400;
1127 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 break;
1129
1130 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001131 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 break;
1133 }
1134
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 /* mode register: 256 color mode */
1136 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001138 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139#elif defined(CONFIG_ZORRO)
1140 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001141 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 }
1144
1145 /******************************************************
1146 *
1147 * 32 bpp
1148 *
1149 */
1150
1151 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001152 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 switch (cinfo->btype) {
1154 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 /* Extended Sequencer Mode: 256c col. mode */
1156 vga_wseq(regbase, CL_SEQR7, 0xf9);
1157 /* MCLK select */
1158 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
1161 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001162 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001163 vga_wseq(regbase, CL_SEQR7, 0x85);
1164 /* Fast Page-Mode writes */
1165 vga_wseq(regbase, CL_SEQRF, 0xb0);
1166 /* MCLK select */
1167 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169
1170 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001171 vga_wseq(regbase, CL_SEQR7, 0x25);
1172 /* Fast Page-Mode writes */
1173 vga_wseq(regbase, CL_SEQRF, 0xb0);
1174 /* MCLK select */
1175 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 vga_wseq(regbase, CL_SEQR7, 0x25);
1180/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 break;
1182
1183 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186
1187 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 /* We already set SRF and SR1F */
1190 break;
1191
1192 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001193 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001194 vga_wseq(regbase, CL_SEQR7,
1195 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001196 control |= 0x6000;
1197 format |= 0x3400;
1198 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 break;
1200
1201 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001202 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 break;
1204 }
1205
Krzysztof Helt8503df62007-10-16 01:29:08 -07001206 /* mode register: 256 color mode */
1207 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001208 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1209 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
1211
1212 /******************************************************
1213 *
1214 * unknown/unsupported bpp
1215 *
1216 */
1217
Krzysztof Helt8503df62007-10-16 01:29:08 -07001218 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001219 dev_err(info->device,
1220 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222
Krzysztof Helt6683e012009-03-31 15:25:06 -07001223 pitch = info->fix.line_length >> 3;
1224 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001226 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 tmp |= 0x10; /* offset overflow bit */
1228
Krzysztof Helt8503df62007-10-16 01:29:08 -07001229 /* screen start addr #16-18, fastpagemode cycles */
1230 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001232 /* screen start address bit 19 */
1233 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001234 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001236 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001237 tmp = 0;
1238 if ((htotal + 5) & 256)
1239 tmp |= 128;
1240 if (hdispend & 256)
1241 tmp |= 64;
1242 if (hsyncstart & 256)
1243 tmp |= 48;
1244 if (vtotal & 1024)
1245 tmp |= 8;
1246 if (vdispend & 1024)
1247 tmp |= 4;
1248 if (vsyncstart & 1024)
1249 tmp |= 3;
1250
1251 vga_wcrt(regbase, CL_CRT1E, tmp);
1252 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1253 }
1254
Krzysztof Helt8503df62007-10-16 01:29:08 -07001255 /* pixel panning */
1256 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
1258 /* [ EGS: SetOffset(); ] */
1259 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001260 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001262 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001263 /* no tiles */
1264 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1265 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1266 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1267 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 /* finally, turn on everything - turn off "FullBandwidth" bit */
1269 /* also, set "DotClock%2" bit where requested */
1270 tmp = 0x01;
1271
1272/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1273 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1274 tmp |= 0x08;
1275*/
1276
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001278 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001281 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282#endif
1283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return 0;
1285}
1286
1287/* for some reason incomprehensible to me, cirrusfb requires that you write
1288 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 cirrusfb_set_par_foo(info);
1292 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293}
1294
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1296 unsigned blue, unsigned transp,
1297 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298{
1299 struct cirrusfb_info *cinfo = info->par;
1300
1301 if (regno > 255)
1302 return -EINVAL;
1303
1304 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1305 u32 v;
1306 red >>= (16 - info->var.red.length);
1307 green >>= (16 - info->var.green.length);
1308 blue >>= (16 - info->var.blue.length);
1309
Krzysztof Helt8503df62007-10-16 01:29:08 -07001310 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 return 1;
1312 v = (red << info->var.red.offset) |
1313 (green << info->var.green.offset) |
1314 (blue << info->var.blue.offset);
1315
Krzysztof Helt060b6002007-10-16 01:29:13 -07001316 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 return 0;
1318 }
1319
Krzysztof Helt8503df62007-10-16 01:29:08 -07001320 if (info->var.bits_per_pixel == 8)
1321 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 return 0;
1324
1325}
1326
1327/*************************************************************************
1328 cirrusfb_pan_display()
1329
1330 performs display panning - provided hardware permits this
1331**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1333 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001335 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001337 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 struct cirrusfb_info *cinfo = info->par;
1339
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001340 dev_dbg(info->device,
1341 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
1343 /* no range checks for xoffset and yoffset, */
1344 /* as fb_pan_display has already done this */
1345 if (var->vmode & FB_VMODE_YWRAP)
1346 return -EINVAL;
1347
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Krzysztof Helt99a45842009-03-31 15:25:09 -07001350 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
1352 if (info->var.bits_per_pixel == 1) {
1353 /* base is already correct */
1354 xpix = (unsigned char) (var->xoffset % 8);
1355 } else {
1356 base /= 4;
1357 xpix = (unsigned char) ((xoffset % 4) * 2);
1358 }
1359
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001360 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001361 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
1363 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001364 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1365 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001367 /* 0xf2 is %11110010, exclude tmp bits */
1368 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 /* construct bits 16, 17 and 18 of screen start address */
1370 if (base & 0x10000)
1371 tmp |= 0x01;
1372 if (base & 0x20000)
1373 tmp |= 0x04;
1374 if (base & 0x40000)
1375 tmp |= 0x08;
1376
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001377 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001380 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001381 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1382 if (is_laguna(cinfo))
1383 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1384 else
1385 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001386 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1387 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389 /* write pixel panning value to AR33; this does not quite work in 8bpp
1390 *
1391 * ### Piccolo..? Will this work?
1392 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001396 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25: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
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001516 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001517 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 case BT_ALPINE:
1519 /* Nothing to do to reset the board. */
1520 break;
1521
1522 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001523 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 break;
1525 }
1526
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001527 /* make sure RAM size set by this point */
1528 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530 /* the P4 is not fully initialized here; I rely on it having been */
1531 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1536 WGen(cinfo, CL_POS102, 0x01);
1537 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001543 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 /* FullBandwidth (video off) and 8/9 dot clock */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Krzysztof Helt8503df62007-10-16 01:29:08 -07001548 /* "magic cookie" - doesn't make any sense to me.. */
1549/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1550 /* unlock all extension registers */
1551 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 /* reset blitter */
1554 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555
1556 switch (cinfo->btype) {
1557 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001558 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 break;
1560 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001561 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001562 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 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);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001614 /* text cursor location high: 0 */
1615 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1616 /* text cursor location low: 0 */
1617 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
Krzysztof Helt8503df62007-10-16 01:29:08 -07001619 /* Underline Row scanline: - */
1620 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001622 /* ext. display controls: ext.adr. wrap */
1623 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624
Krzysztof Helt8503df62007-10-16 01:29:08 -07001625 /* Set/Reset registes: - */
1626 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1627 /* Set/Reset enable: - */
1628 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1629 /* Color Compare: - */
1630 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1631 /* Data Rotate: - */
1632 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1633 /* Read Map Select: - */
1634 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1635 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1636 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1637 /* Miscellaneous: memory map base address, graphics mode */
1638 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1639 /* Color Don't care: involve all planes */
1640 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1641 /* Bit Mask: no mask at all */
1642 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001643
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001644 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001645 /* (5434 can't have bit 3 set for bitblt) */
1646 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* Graphics controller mode extensions: finer granularity,
1649 * 8byte data latches
1650 */
1651 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
Krzysztof Helt8503df62007-10-16 01:29:08 -07001653 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1654 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1655 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1656 /* Background color byte 1: - */
1657 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1658 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659
Krzysztof Helt8503df62007-10-16 01:29:08 -07001660 /* Attribute Controller palette registers: "identity mapping" */
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1662 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1666 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1667 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1675 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1676 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 /* Attribute Controller mode: graphics mode */
1679 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1680 /* Overscan color reg.: reg. 0 */
1681 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1682 /* Color Plane enable: Enable all 4 planes */
1683 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 /* Color Select: - */
1685 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 /* BLT Start/status: Blitter reset */
1690 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1691 /* - " - : "end-of-reset" */
1692 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
1694 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001695 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001696 return;
1697}
1698
Krzysztof Helt8503df62007-10-16 01:29:08 -07001699static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700{
1701#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1702 static int IsOn = 0; /* XXX not ok for multiple boards */
1703
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 if (cinfo->btype == BT_PICASSO4)
1705 return; /* nothing to switch */
1706 if (cinfo->btype == BT_ALPINE)
1707 return; /* nothing to switch */
1708 if (cinfo->btype == BT_GD5480)
1709 return; /* nothing to switch */
1710 if (cinfo->btype == BT_PICASSO) {
1711 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 return;
1714 }
1715 if (on) {
1716 switch (cinfo->btype) {
1717 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001718 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 break;
1720 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 break;
1726 default: /* do nothing */ break;
1727 }
1728 } else {
1729 switch (cinfo->btype) {
1730 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001731 WSFR(cinfo, cinfo->SFR & 0xde);
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 & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 break;
1736 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001737 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001739 default: /* do nothing */
1740 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 }
1742 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743#endif /* CONFIG_ZORRO */
1744}
1745
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746/******************************************/
1747/* Linux 2.6-style accelerated functions */
1748/******************************************/
1749
Krzysztof Helt8503df62007-10-16 01:29:08 -07001750static void cirrusfb_fillrect(struct fb_info *info,
1751 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 struct fb_fillrect modded;
1754 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001755 struct cirrusfb_info *cinfo = info->par;
1756 int m = info->var.bits_per_pixel;
1757 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1758 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001759
1760 if (info->state != FBINFO_STATE_RUNNING)
1761 return;
1762 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1763 cfb_fillrect(info, region);
1764 return;
1765 }
1766
1767 vxres = info->var.xres_virtual;
1768 vyres = info->var.yres_virtual;
1769
1770 memcpy(&modded, region, sizeof(struct fb_fillrect));
1771
Krzysztof Helt8503df62007-10-16 01:29:08 -07001772 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 modded.dx >= vxres || modded.dy >= vyres)
1774 return;
1775
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 if (modded.dx + modded.width > vxres)
1777 modded.width = vxres - modded.dx;
1778 if (modded.dy + modded.height > vyres)
1779 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780
Krzysztof Helt060b6002007-10-16 01:29:13 -07001781 cirrusfb_RectFill(cinfo->regbase,
1782 info->var.bits_per_pixel,
1783 (region->dx * m) / 8, region->dy,
1784 (region->width * m) / 8, region->height,
1785 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001786 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787}
1788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789static void cirrusfb_copyarea(struct fb_info *info,
1790 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792 struct fb_copyarea modded;
1793 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001794 struct cirrusfb_info *cinfo = info->par;
1795 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796
1797 if (info->state != FBINFO_STATE_RUNNING)
1798 return;
1799 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1800 cfb_copyarea(info, area);
1801 return;
1802 }
1803
1804 vxres = info->var.xres_virtual;
1805 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001806 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Krzysztof Helt8503df62007-10-16 01:29:08 -07001808 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 modded.sx >= vxres || modded.sy >= vyres ||
1810 modded.dx >= vxres || modded.dy >= vyres)
1811 return;
1812
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813 if (modded.sx + modded.width > vxres)
1814 modded.width = vxres - modded.sx;
1815 if (modded.dx + modded.width > vxres)
1816 modded.width = vxres - modded.dx;
1817 if (modded.sy + modded.height > vyres)
1818 modded.height = vyres - modded.sy;
1819 if (modded.dy + modded.height > vyres)
1820 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821
Krzysztof Helt060b6002007-10-16 01:29:13 -07001822 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1823 (area->sx * m) / 8, area->sy,
1824 (area->dx * m) / 8, area->dy,
1825 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001826 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001827
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828}
1829
Krzysztof Helt8503df62007-10-16 01:29:08 -07001830static void cirrusfb_imageblit(struct fb_info *info,
1831 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832{
1833 struct cirrusfb_info *cinfo = info->par;
1834
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001835 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001836 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 cfb_imageblit(info, image);
1838}
1839
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840#ifdef CONFIG_PPC_PREP
1841#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1842#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001843static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001844{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 *display = PREP_VIDEO_BASE;
1846 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847}
1848
1849#endif /* CONFIG_PPC_PREP */
1850
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001852static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853
1854/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1855 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1856 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1857 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001858static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1859 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860{
1861 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001862 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001864 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001865 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1866
1867 mem = ((SR14 & 7) + 1) << 20;
1868 } else {
1869 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1870 switch ((SRF & 0x18)) {
1871 case 0x08:
1872 mem = 512 * 1024;
1873 break;
1874 case 0x10:
1875 mem = 1024 * 1024;
1876 break;
1877 /* 64-bit DRAM data bus width; assume 2MB.
1878 * Also indicates 2MB memory on the 5430.
1879 */
1880 case 0x18:
1881 mem = 2048 * 1024;
1882 break;
1883 default:
1884 dev_warn(info->device, "Unknown memory size!\n");
1885 mem = 1024 * 1024;
1886 }
1887 /* If DRAM bank switching is enabled, there must be
1888 * twice as much memory installed. (4MB on the 5434)
1889 */
1890 if (SRF & 0x80)
1891 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895 return mem;
1896}
1897
Krzysztof Helt8503df62007-10-16 01:29:08 -07001898static void get_pci_addrs(const struct pci_dev *pdev,
1899 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901 assert(pdev != NULL);
1902 assert(display != NULL);
1903 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 *display = 0;
1906 *registers = 0;
1907
1908 /* This is a best-guess for now */
1909
1910 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1911 *display = pci_resource_start(pdev, 1);
1912 *registers = pci_resource_start(pdev, 0);
1913 } else {
1914 *display = pci_resource_start(pdev, 0);
1915 *registers = pci_resource_start(pdev, 1);
1916 }
1917
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919}
1920
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001921static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001923 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001924 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001926 if (cinfo->laguna_mmio == NULL)
1927 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001928 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001929#if 0 /* if system didn't claim this region, we would... */
1930 release_mem_region(0xA0000, 65535);
1931#endif
1932 if (release_io_ports)
1933 release_region(0x3C0, 32);
1934 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935}
1936#endif /* CONFIG_PCI */
1937
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001939static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940{
Al Virod91f5bb2007-10-17 00:27:18 +01001941 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001942 struct zorro_dev *zdev = to_zorro_dev(info->device);
1943
1944 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001945
1946 if (cinfo->btype == BT_PICASSO4) {
1947 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001948 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001949 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001951 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001952 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954}
1955#endif /* CONFIG_ZORRO */
1956
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001957/* function table of the above functions */
1958static struct fb_ops cirrusfb_ops = {
1959 .owner = THIS_MODULE,
1960 .fb_open = cirrusfb_open,
1961 .fb_release = cirrusfb_release,
1962 .fb_setcolreg = cirrusfb_setcolreg,
1963 .fb_check_var = cirrusfb_check_var,
1964 .fb_set_par = cirrusfb_set_par,
1965 .fb_pan_display = cirrusfb_pan_display,
1966 .fb_blank = cirrusfb_blank,
1967 .fb_fillrect = cirrusfb_fillrect,
1968 .fb_copyarea = cirrusfb_copyarea,
1969 .fb_imageblit = cirrusfb_imageblit,
1970};
1971
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001972static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001974 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975 struct fb_var_screeninfo *var = &info->var;
1976
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 info->pseudo_palette = cinfo->pseudo_palette;
1978 info->flags = FBINFO_DEFAULT
1979 | FBINFO_HWACCEL_XPAN
1980 | FBINFO_HWACCEL_YPAN
1981 | FBINFO_HWACCEL_FILLRECT
1982 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001983 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 info->flags |= FBINFO_HWACCEL_DISABLED;
1985 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 if (cinfo->btype == BT_GD5480) {
1987 if (var->bits_per_pixel == 16)
1988 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001989 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990 info->screen_base += 2 * MB_;
1991 }
1992
1993 /* Fill fix common fields */
1994 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
1995 sizeof(info->fix.id));
1996
1997 /* monochrome: only 1 memory plane */
1998 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001999 info->fix.smem_len = info->screen_size;
2000 if (var->bits_per_pixel == 1)
2001 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 info->fix.xpanstep = 1;
2004 info->fix.ypanstep = 1;
2005 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006
2007 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 info->fix.mmio_len = 0;
2009 info->fix.accel = FB_ACCEL_NONE;
2010
2011 fb_alloc_cmap(&info->cmap, 256, 0);
2012
2013 return 0;
2014}
2015
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002016static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002018 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020
2021 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002022 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002024 /* set all the vital stuff */
2025 cirrusfb_set_fbinfo(info);
2026
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002027 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002029 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2030 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002031 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002032 err = -EINVAL;
2033 goto err_dealloc_cmap;
2034 }
2035
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 info->var.activate = FB_ACTIVATE_NOW;
2037
Krzysztof Helt99a45842009-03-31 15:25:09 -07002038 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 if (err < 0) {
2040 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002041 dev_dbg(info->device,
2042 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002043 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 }
2045
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 err = register_framebuffer(info);
2047 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002048 dev_err(info->device,
2049 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050 goto err_dealloc_cmap;
2051 }
2052
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 return 0;
2054
2055err_dealloc_cmap:
2056 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002057 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002058 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 return err;
2060}
2061
Krzysztof Helt8503df62007-10-16 01:29:08 -07002062static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063{
2064 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002065
Krzysztof Helt8503df62007-10-16 01:29:08 -07002066 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002067 unregister_framebuffer(info);
2068 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002069 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002070 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002071 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072}
2073
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002075static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2076 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077{
2078 struct cirrusfb_info *cinfo;
2079 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 unsigned long board_addr, board_size;
2081 int ret;
2082
2083 ret = pci_enable_device(pdev);
2084 if (ret < 0) {
2085 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2086 goto err_out;
2087 }
2088
2089 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2090 if (!info) {
2091 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2092 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002093 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094 }
2095
2096 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002097 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002099 dev_dbg(info->device,
2100 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002101 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002102 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2103 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
Krzysztof Helt8503df62007-10-16 01:29:08 -07002105 if (isPReP) {
2106 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002108 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002109#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002110 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002111 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002113 dev_dbg(info->device,
2114 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002115 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002116 /* FIXME: this forces VGA. alternatives? */
2117 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002118 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 }
2120
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002121 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002122 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002124 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002125 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126
2127 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002129 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2130 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131 goto err_release_fb;
2132 }
2133#if 0 /* if the system didn't claim this region, we would... */
2134 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002135 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2136 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137 ret = -EBUSY;
2138 goto err_release_regions;
2139 }
2140#endif
2141 if (request_region(0x3C0, 32, "cirrusfb"))
2142 release_io_ports = 1;
2143
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002144 info->screen_base = ioremap(board_addr, board_size);
2145 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 ret = -EIO;
2147 goto err_release_legacy;
2148 }
2149
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002150 info->fix.smem_start = board_addr;
2151 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 cinfo->unmap = cirrusfb_pci_unmap;
2153
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002154 dev_info(info->device,
2155 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2156 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 pci_set_drvdata(pdev, info);
2158
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002159 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002160 if (!ret)
2161 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002163 pci_set_drvdata(pdev, NULL);
2164 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165err_release_legacy:
2166 if (release_io_ports)
2167 release_region(0x3C0, 32);
2168#if 0
2169 release_mem_region(0xA0000, 65535);
2170err_release_regions:
2171#endif
2172 pci_release_regions(pdev);
2173err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002174 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002175 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177err_out:
2178 return ret;
2179}
2180
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182{
2183 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184
Krzysztof Helt8503df62007-10-16 01:29:08 -07002185 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186}
2187
2188static struct pci_driver cirrusfb_pci_driver = {
2189 .name = "cirrusfb",
2190 .id_table = cirrusfb_pci_table,
2191 .probe = cirrusfb_pci_register,
2192 .remove = __devexit_p(cirrusfb_pci_unregister),
2193#ifdef CONFIG_PM
2194#if 0
2195 .suspend = cirrusfb_pci_suspend,
2196 .resume = cirrusfb_pci_resume,
2197#endif
2198#endif
2199};
2200#endif /* CONFIG_PCI */
2201
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002203static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2204 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205{
2206 struct cirrusfb_info *cinfo;
2207 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002208 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 struct zorro_dev *z2 = NULL;
2210 unsigned long board_addr, board_size, size;
2211 int ret;
2212
2213 btype = ent->driver_data;
2214 if (cirrusfb_zorro_table2[btype].id2)
2215 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2216 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
2218 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2219 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002220 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 ret = -ENOMEM;
2222 goto err_out;
2223 }
2224
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002225 dev_info(info->device, "%s board detected\n",
2226 cirrusfb_board_info[btype].name);
2227
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 cinfo->btype = btype;
2230
Al Viro36ea96a2007-10-27 19:46:58 +01002231 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002232 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 board_addr = zorro_resource_start(z);
2235 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002236 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237
2238 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002239 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2240 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 ret = -EBUSY;
2242 goto err_release_fb;
2243 }
2244
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245 ret = -EIO;
2246
2247 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002248 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 /* To be precise, for the P4 this is not the */
2251 /* begin of the board, but the begin of RAM. */
2252 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2253 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002254 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 if (!cinfo->regbase)
2256 goto err_release_region;
2257
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002258 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002259 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002261 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002263 info->fix.smem_start = board_addr + 16777216;
2264 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2265 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 goto err_unmap_regbase;
2267 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002268 dev_info(info->device, " REG at $%lx\n",
2269 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002271 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002273 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2276 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 goto err_release_region;
2278
2279 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002280 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002281 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002283 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002284 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285 }
2286 cinfo->unmap = cirrusfb_zorro_unmap;
2287
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002288 dev_info(info->device,
2289 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2290 board_size / MB_, board_addr);
2291
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292 zorro_set_drvdata(z, info);
2293
Al Virod91f5bb2007-10-17 00:27:18 +01002294 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002295 if (ret) {
2296 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002297 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002298 iounmap(cinfo->regbase - 0x600000);
2299 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002300 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002301 }
2302 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
2304err_unmap_regbase:
2305 /* Parental advisory: explicit hack */
2306 iounmap(cinfo->regbase - 0x600000);
2307err_release_region:
2308 release_region(board_addr, board_size);
2309err_release_fb:
2310 framebuffer_release(info);
2311err_out:
2312 return ret;
2313}
2314
2315void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2316{
2317 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
Krzysztof Helt8503df62007-10-16 01:29:08 -07002319 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002320}
2321
2322static struct zorro_driver cirrusfb_zorro_driver = {
2323 .name = "cirrusfb",
2324 .id_table = cirrusfb_zorro_table,
2325 .probe = cirrusfb_zorro_register,
2326 .remove = __devexit_p(cirrusfb_zorro_unregister),
2327};
2328#endif /* CONFIG_ZORRO */
2329
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002331static int __init cirrusfb_setup(char *options)
2332{
Vlada Pericee119402008-11-19 15:36:45 -08002333 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 if (!options || !*options)
2336 return 0;
2337
Krzysztof Helt8503df62007-10-16 01:29:08 -07002338 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002339 if (!*this_opt)
2340 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 if (!strcmp(this_opt, "noaccel"))
2343 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002344 else if (!strncmp(this_opt, "mode:", 5))
2345 mode_option = this_opt + 5;
2346 else
2347 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348 }
2349 return 0;
2350}
2351#endif
2352
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 /*
2354 * Modularization
2355 */
2356
2357MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2358MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2359MODULE_LICENSE("GPL");
2360
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002361static int __init cirrusfb_init(void)
2362{
2363 int error = 0;
2364
2365#ifndef MODULE
2366 char *option = NULL;
2367
2368 if (fb_get_options("cirrusfb", &option))
2369 return -ENODEV;
2370 cirrusfb_setup(option);
2371#endif
2372
2373#ifdef CONFIG_ZORRO
2374 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2375#endif
2376#ifdef CONFIG_PCI
2377 error |= pci_register_driver(&cirrusfb_pci_driver);
2378#endif
2379 return error;
2380}
2381
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383{
2384#ifdef CONFIG_PCI
2385 pci_unregister_driver(&cirrusfb_pci_driver);
2386#endif
2387#ifdef CONFIG_ZORRO
2388 zorro_unregister_driver(&cirrusfb_zorro_driver);
2389#endif
2390}
2391
2392module_init(cirrusfb_init);
2393
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002394module_param(mode_option, charp, 0);
2395MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002396module_param(noaccel, bool, 0);
2397MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002398
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399#ifdef MODULE
2400module_exit(cirrusfb_exit);
2401#endif
2402
Linus Torvalds1da177e2005-04-16 15:20:36 -07002403/**********************************************************************/
2404/* about the following functions - I have used the same names for the */
2405/* functions as Markus Wild did in his Retina driver for NetBSD as */
2406/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002407/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408/**********************************************************************/
2409
2410/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002411static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 int regnum, unsigned char val)
2413{
2414 unsigned long regofs = 0;
2415
2416 if (cinfo->btype == BT_PICASSO) {
2417 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002418/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2419 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2421 regofs = 0xfff;
2422 }
2423
Krzysztof Helt8503df62007-10-16 01:29:08 -07002424 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425}
2426
2427/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002428static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429{
2430 unsigned long regofs = 0;
2431
2432 if (cinfo->btype == BT_PICASSO) {
2433 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002434/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2435 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2437 regofs = 0xfff;
2438 }
2439
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441}
2442
2443/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447
Krzysztof Helt8503df62007-10-16 01:29:08 -07002448 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 /* if we're just in "write value" mode, write back the */
2450 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451 vga_w(cinfo->regbase, VGA_ATT_IW,
2452 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453 }
2454 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002455/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2456 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457
2458 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002459 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460}
2461
2462/*** WHDR() - write into the Hidden DAC register ***/
2463/* as the HDR is the only extension register that requires special treatment
2464 * (the other extension registers are accessible just like the "ordinary"
2465 * registers of their functional group) here is a specialized routine for
2466 * accessing the HDR
2467 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469{
2470 unsigned char dummy;
2471
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002472 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002473 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002474 if (cinfo->btype == BT_PICASSO) {
2475 /* Klaus' hint for correct access to HDR on some boards */
2476 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002477 WGen(cinfo, VGA_PEL_MSK, 0x00);
2478 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480 dummy = RGen(cinfo, VGA_PEL_IW);
2481 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482 }
2483 /* now do the usual stuff to access the HDR */
2484
Krzysztof Helt8503df62007-10-16 01:29:08 -07002485 dummy = RGen(cinfo, VGA_PEL_MSK);
2486 udelay(200);
2487 dummy = RGen(cinfo, VGA_PEL_MSK);
2488 udelay(200);
2489 dummy = RGen(cinfo, VGA_PEL_MSK);
2490 udelay(200);
2491 dummy = RGen(cinfo, VGA_PEL_MSK);
2492 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 WGen(cinfo, VGA_PEL_MSK, val);
2495 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496
2497 if (cinfo->btype == BT_PICASSO) {
2498 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002499 dummy = RGen(cinfo, VGA_PEL_IW);
2500 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501
2502 /* and at the end, restore the mask value */
2503 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504 WGen(cinfo, VGA_PEL_MSK, 0xff);
2505 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 }
2507}
2508
Linus Torvalds1da177e2005-04-16 15:20:36 -07002509/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002510static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511{
2512#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002515 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516#endif
2517}
2518
2519/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002520static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521{
2522#ifdef CONFIG_ZORRO
2523 /* writing an arbitrary value to this one causes the monitor switcher */
2524 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002525 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002526 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528#endif
2529}
2530
Linus Torvalds1da177e2005-04-16 15:20:36 -07002531/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533 unsigned char green, unsigned char blue)
2534{
2535 unsigned int data = VGA_PEL_D;
2536
2537 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002538 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539
2540 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002541 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002542 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002543 /* but DAC data register IS, at least for Picasso II */
2544 if (cinfo->btype == BT_PICASSO)
2545 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546 vga_w(cinfo->regbase, data, red);
2547 vga_w(cinfo->regbase, data, green);
2548 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002550 vga_w(cinfo->regbase, data, blue);
2551 vga_w(cinfo->regbase, data, green);
2552 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 }
2554}
2555
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556#if 0
2557/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 unsigned char *green, unsigned char *blue)
2560{
2561 unsigned int data = VGA_PEL_D;
2562
Krzysztof Helt8503df62007-10-16 01:29:08 -07002563 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564
2565 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2566 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2567 if (cinfo->btype == BT_PICASSO)
2568 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 *red = vga_r(cinfo->regbase, data);
2570 *green = vga_r(cinfo->regbase, data);
2571 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573 *blue = vga_r(cinfo->regbase, data);
2574 *green = vga_r(cinfo->regbase, data);
2575 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 }
2577}
2578#endif
2579
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580/*******************************************************************
2581 cirrusfb_WaitBLT()
2582
2583 Wait for the BitBLT engine to complete a possible earlier job
2584*********************************************************************/
2585
2586/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588{
2589 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002591 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592}
2593
2594/*******************************************************************
2595 cirrusfb_BitBLT()
2596
2597 perform accelerated "scrolling"
2598********************************************************************/
2599
Krzysztof Helt8503df62007-10-16 01:29:08 -07002600static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2601 u_short curx, u_short cury,
2602 u_short destx, u_short desty,
2603 u_short width, u_short height,
2604 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605{
2606 u_short nwidth, nheight;
2607 u_long nsrc, ndest;
2608 u_char bltmode;
2609
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 nwidth = width - 1;
2611 nheight = height - 1;
2612
2613 bltmode = 0x00;
2614 /* if source adr < dest addr, do the Blt backwards */
2615 if (cury <= desty) {
2616 if (cury == desty) {
2617 /* if src and dest are on the same line, check x */
2618 if (curx < destx)
2619 bltmode |= 0x01;
2620 } else
2621 bltmode |= 0x01;
2622 }
2623 if (!bltmode) {
2624 /* standard case: forward blitting */
2625 nsrc = (cury * line_length) + curx;
2626 ndest = (desty * line_length) + destx;
2627 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628 /* this means start addresses are at the end,
2629 * counting backwards
2630 */
2631 nsrc = cury * line_length + curx +
2632 nheight * line_length + nwidth;
2633 ndest = desty * line_length + destx +
2634 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635 }
2636
2637 /*
2638 run-down of registers to be programmed:
2639 destination pitch
2640 source pitch
2641 BLT width/height
2642 source start
2643 destination start
2644 BLT mode
2645 BLT ROP
2646 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2647 start/stop
2648 */
2649
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
2652 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002653 /* dest pitch low */
2654 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2655 /* dest pitch hi */
2656 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2657 /* source pitch low */
2658 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2659 /* source pitch hi */
2660 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
2662 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 /* BLT width low */
2664 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2665 /* BLT width hi */
2666 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667
2668 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002669 /* BLT height low */
2670 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2671 /* BLT width hi */
2672 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673
2674 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002675 /* BLT dest low */
2676 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2677 /* BLT dest mid */
2678 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2679 /* BLT dest hi */
2680 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681
2682 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002683 /* BLT src low */
2684 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2685 /* BLT src mid */
2686 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2687 /* BLT src hi */
2688 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689
2690 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002691 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002692
2693 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002694 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695
2696 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002697 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698}
2699
Linus Torvalds1da177e2005-04-16 15:20:36 -07002700/*******************************************************************
2701 cirrusfb_RectFill()
2702
2703 perform accelerated rectangle fill
2704********************************************************************/
2705
Krzysztof Helt8503df62007-10-16 01:29:08 -07002706static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 u_short x, u_short y, u_short width, u_short height,
2708 u_char color, u_short line_length)
2709{
2710 u_short nwidth, nheight;
2711 u_long ndest;
2712 u_char op;
2713
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 nwidth = width - 1;
2715 nheight = height - 1;
2716
2717 ndest = (y * line_length) + x;
2718
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
2721 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002722 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2723 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2724 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2725 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726
2727 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002728 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2729 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730
2731 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002732 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2733 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002734
2735 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002736 /* BLT dest low */
2737 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2738 /* BLT dest mid */
2739 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2740 /* BLT dest hi */
2741 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742
2743 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002744 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2745 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2746 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747
2748 /* This is a ColorExpand Blt, using the */
2749 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002750 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2751 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752
2753 op = 0xc0;
2754 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002755 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2756 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 op = 0x50;
2758 op = 0xd0;
2759 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2761 vga_wgfx(regbase, CL_GR11, color); /* background color */
2762 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2763 vga_wgfx(regbase, CL_GR13, color); /* background color */
2764 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2765 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 op = 0x50;
2767 op = 0xf0;
2768 }
2769 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002770 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771
2772 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002773 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774
2775 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002776 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777}
2778
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779/**************************************************************************
2780 * bestclock() - determine closest possible clock lower(?) than the
2781 * desired pixel clock
2782 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002783static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002785 int n, d;
2786 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
Krzysztof Helt8503df62007-10-16 01:29:08 -07002788 assert(nom != NULL);
2789 assert(den != NULL);
2790 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791
2792 *nom = 0;
2793 *den = 0;
2794 *div = 0;
2795
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 if (freq < 8000)
2797 freq = 8000;
2798
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002799 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002802 int s = 0;
2803
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002804 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002806 int temp = d;
2807
2808 if (temp > 31) {
2809 s = 1;
2810 temp >>= 1;
2811 }
2812 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002813 h = h > freq ? h - freq : freq - h;
2814 if (h < diff) {
2815 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002817 *den = temp;
2818 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819 }
2820 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002821 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002823 if (d > 31) {
2824 s = 1;
2825 d >>= 1;
2826 }
2827 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002828 h = h > freq ? h - freq : freq - h;
2829 if (h < diff) {
2830 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002832 *den = d;
2833 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834 }
2835 }
2836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837}
2838
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839/* -------------------------------------------------------------------------
2840 *
2841 * debugging functions
2842 *
2843 * -------------------------------------------------------------------------
2844 */
2845
2846#ifdef CIRRUSFB_DEBUG
2847
2848/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849 * cirrusfb_dbg_print_regs
2850 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2851 * @reg_class: type of registers to read: %CRT, or %SEQ
2852 *
2853 * DESCRIPTION:
2854 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2855 * old-style I/O ports are queried for information, otherwise MMIO is
2856 * used at the given @base address to query the information.
2857 */
2858
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002859static void cirrusfb_dbg_print_regs(struct fb_info *info,
2860 caddr_t regbase,
2861 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862{
2863 va_list list;
2864 unsigned char val = 0;
2865 unsigned reg;
2866 char *name;
2867
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869
Krzysztof Helt8503df62007-10-16 01:29:08 -07002870 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873
2874 switch (reg_class) {
2875 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002876 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 break;
2878 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 break;
2881 default:
2882 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002883 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884 break;
2885 }
2886
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002887 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
Krzysztof Helt8503df62007-10-16 01:29:08 -07002889 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890 }
2891
Krzysztof Helt8503df62007-10-16 01:29:08 -07002892 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893}
2894
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 * cirrusfb_dbg_reg_dump
2897 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2898 *
2899 * DESCRIPTION:
2900 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2901 * old-style I/O ports are queried for information, otherwise MMIO is
2902 * used at the given @base address to query the information.
2903 */
2904
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002905static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002906{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002907 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002909 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910 "CR00", 0x00,
2911 "CR01", 0x01,
2912 "CR02", 0x02,
2913 "CR03", 0x03,
2914 "CR04", 0x04,
2915 "CR05", 0x05,
2916 "CR06", 0x06,
2917 "CR07", 0x07,
2918 "CR08", 0x08,
2919 "CR09", 0x09,
2920 "CR0A", 0x0A,
2921 "CR0B", 0x0B,
2922 "CR0C", 0x0C,
2923 "CR0D", 0x0D,
2924 "CR0E", 0x0E,
2925 "CR0F", 0x0F,
2926 "CR10", 0x10,
2927 "CR11", 0x11,
2928 "CR12", 0x12,
2929 "CR13", 0x13,
2930 "CR14", 0x14,
2931 "CR15", 0x15,
2932 "CR16", 0x16,
2933 "CR17", 0x17,
2934 "CR18", 0x18,
2935 "CR22", 0x22,
2936 "CR24", 0x24,
2937 "CR26", 0x26,
2938 "CR2D", 0x2D,
2939 "CR2E", 0x2E,
2940 "CR2F", 0x2F,
2941 "CR30", 0x30,
2942 "CR31", 0x31,
2943 "CR32", 0x32,
2944 "CR33", 0x33,
2945 "CR34", 0x34,
2946 "CR35", 0x35,
2947 "CR36", 0x36,
2948 "CR37", 0x37,
2949 "CR38", 0x38,
2950 "CR39", 0x39,
2951 "CR3A", 0x3A,
2952 "CR3B", 0x3B,
2953 "CR3C", 0x3C,
2954 "CR3D", 0x3D,
2955 "CR3E", 0x3E,
2956 "CR3F", 0x3F,
2957 NULL);
2958
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002959 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002961 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002963 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 "SR00", 0x00,
2965 "SR01", 0x01,
2966 "SR02", 0x02,
2967 "SR03", 0x03,
2968 "SR04", 0x04,
2969 "SR08", 0x08,
2970 "SR09", 0x09,
2971 "SR0A", 0x0A,
2972 "SR0B", 0x0B,
2973 "SR0D", 0x0D,
2974 "SR10", 0x10,
2975 "SR11", 0x11,
2976 "SR12", 0x12,
2977 "SR13", 0x13,
2978 "SR14", 0x14,
2979 "SR15", 0x15,
2980 "SR16", 0x16,
2981 "SR17", 0x17,
2982 "SR18", 0x18,
2983 "SR19", 0x19,
2984 "SR1A", 0x1A,
2985 "SR1B", 0x1B,
2986 "SR1C", 0x1C,
2987 "SR1D", 0x1D,
2988 "SR1E", 0x1E,
2989 "SR1F", 0x1F,
2990 NULL);
2991
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002992 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002993}
2994
2995#endif /* CIRRUSFB_DEBUG */
2996