blob: 72454c7381444da7e1d25367d8528a1a99d0c9fa [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/amifb.c -- Amiga builtin chipset frame buffer device
3 *
4 * Copyright (C) 1995-2003 Geert Uytterhoeven
5 *
6 * with work by Roman Zippel
7 *
8 *
9 * This file is based on the Atari frame buffer device (atafb.c):
10 *
11 * Copyright (C) 1994 Martin Schaller
12 * Roman Hodek
13 *
14 * with work by Andreas Schwab
15 * Guenther Kelleter
16 *
17 * and on the original Amiga console driver (amicon.c):
18 *
19 * Copyright (C) 1993 Hamish Macdonald
20 * Greg Harp
21 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
22 *
23 * with work by William Rucklidge (wjr@cs.cornell.edu)
24 * Geert Uytterhoeven
25 * Jes Sorensen (jds@kom.auc.dk)
26 *
27 *
28 * History:
29 *
30 * - 24 Jul 96: Copper generates now vblank interrupt and
31 * VESA Power Saving Protocol is fully implemented
32 * - 14 Jul 96: Rework and hopefully last ECS bugs fixed
33 * - 7 Mar 96: Hardware sprite support by Roman Zippel
34 * - 18 Feb 96: OCS and ECS support by Roman Zippel
35 * Hardware functions completely rewritten
36 * - 2 Dec 95: AGA version by Geert Uytterhoeven
37 *
38 * This file is subject to the terms and conditions of the GNU General Public
39 * License. See the file COPYING in the main directory of this archive
40 * for more details.
41 */
42
43#include <linux/module.h>
44#include <linux/kernel.h>
45#include <linux/errno.h>
46#include <linux/string.h>
47#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/interrupt.h>
50#include <linux/fb.h>
51#include <linux/init.h>
52#include <linux/ioport.h>
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +020053#include <linux/platform_device.h>
Krzysztof Helt84902b72007-10-16 01:29:04 -070054#include <linux/uaccess.h>
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +020055
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <asm/system.h>
57#include <asm/irq.h>
58#include <asm/amigahw.h>
59#include <asm/amigaints.h>
60#include <asm/setup.h>
61
62#include "c2p.h"
63
64
65#define DEBUG
66
67#if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA)
68#define CONFIG_FB_AMIGA_OCS /* define at least one fb driver, this will change later */
69#endif
70
71#if !defined(CONFIG_FB_AMIGA_OCS)
72# define IS_OCS (0)
73#elif defined(CONFIG_FB_AMIGA_ECS) || defined(CONFIG_FB_AMIGA_AGA)
74# define IS_OCS (chipset == TAG_OCS)
75#else
76# define CONFIG_FB_AMIGA_OCS_ONLY
77# define IS_OCS (1)
78#endif
79
80#if !defined(CONFIG_FB_AMIGA_ECS)
81# define IS_ECS (0)
82#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_AGA)
83# define IS_ECS (chipset == TAG_ECS)
84#else
85# define CONFIG_FB_AMIGA_ECS_ONLY
86# define IS_ECS (1)
87#endif
88
89#if !defined(CONFIG_FB_AMIGA_AGA)
90# define IS_AGA (0)
91#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_ECS)
92# define IS_AGA (chipset == TAG_AGA)
93#else
94# define CONFIG_FB_AMIGA_AGA_ONLY
95# define IS_AGA (1)
96#endif
97
98#ifdef DEBUG
Harvey Harrison5ae12172008-04-28 02:15:47 -070099# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100#else
101# define DPRINTK(fmt, args...)
102#endif
103
104/*******************************************************************************
105
106
107 Generic video timings
108 ---------------------
109
110 Timings used by the frame buffer interface:
111
112 +----------+---------------------------------------------+----------+-------+
113 | | ^ | | |
114 | | |upper_margin | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200115 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 +----------###############################################----------+-------+
117 | # ^ # | |
118 | # | # | |
119 | # | # | |
120 | # | # | |
121 | left # | # right | hsync |
122 | margin # | xres # margin | len |
123 |<-------->#<---------------+--------------------------->#<-------->|<----->|
124 | # | # | |
125 | # | # | |
126 | # | # | |
127 | # |yres # | |
128 | # | # | |
129 | # | # | |
130 | # | # | |
131 | # | # | |
132 | # | # | |
133 | # | # | |
134 | # | # | |
135 | # | # | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200136 | # v # | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 +----------###############################################----------+-------+
138 | | ^ | | |
139 | | |lower_margin | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200140 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 +----------+---------------------------------------------+----------+-------+
142 | | ^ | | |
143 | | |vsync_len | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200144 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 +----------+---------------------------------------------+----------+-------+
146
147
148 Amiga video timings
149 -------------------
150
151 The Amiga native chipsets uses another timing scheme:
152
153 - hsstrt: Start of horizontal synchronization pulse
154 - hsstop: End of horizontal synchronization pulse
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100155 - htotal: Last value on the line (i.e. line length = htotal + 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 - vsstrt: Start of vertical synchronization pulse
157 - vsstop: End of vertical synchronization pulse
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100158 - vtotal: Last line value (i.e. number of lines = vtotal + 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 - hcenter: Start of vertical retrace for interlace
160
161 You can specify the blanking timings independently. Currently I just set
162 them equal to the respective synchronization values:
163
164 - hbstrt: Start of horizontal blank
165 - hbstop: End of horizontal blank
166 - vbstrt: Start of vertical blank
167 - vbstop: End of vertical blank
168
169 Horizontal values are in color clock cycles (280 ns), vertical values are in
170 scanlines.
171
172 (0, 0) is somewhere in the upper-left corner :-)
173
174
175 Amiga visible window definitions
176 --------------------------------
177
178 Currently I only have values for AGA, SHRES (28 MHz dotclock). Feel free to
179 make corrections and/or additions.
180
181 Within the above synchronization specifications, the visible window is
182 defined by the following parameters (actual register resolutions may be
183 different; all horizontal values are normalized with respect to the pixel
184 clock):
185
186 - diwstrt_h: Horizontal start of the visible window
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100187 - diwstop_h: Horizontal stop + 1(*) of the visible window
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 - diwstrt_v: Vertical start of the visible window
189 - diwstop_v: Vertical stop of the visible window
190 - ddfstrt: Horizontal start of display DMA
191 - ddfstop: Horizontal stop of display DMA
192 - hscroll: Horizontal display output delay
193
194 Sprite positioning:
195
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100196 - sprstrt_h: Horizontal start - 4 of sprite
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 - sprstrt_v: Vertical start of sprite
198
199 (*) Even Commodore did it wrong in the AGA monitor drivers by not adding 1.
200
201 Horizontal values are in dotclock cycles (35 ns), vertical values are in
202 scanlines.
203
204 (0, 0) is somewhere in the upper-left corner :-)
205
206
207 Dependencies (AGA, SHRES (35 ns dotclock))
208 -------------------------------------------
209
210 Since there are much more parameters for the Amiga display than for the
211 frame buffer interface, there must be some dependencies among the Amiga
212 display parameters. Here's what I found out:
213
214 - ddfstrt and ddfstop are best aligned to 64 pixels.
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100215 - the chipset needs 64 + 4 horizontal pixels after the DMA start before
216 the first pixel is output, so diwstrt_h = ddfstrt + 64 + 4 if you want
217 to display the first pixel on the line too. Increase diwstrt_h for
218 virtual screen panning.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 - the display DMA always fetches 64 pixels at a time (fmode = 3).
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100220 - ddfstop is ddfstrt+#pixels - 64.
221 - diwstop_h = diwstrt_h + xres + 1. Because of the additional 1 this can
222 be 1 more than htotal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 - hscroll simply adds a delay to the display output. Smooth horizontal
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100224 panning needs an extra 64 pixels on the left to prefetch the pixels that
225 `fall off' on the left.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 - if ddfstrt < 192, the sprite DMA cycles are all stolen by the bitplane
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100227 DMA, so it's best to make the DMA start as late as possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 - you really don't want to make ddfstrt < 128, since this will steal DMA
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100229 cycles from the other DMA channels (audio, floppy and Chip RAM refresh).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 - I make diwstop_h and diwstop_v as large as possible.
231
232 General dependencies
233 --------------------
234
235 - all values are SHRES pixel (35ns)
236
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100237 table 1:fetchstart table 2:prefetch table 3:fetchsize
238 ------------------ ---------------- -----------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 Pixclock # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES
240 -------------#------+-----+------#------+-----+------#------+-----+------
241 Bus width 1x # 16 | 32 | 64 # 16 | 32 | 64 # 64 | 64 | 64
242 Bus width 2x # 32 | 64 | 128 # 32 | 64 | 64 # 64 | 64 | 128
243 Bus width 4x # 64 | 128 | 256 # 64 | 64 | 64 # 64 | 128 | 256
244
245 - chipset needs 4 pixels before the first pixel is output
246 - ddfstrt must be aligned to fetchstart (table 1)
247 - chipset needs also prefetch (table 2) to get first pixel data, so
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100248 ddfstrt = ((diwstrt_h - 4) & -fetchstart) - prefetch
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 - for horizontal panning decrease diwstrt_h
250 - the length of a fetchline must be aligned to fetchsize (table 3)
251 - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100252 moved to optimize use of dma (useful for OCS/ECS overscan displays)
253 - ddfstop is ddfstrt + ddfsize - fetchsize
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 - If C= didn't change anything for AGA, then at following positions the
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100255 dma bus is already used:
256 ddfstrt < 48 -> memory refresh
257 < 96 -> disk dma
258 < 160 -> audio dma
259 < 192 -> sprite 0 dma
260 < 416 -> sprite dma (32 per sprite)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 - in accordance with the hardware reference manual a hardware stop is at
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100262 192, but AGA (ECS?) can go below this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 DMA priorities
265 --------------
266
267 Since there are limits on the earliest start value for display DMA and the
268 display of sprites, I use the following policy on horizontal panning and
269 the hardware cursor:
270
271 - if you want to start display DMA too early, you lose the ability to
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100272 do smooth horizontal panning (xpanstep 1 -> 64).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 - if you want to go even further, you lose the hardware cursor too.
274
275 IMHO a hardware cursor is more important for X than horizontal scrolling,
276 so that's my motivation.
277
278
279 Implementation
280 --------------
281
282 ami_decode_var() converts the frame buffer values to the Amiga values. It's
283 just a `straightforward' implementation of the above rules.
284
285
286 Standard VGA timings
287 --------------------
288
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100289 xres yres left right upper lower hsync vsync
290 ---- ---- ---- ----- ----- ----- ----- -----
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 80x25 720 400 27 45 35 12 108 2
292 80x30 720 480 27 45 30 9 108 2
293
294 These were taken from a XFree86 configuration file, recalculated for a 28 MHz
295 dotclock (Amigas don't have a 25 MHz dotclock) and converted to frame buffer
296 generic timings.
297
298 As a comparison, graphics/monitor.h suggests the following:
299
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100300 xres yres left right upper lower hsync vsync
301 ---- ---- ---- ----- ----- ----- ----- -----
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
303 VGA 640 480 52 112 24 19 112 - 2 +
304 VGA70 640 400 52 112 27 21 112 - 2 -
305
306
307 Sync polarities
308 ---------------
309
310 VSYNC HSYNC Vertical size Vertical total
311 ----- ----- ------------- --------------
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100312 + + Reserved Reserved
313 + - 400 414
314 - + 350 362
315 - - 480 496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 Source: CL-GD542X Technical Reference Manual, Cirrus Logic, Oct 1992
318
319
320 Broadcast video timings
321 -----------------------
322
323 According to the CCIR and RETMA specifications, we have the following values:
324
325 CCIR -> PAL
326 -----------
327
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200328 - a scanline is 64 µs long, of which 52.48 µs are visible. This is about
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100329 736 visible 70 ns pixels per line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 - we have 625 scanlines, of which 575 are visible (interlaced); after
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100331 rounding this becomes 576.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 RETMA -> NTSC
334 -------------
335
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200336 - a scanline is 63.5 µs long, of which 53.5 µs are visible. This is about
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100337 736 visible 70 ns pixels per line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 - we have 525 scanlines, of which 485 are visible (interlaced); after
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100339 rounding this becomes 484.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341 Thus if you want a PAL compatible display, you have to do the following:
342
343 - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100344 timings are to be used.
345 - make sure upper_margin + yres + lower_margin + vsync_len = 625 for an
346 interlaced, 312 for a non-interlaced and 156 for a doublescanned
347 display.
348 - make sure left_margin + xres + right_margin + hsync_len = 1816 for a
349 SHRES, 908 for a HIRES and 454 for a LORES display.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90),
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100351 left_margin + 2 * hsync_len must be greater or equal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 - the upper visible part begins at 48 (interlaced; non-interlaced:24,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100353 doublescanned:12), upper_margin + 2 * vsync_len must be greater or
354 equal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100356 of 4 scanlines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 The settings for a NTSC compatible display are straightforward.
359
360 Note that in a strict sense the PAL and NTSC standards only define the
361 encoding of the color part (chrominance) of the video signal and don't say
362 anything about horizontal/vertical synchronization nor refresh rates.
363
364
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100365 -- Geert --
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367*******************************************************************************/
368
369
370 /*
371 * Custom Chipset Definitions
372 */
373
374#define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld)
375
376 /*
377 * BPLCON0 -- Bitplane Control Register 0
378 */
379
380#define BPC0_HIRES (0x8000)
381#define BPC0_BPU2 (0x4000) /* Bit plane used count */
382#define BPC0_BPU1 (0x2000)
383#define BPC0_BPU0 (0x1000)
384#define BPC0_HAM (0x0800) /* HAM mode */
385#define BPC0_DPF (0x0400) /* Double playfield */
386#define BPC0_COLOR (0x0200) /* Enable colorburst */
387#define BPC0_GAUD (0x0100) /* Genlock audio enable */
388#define BPC0_UHRES (0x0080) /* Ultrahi res enable */
389#define BPC0_SHRES (0x0040) /* Super hi res mode */
390#define BPC0_BYPASS (0x0020) /* Bypass LUT - AGA */
391#define BPC0_BPU3 (0x0010) /* AGA */
392#define BPC0_LPEN (0x0008) /* Light pen enable */
393#define BPC0_LACE (0x0004) /* Interlace */
394#define BPC0_ERSY (0x0002) /* External resync */
395#define BPC0_ECSENA (0x0001) /* ECS enable */
396
397 /*
398 * BPLCON2 -- Bitplane Control Register 2
399 */
400
401#define BPC2_ZDBPSEL2 (0x4000) /* Bitplane to be used for ZD - AGA */
402#define BPC2_ZDBPSEL1 (0x2000)
403#define BPC2_ZDBPSEL0 (0x1000)
404#define BPC2_ZDBPEN (0x0800) /* Enable ZD with ZDBPSELx - AGA */
405#define BPC2_ZDCTEN (0x0400) /* Enable ZD with palette bit #31 - AGA */
406#define BPC2_KILLEHB (0x0200) /* Kill EHB mode - AGA */
407#define BPC2_RDRAM (0x0100) /* Color table accesses read, not write - AGA */
408#define BPC2_SOGEN (0x0080) /* SOG output pin high - AGA */
409#define BPC2_PF2PRI (0x0040) /* PF2 priority over PF1 */
410#define BPC2_PF2P2 (0x0020) /* PF2 priority wrt sprites */
411#define BPC2_PF2P1 (0x0010)
412#define BPC2_PF2P0 (0x0008)
413#define BPC2_PF1P2 (0x0004) /* ditto PF1 */
414#define BPC2_PF1P1 (0x0002)
415#define BPC2_PF1P0 (0x0001)
416
417 /*
418 * BPLCON3 -- Bitplane Control Register 3 (AGA)
419 */
420
421#define BPC3_BANK2 (0x8000) /* Bits to select color register bank */
422#define BPC3_BANK1 (0x4000)
423#define BPC3_BANK0 (0x2000)
424#define BPC3_PF2OF2 (0x1000) /* Bits for color table offset when PF2 */
425#define BPC3_PF2OF1 (0x0800)
426#define BPC3_PF2OF0 (0x0400)
427#define BPC3_LOCT (0x0200) /* Color register writes go to low bits */
428#define BPC3_SPRES1 (0x0080) /* Sprite resolution bits */
429#define BPC3_SPRES0 (0x0040)
430#define BPC3_BRDRBLNK (0x0020) /* Border blanked? */
431#define BPC3_BRDRTRAN (0x0010) /* Border transparent? */
432#define BPC3_ZDCLKEN (0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
433#define BPC3_BRDRSPRT (0x0002) /* Sprites in border? */
434#define BPC3_EXTBLKEN (0x0001) /* BLANK programmable */
435
436 /*
437 * BPLCON4 -- Bitplane Control Register 4 (AGA)
438 */
439
440#define BPC4_BPLAM7 (0x8000) /* bitplane color XOR field */
441#define BPC4_BPLAM6 (0x4000)
442#define BPC4_BPLAM5 (0x2000)
443#define BPC4_BPLAM4 (0x1000)
444#define BPC4_BPLAM3 (0x0800)
445#define BPC4_BPLAM2 (0x0400)
446#define BPC4_BPLAM1 (0x0200)
447#define BPC4_BPLAM0 (0x0100)
448#define BPC4_ESPRM7 (0x0080) /* 4 high bits for even sprite colors */
449#define BPC4_ESPRM6 (0x0040)
450#define BPC4_ESPRM5 (0x0020)
451#define BPC4_ESPRM4 (0x0010)
452#define BPC4_OSPRM7 (0x0008) /* 4 high bits for odd sprite colors */
453#define BPC4_OSPRM6 (0x0004)
454#define BPC4_OSPRM5 (0x0002)
455#define BPC4_OSPRM4 (0x0001)
456
457 /*
458 * BEAMCON0 -- Beam Control Register
459 */
460
461#define BMC0_HARDDIS (0x4000) /* Disable hardware limits */
462#define BMC0_LPENDIS (0x2000) /* Disable light pen latch */
463#define BMC0_VARVBEN (0x1000) /* Enable variable vertical blank */
464#define BMC0_LOLDIS (0x0800) /* Disable long/short line toggle */
465#define BMC0_CSCBEN (0x0400) /* Composite sync/blank */
466#define BMC0_VARVSYEN (0x0200) /* Enable variable vertical sync */
467#define BMC0_VARHSYEN (0x0100) /* Enable variable horizontal sync */
468#define BMC0_VARBEAMEN (0x0080) /* Enable variable beam counters */
469#define BMC0_DUAL (0x0040) /* Enable alternate horizontal beam counter */
470#define BMC0_PAL (0x0020) /* Set decodes for PAL */
471#define BMC0_VARCSYEN (0x0010) /* Enable variable composite sync */
472#define BMC0_BLANKEN (0x0008) /* Blank enable (no longer used on AGA) */
473#define BMC0_CSYTRUE (0x0004) /* CSY polarity */
474#define BMC0_VSYTRUE (0x0002) /* VSY polarity */
475#define BMC0_HSYTRUE (0x0001) /* HSY polarity */
476
477
478 /*
479 * FMODE -- Fetch Mode Control Register (AGA)
480 */
481
482#define FMODE_SSCAN2 (0x8000) /* Sprite scan-doubling */
483#define FMODE_BSCAN2 (0x4000) /* Use PF2 modulus every other line */
484#define FMODE_SPAGEM (0x0008) /* Sprite page mode */
485#define FMODE_SPR32 (0x0004) /* Sprite 32 bit fetch */
486#define FMODE_BPAGEM (0x0002) /* Bitplane page mode */
487#define FMODE_BPL32 (0x0001) /* Bitplane 32 bit fetch */
488
489 /*
490 * Tags used to indicate a specific Pixel Clock
491 *
492 * clk_shift is the shift value to get the timings in 35 ns units
493 */
494
495enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
496
497 /*
498 * Tags used to indicate the specific chipset
499 */
500
501enum { TAG_OCS, TAG_ECS, TAG_AGA };
502
503 /*
504 * Tags used to indicate the memory bandwidth
505 */
506
507enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 };
508
509
510 /*
511 * Clock Definitions, Maximum Display Depth
512 *
513 * These depend on the E-Clock or the Chipset, so they are filled in
514 * dynamically
515 */
516
517static u_long pixclock[3]; /* SHRES/HIRES/LORES: index = clk_shift */
518static u_short maxdepth[3]; /* SHRES/HIRES/LORES: index = clk_shift */
519static u_short maxfmode, chipset;
520
521
522 /*
523 * Broadcast Video Timings
524 *
525 * Horizontal values are in 35 ns (SHRES) units
526 * Vertical values are in interlaced scanlines
527 */
528
529#define PAL_DIWSTRT_H (360) /* PAL Window Limits */
530#define PAL_DIWSTRT_V (48)
531#define PAL_HTOTAL (1816)
532#define PAL_VTOTAL (625)
533
534#define NTSC_DIWSTRT_H (360) /* NTSC Window Limits */
535#define NTSC_DIWSTRT_V (40)
536#define NTSC_HTOTAL (1816)
537#define NTSC_VTOTAL (525)
538
539
540 /*
541 * Various macros
542 */
543
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100544#define up2(v) (((v) + 1) & -2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545#define down2(v) ((v) & -2)
546#define div2(v) ((v)>>1)
547#define mod2(v) ((v) & 1)
548
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100549#define up4(v) (((v) + 3) & -4)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550#define down4(v) ((v) & -4)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100551#define mul4(v) ((v) << 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552#define div4(v) ((v)>>2)
553#define mod4(v) ((v) & 3)
554
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100555#define up8(v) (((v) + 7) & -8)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556#define down8(v) ((v) & -8)
557#define div8(v) ((v)>>3)
558#define mod8(v) ((v) & 7)
559
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100560#define up16(v) (((v) + 15) & -16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561#define down16(v) ((v) & -16)
562#define div16(v) ((v)>>4)
563#define mod16(v) ((v) & 15)
564
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100565#define up32(v) (((v) + 31) & -32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566#define down32(v) ((v) & -32)
567#define div32(v) ((v)>>5)
568#define mod32(v) ((v) & 31)
569
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100570#define up64(v) (((v) + 63) & -64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571#define down64(v) ((v) & -64)
572#define div64(v) ((v)>>6)
573#define mod64(v) ((v) & 63)
574
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100575#define upx(x, v) (((v) + (x) - 1) & -(x))
576#define downx(x, v) ((v) & -(x))
577#define modx(x, v) ((v) & ((x) - 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
579/* if x1 is not a constant, this macro won't make real sense :-) */
580#ifdef __mc68000__
581#define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100582 "d" (x2), "d" ((long)((x1) / 0x100000000ULL)), "0" ((long)(x1))); res;})
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583#else
584/* We know a bit about the numbers, so we can do it this way */
585#define DIVUL(x1, x2) ((((long)((unsigned long long)x1 >> 8) / x2) << 8) + \
586 ((((long)((unsigned long long)x1 >> 8) % x2) << 8) / x2))
587#endif
588
589#define highw(x) ((u_long)(x)>>16 & 0xffff)
590#define loww(x) ((u_long)(x) & 0xffff)
591
Al Virob4290a22006-01-12 01:06:12 -0800592#define custom amiga_custom
593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594#define VBlankOn() custom.intena = IF_SETCLR|IF_COPER
595#define VBlankOff() custom.intena = IF_COPER
596
597
598 /*
599 * Chip RAM we reserve for the Frame Buffer
600 *
601 * This defines the Maximum Virtual Screen Size
602 * (Setable per kernel options?)
603 */
604
605#define VIDEOMEMSIZE_AGA_2M (1310720) /* AGA (2MB) : max 1280*1024*256 */
606#define VIDEOMEMSIZE_AGA_1M (786432) /* AGA (1MB) : max 1024*768*256 */
607#define VIDEOMEMSIZE_ECS_2M (655360) /* ECS (2MB) : max 1280*1024*16 */
608#define VIDEOMEMSIZE_ECS_1M (393216) /* ECS (1MB) : max 1024*768*16 */
609#define VIDEOMEMSIZE_OCS (262144) /* OCS : max ca. 800*600*16 */
610
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100611#define SPRITEMEMSIZE (64 * 64 / 4) /* max 64*64*4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612#define DUMMYSPRITEMEMSIZE (8)
613static u_long spritememory;
614
615#define CHIPRAM_SAFETY_LIMIT (16384)
616
617static u_long videomemory;
618
619 /*
620 * This is the earliest allowed start of fetching display data.
621 * Only if you really want no hardware cursor and audio,
622 * set this to 128, but let it better at 192
623 */
624
625static u_long min_fstrt = 192;
626
627#define assignchunk(name, type, ptr, size) \
628{ \
629 (name) = (type)(ptr); \
630 ptr += size; \
631}
632
633
634 /*
635 * Copper Instructions
636 */
637
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100638#define CMOVE(val, reg) (CUSTOM_OFS(reg) << 16 | (val))
639#define CMOVE2(val, reg) ((CUSTOM_OFS(reg) + 2) << 16 | (val))
640#define CWAIT(x, y) (((y) & 0x1fe) << 23 | ((x) & 0x7f0) << 13 | 0x0001fffe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641#define CEND (0xfffffffe)
642
643
644typedef union {
645 u_long l;
646 u_short w[2];
647} copins;
648
649static struct copdisplay {
650 copins *init;
651 copins *wait;
652 copins *list[2][2];
653 copins *rebuild[2];
654} copdisplay;
655
656static u_short currentcop = 0;
657
658 /*
659 * Hardware Cursor API Definitions
660 * These used to be in linux/fb.h, but were preliminary and used by
661 * amifb only anyway
662 */
663
664#define FBIOGET_FCURSORINFO 0x4607
665#define FBIOGET_VCURSORINFO 0x4608
666#define FBIOPUT_VCURSORINFO 0x4609
667#define FBIOGET_CURSORSTATE 0x460A
668#define FBIOPUT_CURSORSTATE 0x460B
669
670
671struct fb_fix_cursorinfo {
672 __u16 crsr_width; /* width and height of the cursor in */
673 __u16 crsr_height; /* pixels (zero if no cursor) */
674 __u16 crsr_xsize; /* cursor size in display pixels */
675 __u16 crsr_ysize;
676 __u16 crsr_color1; /* colormap entry for cursor color1 */
677 __u16 crsr_color2; /* colormap entry for cursor color2 */
678};
679
680struct fb_var_cursorinfo {
681 __u16 width;
682 __u16 height;
683 __u16 xspot;
684 __u16 yspot;
685 __u8 data[1]; /* field with [height][width] */
686};
687
688struct fb_cursorstate {
689 __s16 xoffset;
690 __s16 yoffset;
691 __u16 mode;
692};
693
694#define FB_CURSOR_OFF 0
695#define FB_CURSOR_ON 1
696#define FB_CURSOR_FLASH 2
697
698
699 /*
700 * Hardware Cursor
701 */
702
703static int cursorrate = 20; /* Number of frames/flash toggle */
704static u_short cursorstate = -1;
705static u_short cursormode = FB_CURSOR_OFF;
706
707static u_short *lofsprite, *shfsprite, *dummysprite;
708
709 /*
710 * Current Video Mode
711 */
712
713static struct amifb_par {
714
715 /* General Values */
716
717 int xres; /* vmode */
718 int yres; /* vmode */
719 int vxres; /* vmode */
720 int vyres; /* vmode */
721 int xoffset; /* vmode */
722 int yoffset; /* vmode */
723 u_short bpp; /* vmode */
724 u_short clk_shift; /* vmode */
725 u_short line_shift; /* vmode */
726 int vmode; /* vmode */
727 u_short diwstrt_h; /* vmode */
728 u_short diwstop_h; /* vmode */
729 u_short diwstrt_v; /* vmode */
730 u_short diwstop_v; /* vmode */
731 u_long next_line; /* modulo for next line */
732 u_long next_plane; /* modulo for next plane */
733
734 /* Cursor Values */
735
736 struct {
737 short crsr_x; /* movecursor */
738 short crsr_y; /* movecursor */
739 short spot_x;
740 short spot_y;
741 u_short height;
742 u_short width;
743 u_short fmode;
744 } crsr;
745
746 /* OCS Hardware Registers */
747
748 u_long bplpt0; /* vmode, pan (Note: physical address) */
749 u_long bplpt0wrap; /* vmode, pan (Note: physical address) */
750 u_short ddfstrt;
751 u_short ddfstop;
752 u_short bpl1mod;
753 u_short bpl2mod;
754 u_short bplcon0; /* vmode */
755 u_short bplcon1; /* vmode */
756 u_short htotal; /* vmode */
757 u_short vtotal; /* vmode */
758
759 /* Additional ECS Hardware Registers */
760
761 u_short bplcon3; /* vmode */
762 u_short beamcon0; /* vmode */
763 u_short hsstrt; /* vmode */
764 u_short hsstop; /* vmode */
765 u_short hbstrt; /* vmode */
766 u_short hbstop; /* vmode */
767 u_short vsstrt; /* vmode */
768 u_short vsstop; /* vmode */
769 u_short vbstrt; /* vmode */
770 u_short vbstop; /* vmode */
771 u_short hcenter; /* vmode */
772
773 /* Additional AGA Hardware Registers */
774
775 u_short fmode; /* vmode */
776} currentpar;
777
778
779static struct fb_info fb_info = {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100780 .fix = {
781 .id = "Amiga ",
782 .visual = FB_VISUAL_PSEUDOCOLOR,
783 .accel = FB_ACCEL_AMIGABLITT
784 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785};
786
787
788 /*
789 * Saved color entry 0 so we can restore it when unblanking
790 */
791
792static u_char red0, green0, blue0;
793
794
795#if defined(CONFIG_FB_AMIGA_ECS)
796static u_short ecs_palette[32];
797#endif
798
799
800 /*
801 * Latches for Display Changes during VBlank
802 */
803
804static u_short do_vmode_full = 0; /* Change the Video Mode */
805static u_short do_vmode_pan = 0; /* Update the Video Mode */
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200806static short do_blank = 0; /* (Un)Blank the Screen (±1) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807static u_short do_cursor = 0; /* Move the Cursor */
808
809
810 /*
811 * Various Flags
812 */
813
814static u_short is_blanked = 0; /* Screen is Blanked */
815static u_short is_lace = 0; /* Screen is laced */
816
817 /*
818 * Predefined Video Modes
819 *
820 */
821
822static struct fb_videomode ami_modedb[] __initdata = {
823
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100824 /*
825 * AmigaOS Video Modes
826 *
827 * If you change these, make sure to update DEFMODE_* as well!
828 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100830 {
831 /* 640x200, 15 kHz, 60 Hz (NTSC) */
832 "ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
833 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
834 }, {
835 /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
836 "ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
837 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
838 }, {
839 /* 640x256, 15 kHz, 50 Hz (PAL) */
840 "pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
841 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
842 }, {
843 /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
844 "pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
845 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
846 }, {
847 /* 640x480, 29 kHz, 57 Hz */
848 "multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
849 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
850 }, {
851 /* 640x960, 29 kHz, 57 Hz interlaced */
852 "multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72,
853 16,
854 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
855 }, {
856 /* 640x200, 15 kHz, 72 Hz */
857 "euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
858 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
859 }, {
860 /* 640x400, 15 kHz, 72 Hz interlaced */
861 "euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52,
862 10,
863 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
864 }, {
865 /* 640x400, 29 kHz, 68 Hz */
866 "euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
867 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
868 }, {
869 /* 640x800, 29 kHz, 68 Hz interlaced */
870 "euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80,
871 16,
872 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
873 }, {
874 /* 800x300, 23 kHz, 70 Hz */
875 "super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
876 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
877 }, {
878 /* 800x600, 23 kHz, 70 Hz interlaced */
879 "super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80,
880 14,
881 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
882 }, {
883 /* 640x200, 27 kHz, 57 Hz doublescan */
884 "dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
885 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
886 }, {
887 /* 640x400, 27 kHz, 57 Hz */
888 "dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
889 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
890 }, {
891 /* 640x800, 27 kHz, 57 Hz interlaced */
892 "dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80,
893 14,
894 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
895 }, {
896 /* 640x256, 27 kHz, 47 Hz doublescan */
897 "dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
898 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
899 }, {
900 /* 640x512, 27 kHz, 47 Hz */
901 "dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
902 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
903 }, {
904 /* 640x1024, 27 kHz, 47 Hz interlaced */
905 "dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80,
906 14,
907 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
908 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100910 /*
911 * VGA Video Modes
912 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100914 {
915 /* 640x480, 31 kHz, 60 Hz (VGA) */
916 "vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
917 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
918 }, {
919 /* 640x400, 31 kHz, 70 Hz (VGA) */
920 "vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
921 FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT,
922 FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
923 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
925#if 0
926
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100927 /*
928 * A2024 video modes
929 * These modes don't work yet because there's no A2024 driver.
930 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100932 {
933 /* 1024x800, 10 Hz */
934 "a2024-10", 10, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
935 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
936 }, {
937 /* 1024x800, 15 Hz */
938 "a2024-15", 15, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
939 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
940 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941#endif
942};
943
944#define NUM_TOTAL_MODES ARRAY_SIZE(ami_modedb)
945
946static char *mode_option __initdata = NULL;
947static int round_down_bpp = 1; /* for mode probing */
948
949 /*
950 * Some default modes
951 */
952
953
954#define DEFMODE_PAL 2 /* "pal" for PAL OCS/ECS */
955#define DEFMODE_NTSC 0 /* "ntsc" for NTSC OCS/ECS */
956#define DEFMODE_AMBER_PAL 3 /* "pal-lace" for flicker fixed PAL (A3000) */
957#define DEFMODE_AMBER_NTSC 1 /* "ntsc-lace" for flicker fixed NTSC (A3000) */
958#define DEFMODE_AGA 19 /* "vga70" for AGA */
959
960
961static int amifb_ilbm = 0; /* interleaved or normal bitplanes */
962static int amifb_inverse = 0;
963
964
965 /*
966 * Macros for the conversion from real world values to hardware register
967 * values
968 *
969 * This helps us to keep our attention on the real stuff...
970 *
971 * Hardware limits for AGA:
972 *
973 * parameter min max step
974 * --------- --- ---- ----
975 * diwstrt_h 0 2047 1
976 * diwstrt_v 0 2047 1
977 * diwstop_h 0 4095 1
978 * diwstop_v 0 4095 1
979 *
980 * ddfstrt 0 2032 16
981 * ddfstop 0 2032 16
982 *
983 * htotal 8 2048 8
984 * hsstrt 0 2040 8
985 * hsstop 0 2040 8
986 * vtotal 1 4096 1
987 * vsstrt 0 4095 1
988 * vsstop 0 4095 1
989 * hcenter 0 2040 8
990 *
991 * hbstrt 0 2047 1
992 * hbstop 0 2047 1
993 * vbstrt 0 4095 1
994 * vbstop 0 4095 1
995 *
996 * Horizontal values are in 35 ns (SHRES) pixels
997 * Vertical values are in half scanlines
998 */
999
1000/* bplcon1 (smooth scrolling) */
1001
1002#define hscroll2hw(hscroll) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001003 (((hscroll) << 12 & 0x3000) | ((hscroll) << 8 & 0xc300) | \
1004 ((hscroll) << 4 & 0x0c00) | ((hscroll) << 2 & 0x00f0) | \
1005 ((hscroll)>>2 & 0x000f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
1007/* diwstrt/diwstop/diwhigh (visible display window) */
1008
1009#define diwstrt2hw(diwstrt_h, diwstrt_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001010 (((diwstrt_v) << 7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011#define diwstop2hw(diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001012 (((diwstop_v) << 7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001014 (((diwstop_h) << 3 & 0x2000) | ((diwstop_h) << 11 & 0x1800) | \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001016 ((diwstrt_h) << 3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
1018/* ddfstrt/ddfstop (display DMA) */
1019
1020#define ddfstrt2hw(ddfstrt) div8(ddfstrt)
1021#define ddfstop2hw(ddfstop) div8(ddfstop)
1022
1023/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
1024
1025#define hsstrt2hw(hsstrt) (div8(hsstrt))
1026#define hsstop2hw(hsstop) (div8(hsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001027#define htotal2hw(htotal) (div8(htotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028#define vsstrt2hw(vsstrt) (div2(vsstrt))
1029#define vsstop2hw(vsstop) (div2(vsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001030#define vtotal2hw(vtotal) (div2(vtotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031#define hcenter2hw(htotal) (div8(htotal))
1032
1033/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
1034
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001035#define hbstrt2hw(hbstrt) (((hbstrt) << 8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
1036#define hbstop2hw(hbstop) (((hbstop) << 8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037#define vbstrt2hw(vbstrt) (div2(vbstrt))
1038#define vbstop2hw(vbstop) (div2(vbstop))
1039
1040/* colour */
1041
1042#define rgb2hw8_high(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001043 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044#define rgb2hw8_low(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001045 (((red & 0x0f) << 8) | ((green & 0x0f) << 4) | (blue & 0x0f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046#define rgb2hw4(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001047 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048#define rgb2hw2(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001049 (((red & 0xc0) << 4) | (green & 0xc0) | ((blue & 0xc0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050
1051/* sprpos/sprctl (sprite positioning) */
1052
1053#define spr2hw_pos(start_v, start_h) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001054 (((start_v) << 7 & 0xff00) | ((start_h)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055#define spr2hw_ctl(start_v, start_h, stop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001056 (((stop_v) << 7 & 0xff00) | ((start_v)>>4 & 0x0040) | \
1057 ((stop_v)>>5 & 0x0020) | ((start_h) << 3 & 0x0018) | \
1058 ((start_v)>>7 & 0x0004) | ((stop_v)>>8 & 0x0002) | \
1059 ((start_h)>>2 & 0x0001))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060
1061/* get current vertical position of beam */
1062#define get_vbpos() ((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
1063
1064 /*
1065 * Copper Initialisation List
1066 */
1067
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001068#define COPINITSIZE (sizeof(copins) * 40)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069
1070enum {
1071 cip_bplcon0
1072};
1073
1074 /*
1075 * Long Frame/Short Frame Copper List
1076 * Don't change the order, build_copper()/rebuild_copper() rely on this
1077 */
1078
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001079#define COPLISTSIZE (sizeof(copins) * 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080
1081enum {
1082 cop_wait, cop_bplcon0,
1083 cop_spr0ptrh, cop_spr0ptrl,
1084 cop_diwstrt, cop_diwstop,
1085 cop_diwhigh,
1086};
1087
1088 /*
1089 * Pixel modes for Bitplanes and Sprites
1090 */
1091
1092static u_short bplpixmode[3] = {
1093 BPC0_SHRES, /* 35 ns */
1094 BPC0_HIRES, /* 70 ns */
1095 0 /* 140 ns */
1096};
1097
1098static u_short sprpixmode[3] = {
1099 BPC3_SPRES1 | BPC3_SPRES0, /* 35 ns */
1100 BPC3_SPRES1, /* 70 ns */
1101 BPC3_SPRES0 /* 140 ns */
1102};
1103
1104 /*
1105 * Fetch modes for Bitplanes and Sprites
1106 */
1107
1108static u_short bplfetchmode[3] = {
1109 0, /* 1x */
1110 FMODE_BPL32, /* 2x */
1111 FMODE_BPAGEM | FMODE_BPL32 /* 4x */
1112};
1113
1114static u_short sprfetchmode[3] = {
1115 0, /* 1x */
1116 FMODE_SPR32, /* 2x */
1117 FMODE_SPAGEM | FMODE_SPR32 /* 4x */
1118};
1119
1120
1121 /*
1122 * Interface used by the world
1123 */
1124
1125int amifb_setup(char*);
1126
1127static int amifb_check_var(struct fb_var_screeninfo *var,
1128 struct fb_info *info);
1129static int amifb_set_par(struct fb_info *info);
1130static int amifb_setcolreg(unsigned regno, unsigned red, unsigned green,
1131 unsigned blue, unsigned transp,
1132 struct fb_info *info);
1133static int amifb_blank(int blank, struct fb_info *info);
1134static int amifb_pan_display(struct fb_var_screeninfo *var,
1135 struct fb_info *info);
1136static void amifb_fillrect(struct fb_info *info,
1137 const struct fb_fillrect *rect);
1138static void amifb_copyarea(struct fb_info *info,
1139 const struct fb_copyarea *region);
1140static void amifb_imageblit(struct fb_info *info,
1141 const struct fb_image *image);
Christoph Hellwig67a66802006-01-14 13:21:25 -08001142static int amifb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
1144
1145 /*
1146 * Interface to the low level console driver
1147 */
1148
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02001149static void amifb_deinit(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150
1151 /*
1152 * Internal routines
1153 */
1154
1155static int flash_cursor(void);
David Howells7d12e782006-10-05 14:55:46 +01001156static irqreturn_t amifb_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157static u_long chipalloc(u_long size);
1158static void chipfree(void);
1159
1160 /*
1161 * Hardware routines
1162 */
1163
1164static int ami_decode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001165 struct amifb_par *par);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166static int ami_encode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001167 struct amifb_par *par);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168static void ami_pan_var(struct fb_var_screeninfo *var);
1169static int ami_update_par(void);
1170static void ami_update_display(void);
1171static void ami_init_display(void);
1172static void ami_do_blank(void);
1173static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix);
Al Viro3728d252006-01-12 01:06:31 -08001174static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data);
1175static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176static int ami_get_cursorstate(struct fb_cursorstate *state);
1177static int ami_set_cursorstate(struct fb_cursorstate *state);
1178static void ami_set_sprite(void);
1179static void ami_init_copper(void);
1180static void ami_reinit_copper(void);
1181static void ami_build_copper(void);
1182static void ami_rebuild_copper(void);
1183
1184
1185static struct fb_ops amifb_ops = {
1186 .owner = THIS_MODULE,
1187 .fb_check_var = amifb_check_var,
1188 .fb_set_par = amifb_set_par,
1189 .fb_setcolreg = amifb_setcolreg,
1190 .fb_blank = amifb_blank,
1191 .fb_pan_display = amifb_pan_display,
1192 .fb_fillrect = amifb_fillrect,
1193 .fb_copyarea = amifb_copyarea,
1194 .fb_imageblit = amifb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 .fb_ioctl = amifb_ioctl,
1196};
1197
1198static void __init amifb_setup_mcap(char *spec)
1199{
1200 char *p;
1201 int vmin, vmax, hmin, hmax;
1202
1203 /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
1204 * <V*> vertical freq. in Hz
1205 * <H*> horizontal freq. in kHz
1206 */
1207
1208 if (!(p = strsep(&spec, ";")) || !*p)
1209 return;
1210 vmin = simple_strtoul(p, NULL, 10);
1211 if (vmin <= 0)
1212 return;
1213 if (!(p = strsep(&spec, ";")) || !*p)
1214 return;
1215 vmax = simple_strtoul(p, NULL, 10);
1216 if (vmax <= 0 || vmax <= vmin)
1217 return;
1218 if (!(p = strsep(&spec, ";")) || !*p)
1219 return;
1220 hmin = 1000 * simple_strtoul(p, NULL, 10);
1221 if (hmin <= 0)
1222 return;
1223 if (!(p = strsep(&spec, "")) || !*p)
1224 return;
1225 hmax = 1000 * simple_strtoul(p, NULL, 10);
1226 if (hmax <= 0 || hmax <= hmin)
1227 return;
1228
1229 fb_info.monspecs.vfmin = vmin;
1230 fb_info.monspecs.vfmax = vmax;
1231 fb_info.monspecs.hfmin = hmin;
1232 fb_info.monspecs.hfmax = hmax;
1233}
1234
1235int __init amifb_setup(char *options)
1236{
1237 char *this_opt;
1238
1239 if (!options || !*options)
1240 return 0;
1241
1242 while ((this_opt = strsep(&options, ",")) != NULL) {
1243 if (!*this_opt)
1244 continue;
1245 if (!strcmp(this_opt, "inverse")) {
1246 amifb_inverse = 1;
1247 fb_invert_cmaps();
1248 } else if (!strcmp(this_opt, "ilbm"))
1249 amifb_ilbm = 1;
1250 else if (!strncmp(this_opt, "monitorcap:", 11))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001251 amifb_setup_mcap(this_opt + 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 else if (!strncmp(this_opt, "fstart:", 7))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001253 min_fstrt = simple_strtoul(this_opt + 7, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254 else
1255 mode_option = this_opt;
1256 }
1257
1258 if (min_fstrt < 48)
1259 min_fstrt = 48;
1260
1261 return 0;
1262}
1263
1264
1265static int amifb_check_var(struct fb_var_screeninfo *var,
1266 struct fb_info *info)
1267{
1268 int err;
1269 struct amifb_par par;
1270
1271 /* Validate wanted screen parameters */
1272 if ((err = ami_decode_var(var, &par)))
1273 return err;
1274
1275 /* Encode (possibly rounded) screen parameters */
1276 ami_encode_var(var, &par);
1277 return 0;
1278}
1279
1280
1281static int amifb_set_par(struct fb_info *info)
1282{
1283 struct amifb_par *par = (struct amifb_par *)info->par;
1284
1285 do_vmode_pan = 0;
1286 do_vmode_full = 0;
1287
1288 /* Decode wanted screen parameters */
1289 ami_decode_var(&info->var, par);
1290
1291 /* Set new videomode */
1292 ami_build_copper();
1293
1294 /* Set VBlank trigger */
1295 do_vmode_full = 1;
1296
1297 /* Update fix for new screen parameters */
1298 if (par->bpp == 1) {
1299 info->fix.type = FB_TYPE_PACKED_PIXELS;
1300 info->fix.type_aux = 0;
1301 } else if (amifb_ilbm) {
1302 info->fix.type = FB_TYPE_INTERLEAVED_PLANES;
1303 info->fix.type_aux = par->next_line;
1304 } else {
1305 info->fix.type = FB_TYPE_PLANES;
1306 info->fix.type_aux = 0;
1307 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001308 info->fix.line_length = div8(upx(16 << maxfmode, par->vxres));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
1310 if (par->vmode & FB_VMODE_YWRAP) {
1311 info->fix.ywrapstep = 1;
1312 info->fix.xpanstep = 0;
1313 info->fix.ypanstep = 0;
1314 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YWRAP |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001315 FBINFO_READS_FAST; /* override SCROLL_REDRAW */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 } else {
1317 info->fix.ywrapstep = 0;
1318 if (par->vmode & FB_VMODE_SMOOTH_XPAN)
1319 info->fix.xpanstep = 1;
1320 else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001321 info->fix.xpanstep = 16 << maxfmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 info->fix.ypanstep = 1;
1323 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
1324 }
1325 return 0;
1326}
1327
1328
1329 /*
1330 * Pan or Wrap the Display
1331 *
1332 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
1333 */
1334
1335static int amifb_pan_display(struct fb_var_screeninfo *var,
1336 struct fb_info *info)
1337{
1338 if (var->vmode & FB_VMODE_YWRAP) {
1339 if (var->yoffset < 0 ||
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001340 var->yoffset >= info->var.yres_virtual || var->xoffset)
1341 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 } else {
1343 /*
1344 * TODO: There will be problems when xpan!=1, so some columns
1345 * on the right side will never be seen
1346 */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001347 if (var->xoffset + info->var.xres >
1348 upx(16 << maxfmode, info->var.xres_virtual) ||
1349 var->yoffset + info->var.yres > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 return -EINVAL;
1351 }
1352 ami_pan_var(var);
1353 info->var.xoffset = var->xoffset;
1354 info->var.yoffset = var->yoffset;
1355 if (var->vmode & FB_VMODE_YWRAP)
1356 info->var.vmode |= FB_VMODE_YWRAP;
1357 else
1358 info->var.vmode &= ~FB_VMODE_YWRAP;
1359 return 0;
1360}
1361
1362
1363#if BITS_PER_LONG == 32
1364#define BYTES_PER_LONG 4
1365#define SHIFT_PER_LONG 5
1366#elif BITS_PER_LONG == 64
1367#define BYTES_PER_LONG 8
1368#define SHIFT_PER_LONG 6
1369#else
1370#define Please update me
1371#endif
1372
1373
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001374 /*
1375 * Compose two values, using a bitmask as decision value
1376 * This is equivalent to (a & mask) | (b & ~mask)
1377 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379static inline unsigned long comp(unsigned long a, unsigned long b,
1380 unsigned long mask)
1381{
1382 return ((a ^ b) & mask) ^ b;
1383}
1384
1385
1386static inline unsigned long xor(unsigned long a, unsigned long b,
1387 unsigned long mask)
1388{
1389 return (a & mask) ^ b;
1390}
1391
1392
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001393 /*
1394 * Unaligned forward bit copy using 32-bit or 64-bit memory accesses
1395 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
1397static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
1398 int src_idx, u32 n)
1399{
1400 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001401 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 unsigned long d0, d1;
1403 int m;
1404
1405 if (!n)
1406 return;
1407
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001408 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001410 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411
1412 if (!shift) {
1413 // Same alignment for source and dest
1414
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001415 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 // Single word
1417 if (last)
1418 first &= last;
1419 *dst = comp(*src, *dst, first);
1420 } else {
1421 // Multiple destination words
1422 // Leading bits
1423 if (first) {
1424 *dst = comp(*src, *dst, first);
1425 dst++;
1426 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001427 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 }
1429
1430 // Main chunk
1431 n /= BITS_PER_LONG;
1432 while (n >= 8) {
1433 *dst++ = *src++;
1434 *dst++ = *src++;
1435 *dst++ = *src++;
1436 *dst++ = *src++;
1437 *dst++ = *src++;
1438 *dst++ = *src++;
1439 *dst++ = *src++;
1440 *dst++ = *src++;
1441 n -= 8;
1442 }
1443 while (n--)
1444 *dst++ = *src++;
1445
1446 // Trailing bits
1447 if (last)
1448 *dst = comp(*src, *dst, last);
1449 }
1450 } else {
1451 // Different alignment for source and dest
1452
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001453 right = shift & (BITS_PER_LONG - 1);
1454 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001456 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457 // Single destination word
1458 if (last)
1459 first &= last;
1460 if (shift > 0) {
1461 // Single source word
1462 *dst = comp(*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001463 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 // Single source word
1465 *dst = comp(*src << left, *dst, first);
1466 } else {
1467 // 2 source words
1468 d0 = *src++;
1469 d1 = *src;
1470 *dst = comp(d0 << left | d1 >> right, *dst,
1471 first);
1472 }
1473 } else {
1474 // Multiple destination words
1475 d0 = *src++;
1476 // Leading bits
1477 if (shift > 0) {
1478 // Single source word
1479 *dst = comp(d0 >> right, *dst, first);
1480 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001481 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 } else {
1483 // 2 source words
1484 d1 = *src++;
1485 *dst = comp(d0 << left | d1 >> right, *dst,
1486 first);
1487 d0 = d1;
1488 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001489 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 }
1491
1492 // Main chunk
1493 m = n % BITS_PER_LONG;
1494 n /= BITS_PER_LONG;
1495 while (n >= 4) {
1496 d1 = *src++;
1497 *dst++ = d0 << left | d1 >> right;
1498 d0 = d1;
1499 d1 = *src++;
1500 *dst++ = d0 << left | d1 >> right;
1501 d0 = d1;
1502 d1 = *src++;
1503 *dst++ = d0 << left | d1 >> right;
1504 d0 = d1;
1505 d1 = *src++;
1506 *dst++ = d0 << left | d1 >> right;
1507 d0 = d1;
1508 n -= 4;
1509 }
1510 while (n--) {
1511 d1 = *src++;
1512 *dst++ = d0 << left | d1 >> right;
1513 d0 = d1;
1514 }
1515
1516 // Trailing bits
1517 if (last) {
1518 if (m <= right) {
1519 // Single source word
1520 *dst = comp(d0 << left, *dst, last);
1521 } else {
1522 // 2 source words
1523 d1 = *src;
1524 *dst = comp(d0 << left | d1 >> right,
1525 *dst, last);
1526 }
1527 }
1528 }
1529 }
1530}
1531
1532
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001533 /*
1534 * Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
1535 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537static void bitcpy_rev(unsigned long *dst, int dst_idx,
1538 const unsigned long *src, int src_idx, u32 n)
1539{
1540 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001541 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 unsigned long d0, d1;
1543 int m;
1544
1545 if (!n)
1546 return;
1547
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001548 dst += (n - 1) / BITS_PER_LONG;
1549 src += (n - 1) / BITS_PER_LONG;
1550 if ((n - 1) % BITS_PER_LONG) {
1551 dst_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001553 dst_idx &= BITS_PER_LONG - 1;
1554 src_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001556 src_idx &= BITS_PER_LONG - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 }
1558
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001559 shift = dst_idx - src_idx;
1560 first = ~0UL << (BITS_PER_LONG - 1 - dst_idx);
1561 last = ~(~0UL << (BITS_PER_LONG - 1 - ((dst_idx - n) % BITS_PER_LONG)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562
1563 if (!shift) {
1564 // Same alignment for source and dest
1565
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001566 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 // Single word
1568 if (last)
1569 first &= last;
1570 *dst = comp(*src, *dst, first);
1571 } else {
1572 // Multiple destination words
1573 // Leading bits
1574 if (first) {
1575 *dst = comp(*src, *dst, first);
1576 dst--;
1577 src--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001578 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 }
1580
1581 // Main chunk
1582 n /= BITS_PER_LONG;
1583 while (n >= 8) {
1584 *dst-- = *src--;
1585 *dst-- = *src--;
1586 *dst-- = *src--;
1587 *dst-- = *src--;
1588 *dst-- = *src--;
1589 *dst-- = *src--;
1590 *dst-- = *src--;
1591 *dst-- = *src--;
1592 n -= 8;
1593 }
1594 while (n--)
1595 *dst-- = *src--;
1596
1597 // Trailing bits
1598 if (last)
1599 *dst = comp(*src, *dst, last);
1600 }
1601 } else {
1602 // Different alignment for source and dest
1603
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001604 right = shift & (BITS_PER_LONG - 1);
1605 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001607 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 // Single destination word
1609 if (last)
1610 first &= last;
1611 if (shift < 0) {
1612 // Single source word
1613 *dst = comp(*src << left, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001614 } else if (1 + (unsigned long)src_idx >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 // Single source word
1616 *dst = comp(*src >> right, *dst, first);
1617 } else {
1618 // 2 source words
1619 d0 = *src--;
1620 d1 = *src;
1621 *dst = comp(d0 >> right | d1 << left, *dst,
1622 first);
1623 }
1624 } else {
1625 // Multiple destination words
1626 d0 = *src--;
1627 // Leading bits
1628 if (shift < 0) {
1629 // Single source word
1630 *dst = comp(d0 << left, *dst, first);
1631 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001632 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633 } else {
1634 // 2 source words
1635 d1 = *src--;
1636 *dst = comp(d0 >> right | d1 << left, *dst,
1637 first);
1638 d0 = d1;
1639 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001640 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 }
1642
1643 // Main chunk
1644 m = n % BITS_PER_LONG;
1645 n /= BITS_PER_LONG;
1646 while (n >= 4) {
1647 d1 = *src--;
1648 *dst-- = d0 >> right | d1 << left;
1649 d0 = d1;
1650 d1 = *src--;
1651 *dst-- = d0 >> right | d1 << left;
1652 d0 = d1;
1653 d1 = *src--;
1654 *dst-- = d0 >> right | d1 << left;
1655 d0 = d1;
1656 d1 = *src--;
1657 *dst-- = d0 >> right | d1 << left;
1658 d0 = d1;
1659 n -= 4;
1660 }
1661 while (n--) {
1662 d1 = *src--;
1663 *dst-- = d0 >> right | d1 << left;
1664 d0 = d1;
1665 }
1666
1667 // Trailing bits
1668 if (last) {
1669 if (m <= left) {
1670 // Single source word
1671 *dst = comp(d0 >> right, *dst, last);
1672 } else {
1673 // 2 source words
1674 d1 = *src;
1675 *dst = comp(d0 >> right | d1 << left,
1676 *dst, last);
1677 }
1678 }
1679 }
1680 }
1681}
1682
1683
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001684 /*
1685 * Unaligned forward inverting bit copy using 32-bit or 64-bit memory
1686 * accesses
1687 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001688
1689static void bitcpy_not(unsigned long *dst, int dst_idx,
1690 const unsigned long *src, int src_idx, u32 n)
1691{
1692 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001693 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 unsigned long d0, d1;
1695 int m;
1696
1697 if (!n)
1698 return;
1699
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001700 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001702 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703
1704 if (!shift) {
1705 // Same alignment for source and dest
1706
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001707 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 // Single word
1709 if (last)
1710 first &= last;
1711 *dst = comp(~*src, *dst, first);
1712 } else {
1713 // Multiple destination words
1714 // Leading bits
1715 if (first) {
1716 *dst = comp(~*src, *dst, first);
1717 dst++;
1718 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001719 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 }
1721
1722 // Main chunk
1723 n /= BITS_PER_LONG;
1724 while (n >= 8) {
1725 *dst++ = ~*src++;
1726 *dst++ = ~*src++;
1727 *dst++ = ~*src++;
1728 *dst++ = ~*src++;
1729 *dst++ = ~*src++;
1730 *dst++ = ~*src++;
1731 *dst++ = ~*src++;
1732 *dst++ = ~*src++;
1733 n -= 8;
1734 }
1735 while (n--)
1736 *dst++ = ~*src++;
1737
1738 // Trailing bits
1739 if (last)
1740 *dst = comp(~*src, *dst, last);
1741 }
1742 } else {
1743 // Different alignment for source and dest
1744
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001745 right = shift & (BITS_PER_LONG - 1);
1746 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001748 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 // Single destination word
1750 if (last)
1751 first &= last;
1752 if (shift > 0) {
1753 // Single source word
1754 *dst = comp(~*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001755 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 // Single source word
1757 *dst = comp(~*src << left, *dst, first);
1758 } else {
1759 // 2 source words
1760 d0 = ~*src++;
1761 d1 = ~*src;
1762 *dst = comp(d0 << left | d1 >> right, *dst,
1763 first);
1764 }
1765 } else {
1766 // Multiple destination words
1767 d0 = ~*src++;
1768 // Leading bits
1769 if (shift > 0) {
1770 // Single source word
1771 *dst = comp(d0 >> right, *dst, first);
1772 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001773 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001774 } else {
1775 // 2 source words
1776 d1 = ~*src++;
1777 *dst = comp(d0 << left | d1 >> right, *dst,
1778 first);
1779 d0 = d1;
1780 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001781 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 }
1783
1784 // Main chunk
1785 m = n % BITS_PER_LONG;
1786 n /= BITS_PER_LONG;
1787 while (n >= 4) {
1788 d1 = ~*src++;
1789 *dst++ = d0 << left | d1 >> right;
1790 d0 = d1;
1791 d1 = ~*src++;
1792 *dst++ = d0 << left | d1 >> right;
1793 d0 = d1;
1794 d1 = ~*src++;
1795 *dst++ = d0 << left | d1 >> right;
1796 d0 = d1;
1797 d1 = ~*src++;
1798 *dst++ = d0 << left | d1 >> right;
1799 d0 = d1;
1800 n -= 4;
1801 }
1802 while (n--) {
1803 d1 = ~*src++;
1804 *dst++ = d0 << left | d1 >> right;
1805 d0 = d1;
1806 }
1807
1808 // Trailing bits
1809 if (last) {
1810 if (m <= right) {
1811 // Single source word
1812 *dst = comp(d0 << left, *dst, last);
1813 } else {
1814 // 2 source words
1815 d1 = ~*src;
1816 *dst = comp(d0 << left | d1 >> right,
1817 *dst, last);
1818 }
1819 }
1820 }
1821 }
1822}
1823
1824
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001825 /*
1826 * Unaligned 32-bit pattern fill using 32/64-bit memory accesses
1827 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828
1829static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
1830{
1831 unsigned long val = pat;
1832 unsigned long first, last;
1833
1834 if (!n)
1835 return;
1836
1837#if BITS_PER_LONG == 64
1838 val |= val << 32;
1839#endif
1840
1841 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001842 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001844 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 // Single word
1846 if (last)
1847 first &= last;
1848 *dst = comp(val, *dst, first);
1849 } else {
1850 // Multiple destination words
1851 // Leading bits
1852 if (first) {
1853 *dst = comp(val, *dst, first);
1854 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001855 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 }
1857
1858 // Main chunk
1859 n /= BITS_PER_LONG;
1860 while (n >= 8) {
1861 *dst++ = val;
1862 *dst++ = val;
1863 *dst++ = val;
1864 *dst++ = val;
1865 *dst++ = val;
1866 *dst++ = val;
1867 *dst++ = val;
1868 *dst++ = val;
1869 n -= 8;
1870 }
1871 while (n--)
1872 *dst++ = val;
1873
1874 // Trailing bits
1875 if (last)
1876 *dst = comp(val, *dst, last);
1877 }
1878}
1879
1880
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001881 /*
1882 * Unaligned 32-bit pattern xor using 32/64-bit memory accesses
1883 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884
1885static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
1886{
1887 unsigned long val = pat;
1888 unsigned long first, last;
1889
1890 if (!n)
1891 return;
1892
1893#if BITS_PER_LONG == 64
1894 val |= val << 32;
1895#endif
1896
1897 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001898 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001900 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 // Single word
1902 if (last)
1903 first &= last;
1904 *dst = xor(val, *dst, first);
1905 } else {
1906 // Multiple destination words
1907 // Leading bits
1908 if (first) {
1909 *dst = xor(val, *dst, first);
1910 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001911 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 }
1913
1914 // Main chunk
1915 n /= BITS_PER_LONG;
1916 while (n >= 4) {
1917 *dst++ ^= val;
1918 *dst++ ^= val;
1919 *dst++ ^= val;
1920 *dst++ ^= val;
1921 n -= 4;
1922 }
1923 while (n--)
1924 *dst++ ^= val;
1925
1926 // Trailing bits
1927 if (last)
1928 *dst = xor(val, *dst, last);
1929 }
1930}
1931
1932static inline void fill_one_line(int bpp, unsigned long next_plane,
1933 unsigned long *dst, int dst_idx, u32 n,
1934 u32 color)
1935{
1936 while (1) {
1937 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001938 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
1940 if (!--bpp)
1941 break;
1942 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001943 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944 }
1945}
1946
1947static inline void xor_one_line(int bpp, unsigned long next_plane,
1948 unsigned long *dst, int dst_idx, u32 n,
1949 u32 color)
1950{
1951 while (color) {
1952 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001953 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001954 bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
1955 if (!--bpp)
1956 break;
1957 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001958 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959 }
1960}
1961
1962
1963static void amifb_fillrect(struct fb_info *info,
1964 const struct fb_fillrect *rect)
1965{
1966 struct amifb_par *par = (struct amifb_par *)info->par;
1967 int dst_idx, x2, y2;
1968 unsigned long *dst;
1969 u32 width, height;
1970
1971 if (!rect->width || !rect->height)
1972 return;
1973
1974 /*
1975 * We could use hardware clipping but on many cards you get around
1976 * hardware clipping by writing to framebuffer directly.
1977 * */
1978 x2 = rect->dx + rect->width;
1979 y2 = rect->dy + rect->height;
1980 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
1981 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
1982 width = x2 - rect->dx;
1983 height = y2 - rect->dy;
1984
1985 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001986 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
1987 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
1988 dst_idx += rect->dy * par->next_line * 8 + rect->dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989 while (height--) {
1990 switch (rect->rop) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001991 case ROP_COPY:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 fill_one_line(info->var.bits_per_pixel,
1993 par->next_plane, dst, dst_idx, width,
1994 rect->color);
1995 break;
1996
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001997 case ROP_XOR:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 xor_one_line(info->var.bits_per_pixel, par->next_plane,
1999 dst, dst_idx, width, rect->color);
2000 break;
2001 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002002 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 }
2004}
2005
2006static inline void copy_one_line(int bpp, unsigned long next_plane,
2007 unsigned long *dst, int dst_idx,
2008 unsigned long *src, int src_idx, u32 n)
2009{
2010 while (1) {
2011 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002012 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002013 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002014 src_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 bitcpy(dst, dst_idx, src, src_idx, n);
2016 if (!--bpp)
2017 break;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002018 dst_idx += next_plane * 8;
2019 src_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 }
2021}
2022
2023static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
2024 unsigned long *dst, int dst_idx,
2025 unsigned long *src, int src_idx, u32 n)
2026{
2027 while (1) {
2028 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002029 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002031 src_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 bitcpy_rev(dst, dst_idx, src, src_idx, n);
2033 if (!--bpp)
2034 break;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002035 dst_idx += next_plane * 8;
2036 src_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 }
2038}
2039
2040
2041static void amifb_copyarea(struct fb_info *info,
2042 const struct fb_copyarea *area)
2043{
2044 struct amifb_par *par = (struct amifb_par *)info->par;
2045 int x2, y2;
2046 u32 dx, dy, sx, sy, width, height;
2047 unsigned long *dst, *src;
2048 int dst_idx, src_idx;
2049 int rev_copy = 0;
2050
2051 /* clip the destination */
2052 x2 = area->dx + area->width;
2053 y2 = area->dy + area->height;
2054 dx = area->dx > 0 ? area->dx : 0;
2055 dy = area->dy > 0 ? area->dy : 0;
2056 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
2057 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
2058 width = x2 - dx;
2059 height = y2 - dy;
2060
Roel Kluin091c82c2008-07-23 21:31:18 -07002061 if (area->sx + dx < area->dx || area->sy + dy < area->dy)
2062 return;
2063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064 /* update sx,sy */
2065 sx = area->sx + (dx - area->dx);
2066 sy = area->sy + (dy - area->dy);
2067
2068 /* the source must be completely inside the virtual screen */
Roel Kluin091c82c2008-07-23 21:31:18 -07002069 if (sx + width > info->var.xres_virtual ||
2070 sy + height > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 return;
2072
2073 if (dy > sy || (dy == sy && dx > sx)) {
2074 dy += height;
2075 sy += height;
2076 rev_copy = 1;
2077 }
2078 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002079 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 src = dst;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002081 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 src_idx = dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002083 dst_idx += dy * par->next_line * 8 + dx;
2084 src_idx += sy * par->next_line * 8 + sx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 if (rev_copy) {
2086 while (height--) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002087 dst_idx -= par->next_line * 8;
2088 src_idx -= par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 copy_one_line_rev(info->var.bits_per_pixel,
2090 par->next_plane, dst, dst_idx, src,
2091 src_idx, width);
2092 }
2093 } else {
2094 while (height--) {
2095 copy_one_line(info->var.bits_per_pixel,
2096 par->next_plane, dst, dst_idx, src,
2097 src_idx, width);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002098 dst_idx += par->next_line * 8;
2099 src_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 }
2101 }
2102}
2103
2104
2105static inline void expand_one_line(int bpp, unsigned long next_plane,
2106 unsigned long *dst, int dst_idx, u32 n,
2107 const u8 *data, u32 bgcolor, u32 fgcolor)
2108{
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002109 const unsigned long *src;
2110 int src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002112 while (1) {
2113 dst += dst_idx >> SHIFT_PER_LONG;
2114 dst_idx &= (BITS_PER_LONG - 1);
2115 if ((bgcolor ^ fgcolor) & 1) {
2116 src = (unsigned long *)
2117 ((unsigned long)data & ~(BYTES_PER_LONG - 1));
2118 src_idx = ((unsigned long)data & (BYTES_PER_LONG - 1)) * 8;
2119 if (fgcolor & 1)
2120 bitcpy(dst, dst_idx, src, src_idx, n);
2121 else
2122 bitcpy_not(dst, dst_idx, src, src_idx, n);
2123 /* set or clear */
2124 } else
2125 bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
2126 if (!--bpp)
2127 break;
2128 bgcolor >>= 1;
2129 fgcolor >>= 1;
2130 dst_idx += next_plane * 8;
2131 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132}
2133
2134
2135static void amifb_imageblit(struct fb_info *info, const struct fb_image *image)
2136{
2137 struct amifb_par *par = (struct amifb_par *)info->par;
2138 int x2, y2;
2139 unsigned long *dst;
2140 int dst_idx;
2141 const char *src;
2142 u32 dx, dy, width, height, pitch;
2143
2144 /*
2145 * We could use hardware clipping but on many cards you get around
2146 * hardware clipping by writing to framebuffer directly like we are
2147 * doing here.
2148 */
2149 x2 = image->dx + image->width;
2150 y2 = image->dy + image->height;
2151 dx = image->dx;
2152 dy = image->dy;
2153 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
2154 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
2155 width = x2 - dx;
2156 height = y2 - dy;
2157
2158 if (image->depth == 1) {
2159 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002160 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
2161 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
2162 dst_idx += dy * par->next_line * 8 + dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 src = image->data;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002164 pitch = (image->width + 7) / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 while (height--) {
2166 expand_one_line(info->var.bits_per_pixel,
2167 par->next_plane, dst, dst_idx, width,
2168 src, image->bg_color,
2169 image->fg_color);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002170 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 src += pitch;
2172 }
2173 } else {
Geert Uytterhoeven2eab7ff2008-12-21 15:48:13 +01002174 c2p_planar(info->screen_base, image->data, dx, dy, width,
2175 height, par->next_line, par->next_plane,
2176 image->width, info->var.bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 }
2178}
2179
2180
2181 /*
2182 * Amiga Frame Buffer Specific ioctls
2183 */
2184
Christoph Hellwig67a66802006-01-14 13:21:25 -08002185static int amifb_ioctl(struct fb_info *info,
2186 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187{
2188 union {
2189 struct fb_fix_cursorinfo fix;
2190 struct fb_var_cursorinfo var;
2191 struct fb_cursorstate state;
2192 } crsr;
Al Viro3728d252006-01-12 01:06:31 -08002193 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 int i;
2195
2196 switch (cmd) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002197 case FBIOGET_FCURSORINFO:
2198 i = ami_get_fix_cursorinfo(&crsr.fix);
2199 if (i)
2200 return i;
2201 return copy_to_user(argp, &crsr.fix,
2202 sizeof(crsr.fix)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002204 case FBIOGET_VCURSORINFO:
2205 i = ami_get_var_cursorinfo(&crsr.var,
2206 ((struct fb_var_cursorinfo __user *)arg)->data);
2207 if (i)
2208 return i;
2209 return copy_to_user(argp, &crsr.var,
2210 sizeof(crsr.var)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002212 case FBIOPUT_VCURSORINFO:
2213 if (copy_from_user(&crsr.var, argp, sizeof(crsr.var)))
2214 return -EFAULT;
2215 return ami_set_var_cursorinfo(&crsr.var,
2216 ((struct fb_var_cursorinfo __user *)arg)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002218 case FBIOGET_CURSORSTATE:
2219 i = ami_get_cursorstate(&crsr.state);
2220 if (i)
2221 return i;
2222 return copy_to_user(argp, &crsr.state,
2223 sizeof(crsr.state)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002225 case FBIOPUT_CURSORSTATE:
2226 if (copy_from_user(&crsr.state, argp, sizeof(crsr.state)))
2227 return -EFAULT;
2228 return ami_set_cursorstate(&crsr.state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229 }
2230 return -EINVAL;
2231}
2232
2233
2234 /*
2235 * Allocate, Clear and Align a Block of Chip Memory
2236 */
2237
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00002238static void *aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239
2240static inline u_long __init chipalloc(u_long size)
2241{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00002242 aligned_chipptr = amiga_chip_alloc(size, "amifb [RAM]");
2243 if (!aligned_chipptr) {
Geert Uytterhoevena7076422011-05-21 19:42:55 +00002244 pr_err("amifb: No Chip RAM for frame buffer");
2245 return 0;
2246 }
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00002247 memset(aligned_chipptr, 0, size);
2248 return (u_long)aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249}
2250
2251static inline void chipfree(void)
2252{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00002253 if (aligned_chipptr)
2254 amiga_chip_free(aligned_chipptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255}
2256
2257
2258 /*
2259 * Initialisation
2260 */
2261
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02002262static int __init amifb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263{
2264 int tag, i, err = 0;
2265 u_long chipptr;
2266 u_int defmode;
2267
2268#ifndef MODULE
2269 char *option = NULL;
2270
2271 if (fb_get_options("amifb", &option)) {
2272 amifb_video_off();
2273 return -ENODEV;
2274 }
2275 amifb_setup(option);
2276#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 custom.dmacon = DMAF_ALL | DMAF_MASTER;
2278
2279 switch (amiga_chipset) {
2280#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002281 case CS_OCS:
2282 strcat(fb_info.fix.id, "OCS");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283default_chipset:
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002284 chipset = TAG_OCS;
2285 maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */
2286 maxdepth[TAG_HIRES] = 4;
2287 maxdepth[TAG_LORES] = 6;
2288 maxfmode = TAG_FMODE_1;
2289 defmode = amiga_vblank == 50 ? DEFMODE_PAL : DEFMODE_NTSC;
2290 fb_info.fix.smem_len = VIDEOMEMSIZE_OCS;
2291 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292#endif /* CONFIG_FB_AMIGA_OCS */
2293
2294#ifdef CONFIG_FB_AMIGA_ECS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002295 case CS_ECS:
2296 strcat(fb_info.fix.id, "ECS");
2297 chipset = TAG_ECS;
2298 maxdepth[TAG_SHRES] = 2;
2299 maxdepth[TAG_HIRES] = 4;
2300 maxdepth[TAG_LORES] = 6;
2301 maxfmode = TAG_FMODE_1;
2302 if (AMIGAHW_PRESENT(AMBER_FF))
2303 defmode = amiga_vblank == 50 ? DEFMODE_AMBER_PAL
2304 : DEFMODE_AMBER_NTSC;
2305 else
2306 defmode = amiga_vblank == 50 ? DEFMODE_PAL
2307 : DEFMODE_NTSC;
2308 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
2309 VIDEOMEMSIZE_ECS_2M)
2310 fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_2M;
2311 else
2312 fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_1M;
2313 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314#endif /* CONFIG_FB_AMIGA_ECS */
2315
2316#ifdef CONFIG_FB_AMIGA_AGA
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002317 case CS_AGA:
2318 strcat(fb_info.fix.id, "AGA");
2319 chipset = TAG_AGA;
2320 maxdepth[TAG_SHRES] = 8;
2321 maxdepth[TAG_HIRES] = 8;
2322 maxdepth[TAG_LORES] = 8;
2323 maxfmode = TAG_FMODE_4;
2324 defmode = DEFMODE_AGA;
2325 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
2326 VIDEOMEMSIZE_AGA_2M)
2327 fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_2M;
2328 else
2329 fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_1M;
2330 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331#endif /* CONFIG_FB_AMIGA_AGA */
2332
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002333 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002335 printk("Unknown graphics chipset, defaulting to OCS\n");
2336 strcat(fb_info.fix.id, "Unknown");
2337 goto default_chipset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338#else /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002339 err = -ENODEV;
2340 goto amifb_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341#endif /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002342 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 }
2344
2345 /*
2346 * Calculate the Pixel Clock Values for this Machine
2347 */
2348
2349 {
2350 u_long tmp = DIVUL(200000000000ULL, amiga_eclock);
2351
2352 pixclock[TAG_SHRES] = (tmp + 4) / 8; /* SHRES: 35 ns / 28 MHz */
2353 pixclock[TAG_HIRES] = (tmp + 2) / 4; /* HIRES: 70 ns / 14 MHz */
2354 pixclock[TAG_LORES] = (tmp + 1) / 2; /* LORES: 140 ns / 7 MHz */
2355 }
2356
2357 /*
2358 * Replace the Tag Values with the Real Pixel Clock Values
2359 */
2360
2361 for (i = 0; i < NUM_TOTAL_MODES; i++) {
2362 struct fb_videomode *mode = &ami_modedb[i];
2363 tag = mode->pixclock;
2364 if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
2365 mode->pixclock = pixclock[tag];
2366 }
2367 }
2368
2369 /*
2370 * These monitor specs are for a typical Amiga monitor (e.g. A1960)
2371 */
2372 if (fb_info.monspecs.hfmin == 0) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002373 fb_info.monspecs.hfmin = 15000;
2374 fb_info.monspecs.hfmax = 38000;
2375 fb_info.monspecs.vfmin = 49;
2376 fb_info.monspecs.vfmax = 90;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377 }
2378
2379 fb_info.fbops = &amifb_ops;
2380 fb_info.par = &currentpar;
2381 fb_info.flags = FBINFO_DEFAULT;
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02002382 fb_info.device = &pdev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383
2384 if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, ami_modedb,
2385 NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
2386 err = -EINVAL;
2387 goto amifb_error;
2388 }
2389
Geert Uytterhoevendb3e5282008-07-17 21:16:18 +02002390 fb_videomode_to_modelist(ami_modedb, NUM_TOTAL_MODES,
2391 &fb_info.modelist);
2392
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 round_down_bpp = 0;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002394 chipptr = chipalloc(fb_info.fix.smem_len + SPRITEMEMSIZE +
2395 DUMMYSPRITEMEMSIZE + COPINITSIZE +
2396 4 * COPLISTSIZE);
Geert Uytterhoevena7076422011-05-21 19:42:55 +00002397 if (!chipptr) {
2398 err = -ENOMEM;
2399 goto amifb_error;
2400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401
2402 assignchunk(videomemory, u_long, chipptr, fb_info.fix.smem_len);
2403 assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
2404 assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
2405 assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
2406 assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
2407 assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
2408 assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
2409 assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
2410
2411 /*
2412 * access the videomem with writethrough cache
2413 */
2414 fb_info.fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
2415 videomemory = (u_long)ioremap_writethrough(fb_info.fix.smem_start,
2416 fb_info.fix.smem_len);
2417 if (!videomemory) {
2418 printk("amifb: WARNING! unable to map videomem cached writethrough\n");
Amol Lad57354c42006-12-08 02:40:16 -08002419 fb_info.screen_base = (char *)ZTWO_VADDR(fb_info.fix.smem_start);
2420 } else
2421 fb_info.screen_base = (char *)videomemory;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
2424
2425 /*
2426 * Enable Display DMA
2427 */
2428
2429 custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002430 DMAF_BLITTER | DMAF_SPRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431
2432 /*
2433 * Make sure the Copper has something to do
2434 */
2435
2436 ami_init_copper();
2437
2438 if (request_irq(IRQ_AMIGA_COPPER, amifb_interrupt, 0,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002439 "fb vertb handler", &currentpar)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 err = -EBUSY;
2441 goto amifb_error;
2442 }
2443
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002444 err = fb_alloc_cmap(&fb_info.cmap, 1 << fb_info.var.bits_per_pixel, 0);
Andres Salomoneb8972b2009-03-31 15:25:30 -07002445 if (err)
2446 goto amifb_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447
2448 if (register_framebuffer(&fb_info) < 0) {
2449 err = -EINVAL;
2450 goto amifb_error;
2451 }
2452
2453 printk("fb%d: %s frame buffer device, using %dK of video memory\n",
2454 fb_info.node, fb_info.fix.id, fb_info.fix.smem_len>>10);
2455
2456 return 0;
2457
2458amifb_error:
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02002459 amifb_deinit(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002460 return err;
2461}
2462
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02002463static void amifb_deinit(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464{
Andres Salomoneb8972b2009-03-31 15:25:30 -07002465 if (fb_info.cmap.len)
2466 fb_dealloc_cmap(&fb_info.cmap);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02002467 fb_dealloc_cmap(&fb_info.cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 chipfree();
Amol Lad57354c42006-12-08 02:40:16 -08002469 if (videomemory)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002470 iounmap((void *)videomemory);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 custom.dmacon = DMAF_ALL | DMAF_MASTER;
2472}
2473
2474
2475 /*
2476 * Blank the display.
2477 */
2478
2479static int amifb_blank(int blank, struct fb_info *info)
2480{
2481 do_blank = blank ? blank : -1;
2482
2483 return 0;
2484}
2485
2486 /*
2487 * Flash the cursor (called by VBlank interrupt)
2488 */
2489
2490static int flash_cursor(void)
2491{
2492 static int cursorcount = 1;
2493
2494 if (cursormode == FB_CURSOR_FLASH) {
2495 if (!--cursorcount) {
2496 cursorstate = -cursorstate;
2497 cursorcount = cursorrate;
2498 if (!is_blanked)
2499 return 1;
2500 }
2501 }
2502 return 0;
2503}
2504
2505 /*
2506 * VBlank Display Interrupt
2507 */
2508
David Howells7d12e782006-10-05 14:55:46 +01002509static irqreturn_t amifb_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510{
2511 if (do_vmode_pan || do_vmode_full)
2512 ami_update_display();
2513
2514 if (do_vmode_full)
2515 ami_init_display();
2516
2517 if (do_vmode_pan) {
2518 flash_cursor();
2519 ami_rebuild_copper();
2520 do_cursor = do_vmode_pan = 0;
2521 } else if (do_cursor) {
2522 flash_cursor();
2523 ami_set_sprite();
2524 do_cursor = 0;
2525 } else {
2526 if (flash_cursor())
2527 ami_set_sprite();
2528 }
2529
2530 if (do_blank) {
2531 ami_do_blank();
2532 do_blank = 0;
2533 }
2534
2535 if (do_vmode_full) {
2536 ami_reinit_copper();
2537 do_vmode_full = 0;
2538 }
2539 return IRQ_HANDLED;
2540}
2541
2542/* --------------------------- Hardware routines --------------------------- */
2543
2544 /*
2545 * Get the video params out of `var'. If a value doesn't fit, round
2546 * it up, if it's too big, return -EINVAL.
2547 */
2548
2549static int ami_decode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002550 struct amifb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551{
2552 u_short clk_shift, line_shift;
2553 u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
2554 u_int htotal, vtotal;
2555
2556 /*
2557 * Find a matching Pixel Clock
2558 */
2559
2560 for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
2561 if (var->pixclock <= pixclock[clk_shift])
2562 break;
2563 if (clk_shift > TAG_LORES) {
2564 DPRINTK("pixclock too high\n");
2565 return -EINVAL;
2566 }
2567 par->clk_shift = clk_shift;
2568
2569 /*
2570 * Check the Geometry Values
2571 */
2572
2573 if ((par->xres = var->xres) < 64)
2574 par->xres = 64;
2575 if ((par->yres = var->yres) < 64)
2576 par->yres = 64;
2577 if ((par->vxres = var->xres_virtual) < par->xres)
2578 par->vxres = par->xres;
2579 if ((par->vyres = var->yres_virtual) < par->yres)
2580 par->vyres = par->yres;
2581
2582 par->bpp = var->bits_per_pixel;
2583 if (!var->nonstd) {
2584 if (par->bpp < 1)
2585 par->bpp = 1;
2586 if (par->bpp > maxdepth[clk_shift]) {
2587 if (round_down_bpp && maxdepth[clk_shift])
2588 par->bpp = maxdepth[clk_shift];
2589 else {
2590 DPRINTK("invalid bpp\n");
2591 return -EINVAL;
2592 }
2593 }
2594 } else if (var->nonstd == FB_NONSTD_HAM) {
2595 if (par->bpp < 6)
2596 par->bpp = 6;
2597 if (par->bpp != 6) {
2598 if (par->bpp < 8)
2599 par->bpp = 8;
2600 if (par->bpp != 8 || !IS_AGA) {
2601 DPRINTK("invalid bpp for ham mode\n");
2602 return -EINVAL;
2603 }
2604 }
2605 } else {
2606 DPRINTK("unknown nonstd mode\n");
2607 return -EINVAL;
2608 }
2609
2610 /*
2611 * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing
2612 * checks failed and smooth scrolling is not possible
2613 */
2614
2615 par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
2616 switch (par->vmode & FB_VMODE_MASK) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002617 case FB_VMODE_INTERLACED:
2618 line_shift = 0;
2619 break;
2620 case FB_VMODE_NONINTERLACED:
2621 line_shift = 1;
2622 break;
2623 case FB_VMODE_DOUBLE:
2624 if (!IS_AGA) {
2625 DPRINTK("double mode only possible with aga\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626 return -EINVAL;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002627 }
2628 line_shift = 2;
2629 break;
2630 default:
2631 DPRINTK("unknown video mode\n");
2632 return -EINVAL;
2633 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 }
2635 par->line_shift = line_shift;
2636
2637 /*
2638 * Vertical and Horizontal Timings
2639 */
2640
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002641 xres_n = par->xres << clk_shift;
2642 yres_n = par->yres << line_shift;
2643 par->htotal = down8((var->left_margin + par->xres + var->right_margin +
2644 var->hsync_len) << clk_shift);
2645 par->vtotal =
2646 down2(((var->upper_margin + par->yres + var->lower_margin +
2647 var->vsync_len) << line_shift) + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 if (IS_AGA)
2650 par->bplcon3 = sprpixmode[clk_shift];
2651 else
2652 par->bplcon3 = 0;
2653 if (var->sync & FB_SYNC_BROADCAST) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002654 par->diwstop_h = par->htotal -
2655 ((var->right_margin - var->hsync_len) << clk_shift);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656 if (IS_AGA)
2657 par->diwstop_h += mod4(var->hsync_len);
2658 else
2659 par->diwstop_h = down4(par->diwstop_h);
2660
2661 par->diwstrt_h = par->diwstop_h - xres_n;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002662 par->diwstop_v = par->vtotal -
2663 ((var->lower_margin - var->vsync_len) << line_shift);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 par->diwstrt_v = par->diwstop_v - yres_n;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002665 if (par->diwstop_h >= par->htotal + 8) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 DPRINTK("invalid diwstop_h\n");
2667 return -EINVAL;
2668 }
2669 if (par->diwstop_v > par->vtotal) {
2670 DPRINTK("invalid diwstop_v\n");
2671 return -EINVAL;
2672 }
2673
2674 if (!IS_OCS) {
2675 /* Initialize sync with some reasonable values for pwrsave */
2676 par->hsstrt = 160;
2677 par->hsstop = 320;
2678 par->vsstrt = 30;
2679 par->vsstop = 34;
2680 } else {
2681 par->hsstrt = 0;
2682 par->hsstop = 0;
2683 par->vsstrt = 0;
2684 par->vsstop = 0;
2685 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002686 if (par->vtotal > (PAL_VTOTAL + NTSC_VTOTAL) / 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 /* PAL video mode */
2688 if (par->htotal != PAL_HTOTAL) {
2689 DPRINTK("htotal invalid for pal\n");
2690 return -EINVAL;
2691 }
2692 if (par->diwstrt_h < PAL_DIWSTRT_H) {
2693 DPRINTK("diwstrt_h too low for pal\n");
2694 return -EINVAL;
2695 }
2696 if (par->diwstrt_v < PAL_DIWSTRT_V) {
2697 DPRINTK("diwstrt_v too low for pal\n");
2698 return -EINVAL;
2699 }
2700 htotal = PAL_HTOTAL>>clk_shift;
2701 vtotal = PAL_VTOTAL>>1;
2702 if (!IS_OCS) {
2703 par->beamcon0 = BMC0_PAL;
2704 par->bplcon3 |= BPC3_BRDRBLNK;
2705 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002706 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 par->beamcon0 = BMC0_PAL;
2708 par->hsstop = 1;
2709 } else if (amiga_vblank != 50) {
2710 DPRINTK("pal not supported by this chipset\n");
2711 return -EINVAL;
2712 }
2713 } else {
2714 /* NTSC video mode
2715 * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
2716 * and NTSC activated, so than better let diwstop_h <= 1812
2717 */
2718 if (par->htotal != NTSC_HTOTAL) {
2719 DPRINTK("htotal invalid for ntsc\n");
2720 return -EINVAL;
2721 }
2722 if (par->diwstrt_h < NTSC_DIWSTRT_H) {
2723 DPRINTK("diwstrt_h too low for ntsc\n");
2724 return -EINVAL;
2725 }
2726 if (par->diwstrt_v < NTSC_DIWSTRT_V) {
2727 DPRINTK("diwstrt_v too low for ntsc\n");
2728 return -EINVAL;
2729 }
2730 htotal = NTSC_HTOTAL>>clk_shift;
2731 vtotal = NTSC_VTOTAL>>1;
2732 if (!IS_OCS) {
2733 par->beamcon0 = 0;
2734 par->bplcon3 |= BPC3_BRDRBLNK;
2735 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002736 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 par->beamcon0 = 0;
2738 par->hsstop = 1;
2739 } else if (amiga_vblank != 60) {
2740 DPRINTK("ntsc not supported by this chipset\n");
2741 return -EINVAL;
2742 }
2743 }
2744 if (IS_OCS) {
2745 if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
2746 par->diwstrt_v >= 512 || par->diwstop_v < 256) {
2747 DPRINTK("invalid position for display on ocs\n");
2748 return -EINVAL;
2749 }
2750 }
2751 } else if (!IS_OCS) {
2752 /* Programmable video mode */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002753 par->hsstrt = var->right_margin << clk_shift;
2754 par->hsstop = (var->right_margin + var->hsync_len) << clk_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755 par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
2756 if (!IS_AGA)
2757 par->diwstop_h = down4(par->diwstop_h) - 16;
2758 par->diwstrt_h = par->diwstop_h - xres_n;
2759 par->hbstop = par->diwstrt_h + 4;
2760 par->hbstrt = par->diwstop_h + 4;
2761 if (par->hbstrt >= par->htotal + 8)
2762 par->hbstrt -= par->htotal;
2763 par->hcenter = par->hsstrt + (par->htotal >> 1);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002764 par->vsstrt = var->lower_margin << line_shift;
2765 par->vsstop = (var->lower_margin + var->vsync_len) << line_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 par->diwstop_v = par->vtotal;
2767 if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
2768 par->diwstop_v -= 2;
2769 par->diwstrt_v = par->diwstop_v - yres_n;
2770 par->vbstop = par->diwstrt_v - 2;
2771 par->vbstrt = par->diwstop_v - 2;
2772 if (par->vtotal > 2048) {
2773 DPRINTK("vtotal too high\n");
2774 return -EINVAL;
2775 }
2776 if (par->htotal > 2048) {
2777 DPRINTK("htotal too high\n");
2778 return -EINVAL;
2779 }
2780 par->bplcon3 |= BPC3_EXTBLKEN;
2781 par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002782 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
2783 BMC0_PAL | BMC0_VARCSYEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
2785 par->beamcon0 |= BMC0_HSYTRUE;
2786 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
2787 par->beamcon0 |= BMC0_VSYTRUE;
2788 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
2789 par->beamcon0 |= BMC0_CSYTRUE;
2790 htotal = par->htotal>>clk_shift;
2791 vtotal = par->vtotal>>1;
2792 } else {
2793 DPRINTK("only broadcast modes possible for ocs\n");
2794 return -EINVAL;
2795 }
2796
2797 /*
2798 * Checking the DMA timing
2799 */
2800
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002801 fconst = 16 << maxfmode << clk_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802
2803 /*
2804 * smallest window start value without turn off other dma cycles
2805 * than sprite1-7, unless you change min_fstrt
2806 */
2807
2808
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002809 fsize = ((maxfmode + clk_shift <= 1) ? fconst : 64);
2810 fstrt = downx(fconst, par->diwstrt_h - 4) - fsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 if (fstrt < min_fstrt) {
2812 DPRINTK("fetch start too low\n");
2813 return -EINVAL;
2814 }
2815
2816 /*
2817 * smallest window start value where smooth scrolling is possible
2818 */
2819
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002820 fstrt = downx(fconst, par->diwstrt_h - fconst + (1 << clk_shift) - 4) -
2821 fsize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822 if (fstrt < min_fstrt)
2823 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
2824
2825 maxfetchstop = down16(par->htotal - 80);
2826
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002827 fstrt = downx(fconst, par->diwstrt_h - 4) - 64 - fconst;
2828 fsize = upx(fconst, xres_n +
2829 modx(fconst, downx(1 << clk_shift, par->diwstrt_h - 4)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 if (fstrt + fsize > maxfetchstop)
2831 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
2832
2833 fsize = upx(fconst, xres_n);
2834 if (fstrt + fsize > maxfetchstop) {
2835 DPRINTK("fetch stop too high\n");
2836 return -EINVAL;
2837 }
2838
2839 if (maxfmode + clk_shift <= 1) {
2840 fsize = up64(xres_n + fconst - 1);
2841 if (min_fstrt + fsize - 64 > maxfetchstop)
2842 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
2843
2844 fsize = up64(xres_n);
2845 if (min_fstrt + fsize - 64 > maxfetchstop) {
2846 DPRINTK("fetch size too high\n");
2847 return -EINVAL;
2848 }
2849
2850 fsize -= 64;
2851 } else
2852 fsize -= fconst;
2853
2854 /*
2855 * Check if there is enough time to update the bitplane pointers for ywrap
2856 */
2857
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002858 if (par->htotal - fsize - 64 < par->bpp * 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 par->vmode &= ~FB_VMODE_YWRAP;
2860
2861 /*
2862 * Bitplane calculations and check the Memory Requirements
2863 */
2864
2865 if (amifb_ilbm) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002866 par->next_plane = div8(upx(16 << maxfmode, par->vxres));
2867 par->next_line = par->bpp * par->next_plane;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 if (par->next_line * par->vyres > fb_info.fix.smem_len) {
2869 DPRINTK("too few video mem\n");
2870 return -EINVAL;
2871 }
2872 } else {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002873 par->next_line = div8(upx(16 << maxfmode, par->vxres));
2874 par->next_plane = par->vyres * par->next_line;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875 if (par->next_plane * par->bpp > fb_info.fix.smem_len) {
2876 DPRINTK("too few video mem\n");
2877 return -EINVAL;
2878 }
2879 }
2880
2881 /*
2882 * Hardware Register Values
2883 */
2884
2885 par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
2886 if (!IS_OCS)
2887 par->bplcon0 |= BPC0_ECSENA;
2888 if (par->bpp == 8)
2889 par->bplcon0 |= BPC0_BPU3;
2890 else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002891 par->bplcon0 |= par->bpp << 12;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 if (var->nonstd == FB_NONSTD_HAM)
2893 par->bplcon0 |= BPC0_HAM;
2894 if (var->sync & FB_SYNC_EXT)
2895 par->bplcon0 |= BPC0_ERSY;
2896
2897 if (IS_AGA)
2898 par->fmode = bplfetchmode[maxfmode];
2899
2900 switch (par->vmode & FB_VMODE_MASK) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002901 case FB_VMODE_INTERLACED:
2902 par->bplcon0 |= BPC0_LACE;
2903 break;
2904 case FB_VMODE_DOUBLE:
2905 if (IS_AGA)
2906 par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
2907 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908 }
2909
2910 if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
2911 par->xoffset = var->xoffset;
2912 par->yoffset = var->yoffset;
2913 if (par->vmode & FB_VMODE_YWRAP) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002914 if (par->xoffset || par->yoffset < 0 ||
2915 par->yoffset >= par->vyres)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916 par->xoffset = par->yoffset = 0;
2917 } else {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002918 if (par->xoffset < 0 ||
2919 par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) ||
2920 par->yoffset < 0 || par->yoffset > par->vyres - par->yres)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 par->xoffset = par->yoffset = 0;
2922 }
2923 } else
2924 par->xoffset = par->yoffset = 0;
2925
2926 par->crsr.crsr_x = par->crsr.crsr_y = 0;
2927 par->crsr.spot_x = par->crsr.spot_y = 0;
2928 par->crsr.height = par->crsr.width = 0;
2929
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930 return 0;
2931}
2932
2933 /*
2934 * Fill the `var' structure based on the values in `par' and maybe
2935 * other values read out of the hardware.
2936 */
2937
2938static int ami_encode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002939 struct amifb_par *par)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940{
2941 u_short clk_shift, line_shift;
2942
2943 memset(var, 0, sizeof(struct fb_var_screeninfo));
2944
2945 clk_shift = par->clk_shift;
2946 line_shift = par->line_shift;
2947
2948 var->xres = par->xres;
2949 var->yres = par->yres;
2950 var->xres_virtual = par->vxres;
2951 var->yres_virtual = par->vyres;
2952 var->xoffset = par->xoffset;
2953 var->yoffset = par->yoffset;
2954
2955 var->bits_per_pixel = par->bpp;
2956 var->grayscale = 0;
2957
2958 var->red.offset = 0;
2959 var->red.msb_right = 0;
2960 var->red.length = par->bpp;
2961 if (par->bplcon0 & BPC0_HAM)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002962 var->red.length -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 var->blue = var->green = var->red;
2964 var->transp.offset = 0;
2965 var->transp.length = 0;
2966 var->transp.msb_right = 0;
2967
2968 if (par->bplcon0 & BPC0_HAM)
2969 var->nonstd = FB_NONSTD_HAM;
2970 else
2971 var->nonstd = 0;
2972 var->activate = 0;
2973
2974 var->height = -1;
2975 var->width = -1;
2976
2977 var->pixclock = pixclock[clk_shift];
2978
2979 if (IS_AGA && par->fmode & FMODE_BSCAN2)
2980 var->vmode = FB_VMODE_DOUBLE;
2981 else if (par->bplcon0 & BPC0_LACE)
2982 var->vmode = FB_VMODE_INTERLACED;
2983 else
2984 var->vmode = FB_VMODE_NONINTERLACED;
2985
2986 if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002987 var->hsync_len = (par->hsstop - par->hsstrt)>>clk_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002988 var->right_margin = par->hsstrt>>clk_shift;
2989 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002990 var->vsync_len = (par->vsstop - par->vsstrt)>>line_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002991 var->lower_margin = par->vsstrt>>line_shift;
2992 var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
2993 var->sync = 0;
2994 if (par->beamcon0 & BMC0_HSYTRUE)
2995 var->sync |= FB_SYNC_HOR_HIGH_ACT;
2996 if (par->beamcon0 & BMC0_VSYTRUE)
2997 var->sync |= FB_SYNC_VERT_HIGH_ACT;
2998 if (par->beamcon0 & BMC0_CSYTRUE)
2999 var->sync |= FB_SYNC_COMP_HIGH_ACT;
3000 } else {
3001 var->sync = FB_SYNC_BROADCAST;
3002 var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
3003 var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
3004 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
3005 var->vsync_len = 4>>line_shift;
3006 var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
3007 var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003008 var->lower_margin - var->vsync_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003009 }
3010
3011 if (par->bplcon0 & BPC0_ERSY)
3012 var->sync |= FB_SYNC_EXT;
3013 if (par->vmode & FB_VMODE_YWRAP)
3014 var->vmode |= FB_VMODE_YWRAP;
3015
3016 return 0;
3017}
3018
3019
3020 /*
3021 * Pan or Wrap the Display
3022 *
3023 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
3024 * in `var'.
3025 */
3026
3027static void ami_pan_var(struct fb_var_screeninfo *var)
3028{
3029 struct amifb_par *par = &currentpar;
3030
3031 par->xoffset = var->xoffset;
3032 par->yoffset = var->yoffset;
3033 if (var->vmode & FB_VMODE_YWRAP)
3034 par->vmode |= FB_VMODE_YWRAP;
3035 else
3036 par->vmode &= ~FB_VMODE_YWRAP;
3037
3038 do_vmode_pan = 0;
3039 ami_update_par();
3040 do_vmode_pan = 1;
3041}
3042
3043 /*
3044 * Update hardware
3045 */
3046
3047static int ami_update_par(void)
3048{
3049 struct amifb_par *par = &currentpar;
3050 short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod;
3051
3052 clk_shift = par->clk_shift;
3053
3054 if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003055 par->xoffset = upx(16 << maxfmode, par->xoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003057 fconst = 16 << maxfmode << clk_shift;
3058 vshift = modx(16 << maxfmode, par->xoffset);
3059 fstrt = par->diwstrt_h - (vshift << clk_shift) - 4;
3060 fsize = (par->xres + vshift) << clk_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 shift = modx(fconst, fstrt);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003062 move = downx(2 << maxfmode, div8(par->xoffset));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003063 if (maxfmode + clk_shift > 1) {
3064 fstrt = downx(fconst, fstrt) - 64;
3065 fsize = upx(fconst, fsize);
3066 fstop = fstrt + fsize - fconst;
3067 } else {
3068 mod = fstrt = downx(fconst, fstrt) - fconst;
3069 fstop = fstrt + upx(fconst, fsize) - 64;
3070 fsize = up64(fsize);
3071 fstrt = fstop - fsize + 64;
3072 if (fstrt < min_fstrt) {
3073 fstop += min_fstrt - fstrt;
3074 fstrt = min_fstrt;
3075 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003076 move = move - div8((mod - fstrt)>>clk_shift);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077 }
3078 mod = par->next_line - div8(fsize>>clk_shift);
3079 par->ddfstrt = fstrt;
3080 par->ddfstop = fstop;
3081 par->bplcon1 = hscroll2hw(shift);
3082 par->bpl2mod = mod;
3083 if (par->bplcon0 & BPC0_LACE)
3084 par->bpl2mod += par->next_line;
3085 if (IS_AGA && (par->fmode & FMODE_BSCAN2))
3086 par->bpl1mod = -div8(fsize>>clk_shift);
3087 else
3088 par->bpl1mod = par->bpl2mod;
3089
3090 if (par->yoffset) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003091 par->bplpt0 = fb_info.fix.smem_start +
3092 par->next_line * par->yoffset + move;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 if (par->vmode & FB_VMODE_YWRAP) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003094 if (par->yoffset > par->vyres - par->yres) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095 par->bplpt0wrap = fb_info.fix.smem_start + move;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003096 if (par->bplcon0 & BPC0_LACE &&
3097 mod2(par->diwstrt_v + par->vyres -
3098 par->yoffset))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003099 par->bplpt0wrap += par->next_line;
3100 }
3101 }
3102 } else
3103 par->bplpt0 = fb_info.fix.smem_start + move;
3104
3105 if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
3106 par->bplpt0 += par->next_line;
3107
3108 return 0;
3109}
3110
3111
3112 /*
3113 * Set a single color register. The values supplied are already
3114 * rounded down to the hardware's capabilities (according to the
3115 * entries in the var structure). Return != 0 for invalid regno.
3116 */
3117
3118static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003119 u_int transp, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120{
3121 if (IS_AGA) {
3122 if (regno > 255)
3123 return 1;
3124 } else if (currentpar.bplcon0 & BPC0_SHRES) {
3125 if (regno > 3)
3126 return 1;
3127 } else {
3128 if (regno > 31)
3129 return 1;
3130 }
3131 red >>= 8;
3132 green >>= 8;
3133 blue >>= 8;
3134 if (!regno) {
3135 red0 = red;
3136 green0 = green;
3137 blue0 = blue;
3138 }
3139
3140 /*
3141 * Update the corresponding Hardware Color Register, unless it's Color
3142 * Register 0 and the screen is blanked.
3143 *
3144 * VBlank is switched off to protect bplcon3 or ecs_palette[] from
3145 * being changed by ami_do_blank() during the VBlank.
3146 */
3147
3148 if (regno || !is_blanked) {
3149#if defined(CONFIG_FB_AMIGA_AGA)
3150 if (IS_AGA) {
3151 u_short bplcon3 = currentpar.bplcon3;
3152 VBlankOff();
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003153 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000);
3154 custom.color[regno & 31] = rgb2hw8_high(red, green,
3155 blue);
3156 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000) |
3157 BPC3_LOCT;
3158 custom.color[regno & 31] = rgb2hw8_low(red, green,
3159 blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 custom.bplcon3 = bplcon3;
3161 VBlankOn();
3162 } else
3163#endif
3164#if defined(CONFIG_FB_AMIGA_ECS)
3165 if (currentpar.bplcon0 & BPC0_SHRES) {
3166 u_short color, mask;
3167 int i;
3168
3169 mask = 0x3333;
3170 color = rgb2hw2(red, green, blue);
3171 VBlankOff();
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003172 for (i = regno + 12; i >= (int)regno; i -= 4)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003174 mask <<= 2; color >>= 2;
3175 regno = down16(regno) + mul4(mod4(regno));
3176 for (i = regno + 3; i >= (int)regno; i--)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
3178 VBlankOn();
3179 } else
3180#endif
3181 custom.color[regno] = rgb2hw4(red, green, blue);
3182 }
3183 return 0;
3184}
3185
3186static void ami_update_display(void)
3187{
3188 struct amifb_par *par = &currentpar;
3189
3190 custom.bplcon1 = par->bplcon1;
3191 custom.bpl1mod = par->bpl1mod;
3192 custom.bpl2mod = par->bpl2mod;
3193 custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
3194 custom.ddfstop = ddfstop2hw(par->ddfstop);
3195}
3196
3197 /*
3198 * Change the video mode (called by VBlank interrupt)
3199 */
3200
3201static void ami_init_display(void)
3202{
3203 struct amifb_par *par = &currentpar;
3204 int i;
3205
3206 custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
3207 custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
3208 if (!IS_OCS) {
3209 custom.bplcon3 = par->bplcon3;
3210 if (IS_AGA)
3211 custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
3212 if (par->beamcon0 & BMC0_VARBEAMEN) {
3213 custom.htotal = htotal2hw(par->htotal);
3214 custom.hbstrt = hbstrt2hw(par->hbstrt);
3215 custom.hbstop = hbstop2hw(par->hbstop);
3216 custom.hsstrt = hsstrt2hw(par->hsstrt);
3217 custom.hsstop = hsstop2hw(par->hsstop);
3218 custom.hcenter = hcenter2hw(par->hcenter);
3219 custom.vtotal = vtotal2hw(par->vtotal);
3220 custom.vbstrt = vbstrt2hw(par->vbstrt);
3221 custom.vbstop = vbstop2hw(par->vbstop);
3222 custom.vsstrt = vsstrt2hw(par->vsstrt);
3223 custom.vsstop = vsstop2hw(par->vsstop);
3224 }
3225 }
3226 if (!IS_OCS || par->hsstop)
3227 custom.beamcon0 = par->beamcon0;
3228 if (IS_AGA)
3229 custom.fmode = par->fmode;
3230
3231 /*
3232 * The minimum period for audio depends on htotal
3233 */
3234
3235 amiga_audio_min_period = div16(par->htotal);
3236
3237 is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
3238#if 1
3239 if (is_lace) {
3240 i = custom.vposr >> 15;
3241 } else {
3242 custom.vposw = custom.vposr | 0x8000;
3243 i = 1;
3244 }
3245#else
3246 i = 1;
3247 custom.vposw = custom.vposr | 0x8000;
3248#endif
3249 custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][i]);
3250}
3251
3252 /*
3253 * (Un)Blank the screen (called by VBlank interrupt)
3254 */
3255
3256static void ami_do_blank(void)
3257{
3258 struct amifb_par *par = &currentpar;
3259#if defined(CONFIG_FB_AMIGA_AGA)
3260 u_short bplcon3 = par->bplcon3;
3261#endif
3262 u_char red, green, blue;
3263
3264 if (do_blank > 0) {
3265 custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
3266 red = green = blue = 0;
3267 if (!IS_OCS && do_blank > 1) {
3268 switch (do_blank) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003269 case FB_BLANK_VSYNC_SUSPEND:
3270 custom.hsstrt = hsstrt2hw(par->hsstrt);
3271 custom.hsstop = hsstop2hw(par->hsstop);
3272 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
3273 custom.vsstop = vsstop2hw(par->vtotal + 4);
3274 break;
3275 case FB_BLANK_HSYNC_SUSPEND:
3276 custom.hsstrt = hsstrt2hw(par->htotal + 16);
3277 custom.hsstop = hsstop2hw(par->htotal + 16);
3278 custom.vsstrt = vsstrt2hw(par->vsstrt);
3279 custom.vsstop = vsstrt2hw(par->vsstop);
3280 break;
3281 case FB_BLANK_POWERDOWN:
3282 custom.hsstrt = hsstrt2hw(par->htotal + 16);
3283 custom.hsstop = hsstop2hw(par->htotal + 16);
3284 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
3285 custom.vsstop = vsstop2hw(par->vtotal + 4);
3286 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003287 }
3288 if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
3289 custom.htotal = htotal2hw(par->htotal);
3290 custom.vtotal = vtotal2hw(par->vtotal);
3291 custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003292 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293 }
3294 }
3295 } else {
3296 custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
3297 red = red0;
3298 green = green0;
3299 blue = blue0;
3300 if (!IS_OCS) {
3301 custom.hsstrt = hsstrt2hw(par->hsstrt);
3302 custom.hsstop = hsstop2hw(par->hsstop);
3303 custom.vsstrt = vsstrt2hw(par->vsstrt);
3304 custom.vsstop = vsstop2hw(par->vsstop);
3305 custom.beamcon0 = par->beamcon0;
3306 }
3307 }
3308#if defined(CONFIG_FB_AMIGA_AGA)
3309 if (IS_AGA) {
3310 custom.bplcon3 = bplcon3;
3311 custom.color[0] = rgb2hw8_high(red, green, blue);
3312 custom.bplcon3 = bplcon3 | BPC3_LOCT;
3313 custom.color[0] = rgb2hw8_low(red, green, blue);
3314 custom.bplcon3 = bplcon3;
3315 } else
3316#endif
3317#if defined(CONFIG_FB_AMIGA_ECS)
3318 if (par->bplcon0 & BPC0_SHRES) {
3319 u_short color, mask;
3320 int i;
3321
3322 mask = 0x3333;
3323 color = rgb2hw2(red, green, blue);
3324 for (i = 12; i >= 0; i -= 4)
3325 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003326 mask <<= 2; color >>= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 for (i = 3; i >= 0; i--)
3328 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
3329 } else
3330#endif
3331 custom.color[0] = rgb2hw4(red, green, blue);
3332 is_blanked = do_blank > 0 ? do_blank : 0;
3333}
3334
3335static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix)
3336{
3337 struct amifb_par *par = &currentpar;
3338
3339 fix->crsr_width = fix->crsr_xsize = par->crsr.width;
3340 fix->crsr_height = fix->crsr_ysize = par->crsr.height;
3341 fix->crsr_color1 = 17;
3342 fix->crsr_color2 = 18;
3343 return 0;
3344}
3345
Al Viro3728d252006-01-12 01:06:31 -08003346static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003347{
3348 struct amifb_par *par = &currentpar;
3349 register u_short *lspr, *sspr;
3350#ifdef __mc68000__
3351 register u_long datawords asm ("d2");
3352#else
3353 register u_long datawords;
3354#endif
3355 register short delta;
3356 register u_char color;
3357 short height, width, bits, words;
3358 int size, alloc;
3359
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003360 size = par->crsr.height * par->crsr.width;
3361 alloc = var->height * var->width;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003362 var->height = par->crsr.height;
3363 var->width = par->crsr.width;
3364 var->xspot = par->crsr.spot_x;
3365 var->yspot = par->crsr.spot_y;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003366 if (size > var->height * var->width)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 return -ENAMETOOLONG;
Al Viro3728d252006-01-12 01:06:31 -08003368 if (!access_ok(VERIFY_WRITE, data, size))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 return -EFAULT;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003370 delta = 1 << par->crsr.fmode;
3371 lspr = lofsprite + (delta << 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 if (par->bplcon0 & BPC0_LACE)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003373 sspr = shfsprite + (delta << 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003374 else
Al Viro3728d252006-01-12 01:06:31 -08003375 sspr = NULL;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003376 for (height = (short)var->height - 1; height >= 0; height--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 bits = 0; words = delta; datawords = 0;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003378 for (width = (short)var->width - 1; width >= 0; width--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003379 if (bits == 0) {
3380 bits = 16; --words;
3381#ifdef __mc68000__
3382 asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
3383 : "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
3384#else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003385 datawords = (*(lspr + delta) << 16) | (*lspr++);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003386#endif
3387 }
3388 --bits;
3389#ifdef __mc68000__
3390 asm volatile (
3391 "clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
3392 "swap %1 ; lslw #1,%1 ; roxlb #1,%0"
3393 : "=d" (color), "=d" (datawords) : "1" (datawords));
3394#else
3395 color = (((datawords >> 30) & 2)
3396 | ((datawords >> 15) & 1));
3397 datawords <<= 1;
3398#endif
3399 put_user(color, data++);
3400 }
3401 if (bits > 0) {
3402 --words; ++lspr;
3403 }
3404 while (--words >= 0)
3405 ++lspr;
3406#ifdef __mc68000__
3407 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
3408 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
3409#else
3410 lspr += delta;
3411 if (sspr) {
3412 u_short *tmp = lspr;
3413 lspr = sspr;
3414 sspr = tmp;
3415 }
3416#endif
3417 }
3418 return 0;
3419}
3420
Al Viro3728d252006-01-12 01:06:31 -08003421static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003422{
3423 struct amifb_par *par = &currentpar;
3424 register u_short *lspr, *sspr;
3425#ifdef __mc68000__
3426 register u_long datawords asm ("d2");
3427#else
3428 register u_long datawords;
3429#endif
3430 register short delta;
3431 u_short fmode;
3432 short height, width, bits, words;
3433
3434 if (!var->width)
3435 return -EINVAL;
3436 else if (var->width <= 16)
3437 fmode = TAG_FMODE_1;
3438 else if (var->width <= 32)
3439 fmode = TAG_FMODE_2;
3440 else if (var->width <= 64)
3441 fmode = TAG_FMODE_4;
3442 else
3443 return -EINVAL;
3444 if (fmode > maxfmode)
3445 return -EINVAL;
3446 if (!var->height)
3447 return -EINVAL;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003448 if (!access_ok(VERIFY_READ, data, var->width * var->height))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449 return -EFAULT;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003450 delta = 1 << fmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003451 lofsprite = shfsprite = (u_short *)spritememory;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003452 lspr = lofsprite + (delta << 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453 if (par->bplcon0 & BPC0_LACE) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003454 if (((var->height + 4) << fmode << 2) > SPRITEMEMSIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003455 return -EINVAL;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003456 memset(lspr, 0, (var->height + 4) << fmode << 2);
3457 shfsprite += ((var->height + 5)&-2) << fmode;
3458 sspr = shfsprite + (delta << 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003459 } else {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003460 if (((var->height + 2) << fmode << 2) > SPRITEMEMSIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003461 return -EINVAL;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003462 memset(lspr, 0, (var->height + 2) << fmode << 2);
Al Viro3728d252006-01-12 01:06:31 -08003463 sspr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003464 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003465 for (height = (short)var->height - 1; height >= 0; height--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003466 bits = 16; words = delta; datawords = 0;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003467 for (width = (short)var->width - 1; width >= 0; width--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003468 unsigned long tdata = 0;
Al Viro3728d252006-01-12 01:06:31 -08003469 get_user(tdata, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470 data++;
3471#ifdef __mc68000__
3472 asm volatile (
3473 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
3474 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
3475 : "=d" (datawords)
3476 : "0" (datawords), "d" (tdata));
3477#else
3478 datawords = ((datawords << 1) & 0xfffefffe);
3479 datawords |= tdata & 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003480 datawords |= (tdata & 2) << (16 - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003481#endif
3482 if (--bits == 0) {
3483 bits = 16; --words;
3484#ifdef __mc68000__
3485 asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
3486 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
3487#else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003488 *(lspr + delta) = (u_short) (datawords >> 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003489 *lspr++ = (u_short) (datawords & 0xffff);
3490#endif
3491 }
3492 }
3493 if (bits < 16) {
3494 --words;
3495#ifdef __mc68000__
3496 asm volatile (
3497 "swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
3498 "swap %2 ; lslw %4,%2 ; movew %2,%0@+"
3499 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
3500#else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003501 *(lspr + delta) = (u_short) (datawords >> (16 + bits));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502 *lspr++ = (u_short) ((datawords & 0x0000ffff) >> bits);
3503#endif
3504 }
3505 while (--words >= 0) {
3506#ifdef __mc68000__
3507 asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
3508 : "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
3509#else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003510 *(lspr + delta) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003511 *lspr++ = 0;
3512#endif
3513 }
3514#ifdef __mc68000__
3515 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
3516 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
3517#else
3518 lspr += delta;
3519 if (sspr) {
3520 u_short *tmp = lspr;
3521 lspr = sspr;
3522 sspr = tmp;
3523 }
3524#endif
3525 }
3526 par->crsr.height = var->height;
3527 par->crsr.width = var->width;
3528 par->crsr.spot_x = var->xspot;
3529 par->crsr.spot_y = var->yspot;
3530 par->crsr.fmode = fmode;
3531 if (IS_AGA) {
3532 par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
3533 par->fmode |= sprfetchmode[fmode];
3534 custom.fmode = par->fmode;
3535 }
3536 return 0;
3537}
3538
3539static int ami_get_cursorstate(struct fb_cursorstate *state)
3540{
3541 struct amifb_par *par = &currentpar;
3542
3543 state->xoffset = par->crsr.crsr_x;
3544 state->yoffset = par->crsr.crsr_y;
3545 state->mode = cursormode;
3546 return 0;
3547}
3548
3549static int ami_set_cursorstate(struct fb_cursorstate *state)
3550{
3551 struct amifb_par *par = &currentpar;
3552
3553 par->crsr.crsr_x = state->xoffset;
3554 par->crsr.crsr_y = state->yoffset;
3555 if ((cursormode = state->mode) == FB_CURSOR_OFF)
3556 cursorstate = -1;
3557 do_cursor = 1;
3558 return 0;
3559}
3560
3561static void ami_set_sprite(void)
3562{
3563 struct amifb_par *par = &currentpar;
3564 copins *copl, *cops;
3565 u_short hs, vs, ve;
3566 u_long pl, ps, pt;
3567 short mx, my;
3568
3569 cops = copdisplay.list[currentcop][0];
3570 copl = copdisplay.list[currentcop][1];
3571 ps = pl = ZTWO_PADDR(dummysprite);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003572 mx = par->crsr.crsr_x - par->crsr.spot_x;
3573 my = par->crsr.crsr_y - par->crsr.spot_y;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003574 if (!(par->vmode & FB_VMODE_YWRAP)) {
3575 mx -= par->xoffset;
3576 my -= par->yoffset;
3577 }
3578 if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
3579 mx > -(short)par->crsr.width && mx < par->xres &&
3580 my > -(short)par->crsr.height && my < par->yres) {
3581 pl = ZTWO_PADDR(lofsprite);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003582 hs = par->diwstrt_h + (mx << par->clk_shift) - 4;
3583 vs = par->diwstrt_v + (my << par->line_shift);
3584 ve = vs + (par->crsr.height << par->line_shift);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 if (par->bplcon0 & BPC0_LACE) {
3586 ps = ZTWO_PADDR(shfsprite);
3587 lofsprite[0] = spr2hw_pos(vs, hs);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003588 shfsprite[0] = spr2hw_pos(vs + 1, hs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003589 if (mod2(vs)) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003590 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
3591 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003592 pt = pl; pl = ps; ps = pt;
3593 } else {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003594 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
3595 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003596 }
3597 } else {
3598 lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003599 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 }
3601 }
3602 copl[cop_spr0ptrh].w[1] = highw(pl);
3603 copl[cop_spr0ptrl].w[1] = loww(pl);
3604 if (par->bplcon0 & BPC0_LACE) {
3605 cops[cop_spr0ptrh].w[1] = highw(ps);
3606 cops[cop_spr0ptrl].w[1] = loww(ps);
3607 }
3608}
3609
3610
3611 /*
3612 * Initialise the Copper Initialisation List
3613 */
3614
3615static void __init ami_init_copper(void)
3616{
3617 copins *cop = copdisplay.init;
3618 u_long p;
3619 int i;
3620
3621 if (!IS_OCS) {
3622 (cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
3623 (cop++)->l = CMOVE(0x0181, diwstrt);
3624 (cop++)->l = CMOVE(0x0281, diwstop);
3625 (cop++)->l = CMOVE(0x0000, diwhigh);
3626 } else
3627 (cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
3628 p = ZTWO_PADDR(dummysprite);
3629 for (i = 0; i < 8; i++) {
3630 (cop++)->l = CMOVE(0, spr[i].pos);
3631 (cop++)->l = CMOVE(highw(p), sprpt[i]);
3632 (cop++)->l = CMOVE2(loww(p), sprpt[i]);
3633 }
3634
3635 (cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
3636 copdisplay.wait = cop;
3637 (cop++)->l = CEND;
3638 (cop++)->l = CMOVE(0, copjmp2);
3639 cop->l = CEND;
3640
3641 custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
3642 custom.copjmp1 = 0;
3643}
3644
3645static void ami_reinit_copper(void)
3646{
3647 struct amifb_par *par = &currentpar;
3648
3649 copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003650 copdisplay.wait->l = CWAIT(32, par->diwstrt_v - 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651}
3652
3653 /*
3654 * Build the Copper List
3655 */
3656
3657static void ami_build_copper(void)
3658{
3659 struct amifb_par *par = &currentpar;
3660 copins *copl, *cops;
3661 u_long p;
3662
3663 currentcop = 1 - currentcop;
3664
3665 copl = copdisplay.list[currentcop][1];
3666
3667 (copl++)->l = CWAIT(0, 10);
3668 (copl++)->l = CMOVE(par->bplcon0, bplcon0);
3669 (copl++)->l = CMOVE(0, sprpt[0]);
3670 (copl++)->l = CMOVE2(0, sprpt[0]);
3671
3672 if (par->bplcon0 & BPC0_LACE) {
3673 cops = copdisplay.list[currentcop][0];
3674
3675 (cops++)->l = CWAIT(0, 10);
3676 (cops++)->l = CMOVE(par->bplcon0, bplcon0);
3677 (cops++)->l = CMOVE(0, sprpt[0]);
3678 (cops++)->l = CMOVE2(0, sprpt[0]);
3679
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003680 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v + 1), diwstrt);
3681 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v + 1), diwstop);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003682 (cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
3683 (cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
3684 if (!IS_OCS) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003685 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v + 1,
3686 par->diwstop_h, par->diwstop_v + 1), diwhigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 (cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003688 par->diwstop_h, par->diwstop_v), diwhigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003689#if 0
3690 if (par->beamcon0 & BMC0_VARBEAMEN) {
3691 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003692 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt + 1), vbstrt);
3693 (copl++)->l = CMOVE(vbstop2hw(par->vbstop + 1), vbstop);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003694 (cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
3695 (cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
3696 (cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
3697 }
3698#endif
3699 }
3700 p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
3701 (copl++)->l = CMOVE(highw(p), cop2lc);
3702 (copl++)->l = CMOVE2(loww(p), cop2lc);
3703 p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
3704 (cops++)->l = CMOVE(highw(p), cop2lc);
3705 (cops++)->l = CMOVE2(loww(p), cop2lc);
3706 copdisplay.rebuild[0] = cops;
3707 } else {
3708 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
3709 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
3710 if (!IS_OCS) {
3711 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003712 par->diwstop_h, par->diwstop_v), diwhigh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003713#if 0
3714 if (par->beamcon0 & BMC0_VARBEAMEN) {
3715 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
3716 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
3717 (copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
3718 }
3719#endif
3720 }
3721 }
3722 copdisplay.rebuild[1] = copl;
3723
3724 ami_update_par();
3725 ami_rebuild_copper();
3726}
3727
3728 /*
3729 * Rebuild the Copper List
3730 *
3731 * We only change the things that are not static
3732 */
3733
3734static void ami_rebuild_copper(void)
3735{
3736 struct amifb_par *par = &currentpar;
3737 copins *copl, *cops;
3738 u_short line, h_end1, h_end2;
3739 short i;
3740 u_long p;
3741
3742 if (IS_AGA && maxfmode + par->clk_shift == 0)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003743 h_end1 = par->diwstrt_h - 64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003744 else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003745 h_end1 = par->htotal - 32;
3746 h_end2 = par->ddfstop + 64;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747
3748 ami_set_sprite();
3749
3750 copl = copdisplay.rebuild[1];
3751 p = par->bplpt0;
3752 if (par->vmode & FB_VMODE_YWRAP) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003753 if ((par->vyres - par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
3754 if (par->yoffset > par->vyres - par->yres) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003755 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
3756 (copl++)->l = CMOVE(highw(p), bplpt[i]);
3757 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
3758 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003759 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003760 while (line >= 512) {
3761 (copl++)->l = CWAIT(h_end1, 510);
3762 line -= 512;
3763 }
3764 if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
3765 (copl++)->l = CWAIT(h_end1, line);
3766 else
3767 (copl++)->l = CWAIT(h_end2, line);
3768 p = par->bplpt0wrap;
3769 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003770 } else
3771 p = par->bplpt0wrap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003772 }
3773 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
3774 (copl++)->l = CMOVE(highw(p), bplpt[i]);
3775 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
3776 }
3777 copl->l = CEND;
3778
3779 if (par->bplcon0 & BPC0_LACE) {
3780 cops = copdisplay.rebuild[0];
3781 p = par->bplpt0;
3782 if (mod2(par->diwstrt_v))
3783 p -= par->next_line;
3784 else
3785 p += par->next_line;
3786 if (par->vmode & FB_VMODE_YWRAP) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003787 if ((par->vyres - par->yoffset) != 1 || mod2(par->diwstrt_v)) {
3788 if (par->yoffset > par->vyres - par->yres + 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003789 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
3790 (cops++)->l = CMOVE(highw(p), bplpt[i]);
3791 (cops++)->l = CMOVE2(loww(p), bplpt[i]);
3792 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003793 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003794 while (line >= 512) {
3795 (cops++)->l = CWAIT(h_end1, 510);
3796 line -= 512;
3797 }
3798 if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
3799 (cops++)->l = CWAIT(h_end1, line);
3800 else
3801 (cops++)->l = CWAIT(h_end2, line);
3802 p = par->bplpt0wrap;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003803 if (mod2(par->diwstrt_v + par->vyres -
3804 par->yoffset))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003805 p -= par->next_line;
3806 else
3807 p += par->next_line;
3808 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003809 } else
3810 p = par->bplpt0wrap - par->next_line;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003811 }
3812 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
3813 (cops++)->l = CMOVE(highw(p), bplpt[i]);
3814 (cops++)->l = CMOVE2(loww(p), bplpt[i]);
3815 }
3816 cops->l = CEND;
3817 }
3818}
3819
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003820static int __exit amifb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003821{
3822 unregister_framebuffer(&fb_info);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003823 amifb_deinit(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003824 amifb_video_off();
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003825 return 0;
3826}
3827
3828static struct platform_driver amifb_driver = {
3829 .remove = __exit_p(amifb_remove),
3830 .driver = {
3831 .name = "amiga-video",
3832 .owner = THIS_MODULE,
3833 },
3834};
3835
3836static int __init amifb_init(void)
3837{
3838 return platform_driver_probe(&amifb_driver, amifb_probe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003839}
Adrian Bunk57987122008-07-23 21:31:43 -07003840
3841module_init(amifb_init);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003842
3843static void __exit amifb_exit(void)
3844{
3845 platform_driver_unregister(&amifb_driver);
3846}
3847
Adrian Bunk57987122008-07-23 21:31:43 -07003848module_exit(amifb_exit);
3849
3850MODULE_LICENSE("GPL");
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003851MODULE_ALIAS("platform:amiga-video");