xref: /OK3568_Linux_fs/kernel/drivers/video/fbdev/amifb.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * linux/drivers/video/amifb.c -- Amiga builtin chipset frame buffer device
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  *    Copyright (C) 1995-2003 Geert Uytterhoeven
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  *          with work by Roman Zippel
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * This file is based on the Atari frame buffer device (atafb.c):
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  *    Copyright (C) 1994 Martin Schaller
12*4882a593Smuzhiyun  *                       Roman Hodek
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  *          with work by Andreas Schwab
15*4882a593Smuzhiyun  *                       Guenther Kelleter
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * and on the original Amiga console driver (amicon.c):
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  *    Copyright (C) 1993 Hamish Macdonald
20*4882a593Smuzhiyun  *                       Greg Harp
21*4882a593Smuzhiyun  *    Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
22*4882a593Smuzhiyun  *
23*4882a593Smuzhiyun  *          with work by William Rucklidge (wjr@cs.cornell.edu)
24*4882a593Smuzhiyun  *                       Geert Uytterhoeven
25*4882a593Smuzhiyun  *                       Jes Sorensen (jds@kom.auc.dk)
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  *
28*4882a593Smuzhiyun  * History:
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  *   - 24 Jul 96: Copper generates now vblank interrupt and
31*4882a593Smuzhiyun  *                VESA Power Saving Protocol is fully implemented
32*4882a593Smuzhiyun  *   - 14 Jul 96: Rework and hopefully last ECS bugs fixed
33*4882a593Smuzhiyun  *   -  7 Mar 96: Hardware sprite support by Roman Zippel
34*4882a593Smuzhiyun  *   - 18 Feb 96: OCS and ECS support by Roman Zippel
35*4882a593Smuzhiyun  *                Hardware functions completely rewritten
36*4882a593Smuzhiyun  *   -  2 Dec 95: AGA version by Geert Uytterhoeven
37*4882a593Smuzhiyun  *
38*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General Public
39*4882a593Smuzhiyun  * License. See the file COPYING in the main directory of this archive
40*4882a593Smuzhiyun  * for more details.
41*4882a593Smuzhiyun  */
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun #include <linux/module.h>
44*4882a593Smuzhiyun #include <linux/kernel.h>
45*4882a593Smuzhiyun #include <linux/errno.h>
46*4882a593Smuzhiyun #include <linux/string.h>
47*4882a593Smuzhiyun #include <linux/mm.h>
48*4882a593Smuzhiyun #include <linux/delay.h>
49*4882a593Smuzhiyun #include <linux/interrupt.h>
50*4882a593Smuzhiyun #include <linux/fb.h>
51*4882a593Smuzhiyun #include <linux/init.h>
52*4882a593Smuzhiyun #include <linux/ioport.h>
53*4882a593Smuzhiyun #include <linux/platform_device.h>
54*4882a593Smuzhiyun #include <linux/uaccess.h>
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun #include <asm/irq.h>
57*4882a593Smuzhiyun #include <asm/amigahw.h>
58*4882a593Smuzhiyun #include <asm/amigaints.h>
59*4882a593Smuzhiyun #include <asm/setup.h>
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun #include "c2p.h"
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun #define DEBUG
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun #if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA)
67*4882a593Smuzhiyun #define CONFIG_FB_AMIGA_OCS   /* define at least one fb driver, this will change later */
68*4882a593Smuzhiyun #endif
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun #if !defined(CONFIG_FB_AMIGA_OCS)
71*4882a593Smuzhiyun #  define IS_OCS (0)
72*4882a593Smuzhiyun #elif defined(CONFIG_FB_AMIGA_ECS) || defined(CONFIG_FB_AMIGA_AGA)
73*4882a593Smuzhiyun #  define IS_OCS (chipset == TAG_OCS)
74*4882a593Smuzhiyun #else
75*4882a593Smuzhiyun #  define CONFIG_FB_AMIGA_OCS_ONLY
76*4882a593Smuzhiyun #  define IS_OCS (1)
77*4882a593Smuzhiyun #endif
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #if !defined(CONFIG_FB_AMIGA_ECS)
80*4882a593Smuzhiyun #  define IS_ECS (0)
81*4882a593Smuzhiyun #elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_AGA)
82*4882a593Smuzhiyun #  define IS_ECS (chipset == TAG_ECS)
83*4882a593Smuzhiyun #else
84*4882a593Smuzhiyun #  define CONFIG_FB_AMIGA_ECS_ONLY
85*4882a593Smuzhiyun #  define IS_ECS (1)
86*4882a593Smuzhiyun #endif
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun #if !defined(CONFIG_FB_AMIGA_AGA)
89*4882a593Smuzhiyun #  define IS_AGA (0)
90*4882a593Smuzhiyun #elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_ECS)
91*4882a593Smuzhiyun #  define IS_AGA (chipset == TAG_AGA)
92*4882a593Smuzhiyun #else
93*4882a593Smuzhiyun #  define CONFIG_FB_AMIGA_AGA_ONLY
94*4882a593Smuzhiyun #  define IS_AGA (1)
95*4882a593Smuzhiyun #endif
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun #ifdef DEBUG
98*4882a593Smuzhiyun #  define DPRINTK(fmt, args...)	printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
99*4882a593Smuzhiyun #else
100*4882a593Smuzhiyun #  define DPRINTK(fmt, args...)
101*4882a593Smuzhiyun #endif
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun /*******************************************************************************
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun    Generic video timings
107*4882a593Smuzhiyun    ---------------------
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun    Timings used by the frame buffer interface:
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun    +----------+---------------------------------------------+----------+-------+
112*4882a593Smuzhiyun    |          |                ^                            |          |       |
113*4882a593Smuzhiyun    |          |                |upper_margin                |          |       |
114*4882a593Smuzhiyun    |          |                v                            |          |       |
115*4882a593Smuzhiyun    +----------###############################################----------+-------+
116*4882a593Smuzhiyun    |          #                ^                            #          |       |
117*4882a593Smuzhiyun    |          #                |                            #          |       |
118*4882a593Smuzhiyun    |          #                |                            #          |       |
119*4882a593Smuzhiyun    |          #                |                            #          |       |
120*4882a593Smuzhiyun    |   left   #                |                            #  right   | hsync |
121*4882a593Smuzhiyun    |  margin  #                |       xres                 #  margin  |  len  |
122*4882a593Smuzhiyun    |<-------->#<---------------+--------------------------->#<-------->|<----->|
123*4882a593Smuzhiyun    |          #                |                            #          |       |
124*4882a593Smuzhiyun    |          #                |                            #          |       |
125*4882a593Smuzhiyun    |          #                |                            #          |       |
126*4882a593Smuzhiyun    |          #                |yres                        #          |       |
127*4882a593Smuzhiyun    |          #                |                            #          |       |
128*4882a593Smuzhiyun    |          #                |                            #          |       |
129*4882a593Smuzhiyun    |          #                |                            #          |       |
130*4882a593Smuzhiyun    |          #                |                            #          |       |
131*4882a593Smuzhiyun    |          #                |                            #          |       |
132*4882a593Smuzhiyun    |          #                |                            #          |       |
133*4882a593Smuzhiyun    |          #                |                            #          |       |
134*4882a593Smuzhiyun    |          #                |                            #          |       |
135*4882a593Smuzhiyun    |          #                v                            #          |       |
136*4882a593Smuzhiyun    +----------###############################################----------+-------+
137*4882a593Smuzhiyun    |          |                ^                            |          |       |
138*4882a593Smuzhiyun    |          |                |lower_margin                |          |       |
139*4882a593Smuzhiyun    |          |                v                            |          |       |
140*4882a593Smuzhiyun    +----------+---------------------------------------------+----------+-------+
141*4882a593Smuzhiyun    |          |                ^                            |          |       |
142*4882a593Smuzhiyun    |          |                |vsync_len                   |          |       |
143*4882a593Smuzhiyun    |          |                v                            |          |       |
144*4882a593Smuzhiyun    +----------+---------------------------------------------+----------+-------+
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun    Amiga video timings
148*4882a593Smuzhiyun    -------------------
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun    The Amiga native chipsets uses another timing scheme:
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun       - hsstrt:   Start of horizontal synchronization pulse
153*4882a593Smuzhiyun       - hsstop:   End of horizontal synchronization pulse
154*4882a593Smuzhiyun       - htotal:   Last value on the line (i.e. line length = htotal + 1)
155*4882a593Smuzhiyun       - vsstrt:   Start of vertical synchronization pulse
156*4882a593Smuzhiyun       - vsstop:   End of vertical synchronization pulse
157*4882a593Smuzhiyun       - vtotal:   Last line value (i.e. number of lines = vtotal + 1)
158*4882a593Smuzhiyun       - hcenter:  Start of vertical retrace for interlace
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun    You can specify the blanking timings independently. Currently I just set
161*4882a593Smuzhiyun    them equal to the respective synchronization values:
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun       - hbstrt:   Start of horizontal blank
164*4882a593Smuzhiyun       - hbstop:   End of horizontal blank
165*4882a593Smuzhiyun       - vbstrt:   Start of vertical blank
166*4882a593Smuzhiyun       - vbstop:   End of vertical blank
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun    Horizontal values are in color clock cycles (280 ns), vertical values are in
169*4882a593Smuzhiyun    scanlines.
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun    (0, 0) is somewhere in the upper-left corner :-)
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun    Amiga visible window definitions
175*4882a593Smuzhiyun    --------------------------------
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun    Currently I only have values for AGA, SHRES (28 MHz dotclock). Feel free to
178*4882a593Smuzhiyun    make corrections and/or additions.
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun    Within the above synchronization specifications, the visible window is
181*4882a593Smuzhiyun    defined by the following parameters (actual register resolutions may be
182*4882a593Smuzhiyun    different; all horizontal values are normalized with respect to the pixel
183*4882a593Smuzhiyun    clock):
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun       - diwstrt_h:   Horizontal start of the visible window
186*4882a593Smuzhiyun       - diwstop_h:   Horizontal stop + 1(*) of the visible window
187*4882a593Smuzhiyun       - diwstrt_v:   Vertical start of the visible window
188*4882a593Smuzhiyun       - diwstop_v:   Vertical stop of the visible window
189*4882a593Smuzhiyun       - ddfstrt:     Horizontal start of display DMA
190*4882a593Smuzhiyun       - ddfstop:     Horizontal stop of display DMA
191*4882a593Smuzhiyun       - hscroll:     Horizontal display output delay
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun    Sprite positioning:
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun       - sprstrt_h:   Horizontal start - 4 of sprite
196*4882a593Smuzhiyun       - sprstrt_v:   Vertical start of sprite
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun    (*) Even Commodore did it wrong in the AGA monitor drivers by not adding 1.
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun    Horizontal values are in dotclock cycles (35 ns), vertical values are in
201*4882a593Smuzhiyun    scanlines.
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun    (0, 0) is somewhere in the upper-left corner :-)
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun    Dependencies (AGA, SHRES (35 ns dotclock))
207*4882a593Smuzhiyun    -------------------------------------------
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun    Since there are much more parameters for the Amiga display than for the
210*4882a593Smuzhiyun    frame buffer interface, there must be some dependencies among the Amiga
211*4882a593Smuzhiyun    display parameters. Here's what I found out:
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun       - ddfstrt and ddfstop are best aligned to 64 pixels.
214*4882a593Smuzhiyun       - the chipset needs 64 + 4 horizontal pixels after the DMA start before
215*4882a593Smuzhiyun 	the first pixel is output, so diwstrt_h = ddfstrt + 64 + 4 if you want
216*4882a593Smuzhiyun 	to display the first pixel on the line too. Increase diwstrt_h for
217*4882a593Smuzhiyun 	virtual screen panning.
218*4882a593Smuzhiyun       - the display DMA always fetches 64 pixels at a time (fmode = 3).
219*4882a593Smuzhiyun       - ddfstop is ddfstrt+#pixels - 64.
220*4882a593Smuzhiyun       - diwstop_h = diwstrt_h + xres + 1. Because of the additional 1 this can
221*4882a593Smuzhiyun 	be 1 more than htotal.
222*4882a593Smuzhiyun       - hscroll simply adds a delay to the display output. Smooth horizontal
223*4882a593Smuzhiyun 	panning needs an extra 64 pixels on the left to prefetch the pixels that
224*4882a593Smuzhiyun 	`fall off' on the left.
225*4882a593Smuzhiyun       - if ddfstrt < 192, the sprite DMA cycles are all stolen by the bitplane
226*4882a593Smuzhiyun 	DMA, so it's best to make the DMA start as late as possible.
227*4882a593Smuzhiyun       - you really don't want to make ddfstrt < 128, since this will steal DMA
228*4882a593Smuzhiyun 	cycles from the other DMA channels (audio, floppy and Chip RAM refresh).
229*4882a593Smuzhiyun       - I make diwstop_h and diwstop_v as large as possible.
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun    General dependencies
232*4882a593Smuzhiyun    --------------------
233*4882a593Smuzhiyun 
234*4882a593Smuzhiyun       - all values are SHRES pixel (35ns)
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 		  table 1:fetchstart  table 2:prefetch    table 3:fetchsize
237*4882a593Smuzhiyun 		  ------------------  ----------------    -----------------
238*4882a593Smuzhiyun    Pixclock     # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES
239*4882a593Smuzhiyun    -------------#------+-----+------#------+-----+------#------+-----+------
240*4882a593Smuzhiyun    Bus width 1x #   16 |  32 |  64  #   16 |  32 |  64  #   64 |  64 |  64
241*4882a593Smuzhiyun    Bus width 2x #   32 |  64 | 128  #   32 |  64 |  64  #   64 |  64 | 128
242*4882a593Smuzhiyun    Bus width 4x #   64 | 128 | 256  #   64 |  64 |  64  #   64 | 128 | 256
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun       - chipset needs 4 pixels before the first pixel is output
245*4882a593Smuzhiyun       - ddfstrt must be aligned to fetchstart (table 1)
246*4882a593Smuzhiyun       - chipset needs also prefetch (table 2) to get first pixel data, so
247*4882a593Smuzhiyun 	ddfstrt = ((diwstrt_h - 4) & -fetchstart) - prefetch
248*4882a593Smuzhiyun       - for horizontal panning decrease diwstrt_h
249*4882a593Smuzhiyun       - the length of a fetchline must be aligned to fetchsize (table 3)
250*4882a593Smuzhiyun       - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit
251*4882a593Smuzhiyun 	moved to optimize use of dma (useful for OCS/ECS overscan displays)
252*4882a593Smuzhiyun       - ddfstop is ddfstrt + ddfsize - fetchsize
253*4882a593Smuzhiyun       - If C= didn't change anything for AGA, then at following positions the
254*4882a593Smuzhiyun 	dma bus is already used:
255*4882a593Smuzhiyun 	ddfstrt <  48 -> memory refresh
256*4882a593Smuzhiyun 		<  96 -> disk dma
257*4882a593Smuzhiyun 		< 160 -> audio dma
258*4882a593Smuzhiyun 		< 192 -> sprite 0 dma
259*4882a593Smuzhiyun 		< 416 -> sprite dma (32 per sprite)
260*4882a593Smuzhiyun       - in accordance with the hardware reference manual a hardware stop is at
261*4882a593Smuzhiyun 	192, but AGA (ECS?) can go below this.
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun    DMA priorities
264*4882a593Smuzhiyun    --------------
265*4882a593Smuzhiyun 
266*4882a593Smuzhiyun    Since there are limits on the earliest start value for display DMA and the
267*4882a593Smuzhiyun    display of sprites, I use the following policy on horizontal panning and
268*4882a593Smuzhiyun    the hardware cursor:
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun       - if you want to start display DMA too early, you lose the ability to
271*4882a593Smuzhiyun 	do smooth horizontal panning (xpanstep 1 -> 64).
272*4882a593Smuzhiyun       - if you want to go even further, you lose the hardware cursor too.
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun    IMHO a hardware cursor is more important for X than horizontal scrolling,
275*4882a593Smuzhiyun    so that's my motivation.
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun    Implementation
279*4882a593Smuzhiyun    --------------
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun    ami_decode_var() converts the frame buffer values to the Amiga values. It's
282*4882a593Smuzhiyun    just a `straightforward' implementation of the above rules.
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 
285*4882a593Smuzhiyun    Standard VGA timings
286*4882a593Smuzhiyun    --------------------
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	       xres  yres    left  right  upper  lower    hsync    vsync
289*4882a593Smuzhiyun 	       ----  ----    ----  -----  -----  -----    -----    -----
290*4882a593Smuzhiyun       80x25     720   400      27     45     35     12      108        2
291*4882a593Smuzhiyun       80x30     720   480      27     45     30      9      108        2
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun    These were taken from a XFree86 configuration file, recalculated for a 28 MHz
294*4882a593Smuzhiyun    dotclock (Amigas don't have a 25 MHz dotclock) and converted to frame buffer
295*4882a593Smuzhiyun    generic timings.
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun    As a comparison, graphics/monitor.h suggests the following:
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 	       xres  yres    left  right  upper  lower    hsync    vsync
300*4882a593Smuzhiyun 	       ----  ----    ----  -----  -----  -----    -----    -----
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun       VGA       640   480      52    112     24     19    112 -      2 +
303*4882a593Smuzhiyun       VGA70     640   400      52    112     27     21    112 -      2 -
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun    Sync polarities
307*4882a593Smuzhiyun    ---------------
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun       VSYNC    HSYNC    Vertical size    Vertical total
310*4882a593Smuzhiyun       -----    -----    -------------    --------------
311*4882a593Smuzhiyun 	+        +           Reserved          Reserved
312*4882a593Smuzhiyun 	+        -                400               414
313*4882a593Smuzhiyun 	-        +                350               362
314*4882a593Smuzhiyun 	-        -                480               496
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun    Source: CL-GD542X Technical Reference Manual, Cirrus Logic, Oct 1992
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun    Broadcast video timings
320*4882a593Smuzhiyun    -----------------------
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun    According to the CCIR and RETMA specifications, we have the following values:
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun    CCIR -> PAL
325*4882a593Smuzhiyun    -----------
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun       - a scanline is 64 µs long, of which 52.48 µs are visible. This is about
328*4882a593Smuzhiyun 	736 visible 70 ns pixels per line.
329*4882a593Smuzhiyun       - we have 625 scanlines, of which 575 are visible (interlaced); after
330*4882a593Smuzhiyun 	rounding this becomes 576.
331*4882a593Smuzhiyun 
332*4882a593Smuzhiyun    RETMA -> NTSC
333*4882a593Smuzhiyun    -------------
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun       - a scanline is 63.5 µs long, of which 53.5 µs are visible.  This is about
336*4882a593Smuzhiyun 	736 visible 70 ns pixels per line.
337*4882a593Smuzhiyun       - we have 525 scanlines, of which 485 are visible (interlaced); after
338*4882a593Smuzhiyun 	rounding this becomes 484.
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun    Thus if you want a PAL compatible display, you have to do the following:
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun       - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast
343*4882a593Smuzhiyun 	timings are to be used.
344*4882a593Smuzhiyun       - make sure upper_margin + yres + lower_margin + vsync_len = 625 for an
345*4882a593Smuzhiyun 	interlaced, 312 for a non-interlaced and 156 for a doublescanned
346*4882a593Smuzhiyun 	display.
347*4882a593Smuzhiyun       - make sure left_margin + xres + right_margin + hsync_len = 1816 for a
348*4882a593Smuzhiyun 	SHRES, 908 for a HIRES and 454 for a LORES display.
349*4882a593Smuzhiyun       - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90),
350*4882a593Smuzhiyun 	left_margin + 2 * hsync_len must be greater or equal.
351*4882a593Smuzhiyun       - the upper visible part begins at 48 (interlaced; non-interlaced:24,
352*4882a593Smuzhiyun 	doublescanned:12), upper_margin + 2 * vsync_len must be greater or
353*4882a593Smuzhiyun 	equal.
354*4882a593Smuzhiyun       - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync
355*4882a593Smuzhiyun 	of 4 scanlines
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun    The settings for a NTSC compatible display are straightforward.
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun    Note that in a strict sense the PAL and NTSC standards only define the
360*4882a593Smuzhiyun    encoding of the color part (chrominance) of the video signal and don't say
361*4882a593Smuzhiyun    anything about horizontal/vertical synchronization nor refresh rates.
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 							    -- Geert --
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun *******************************************************************************/
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/*
370*4882a593Smuzhiyun 	 * Custom Chipset Definitions
371*4882a593Smuzhiyun 	 */
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun #define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld)
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	/*
376*4882a593Smuzhiyun 	 * BPLCON0 -- Bitplane Control Register 0
377*4882a593Smuzhiyun 	 */
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun #define BPC0_HIRES	(0x8000)
380*4882a593Smuzhiyun #define BPC0_BPU2	(0x4000) /* Bit plane used count */
381*4882a593Smuzhiyun #define BPC0_BPU1	(0x2000)
382*4882a593Smuzhiyun #define BPC0_BPU0	(0x1000)
383*4882a593Smuzhiyun #define BPC0_HAM	(0x0800) /* HAM mode */
384*4882a593Smuzhiyun #define BPC0_DPF	(0x0400) /* Double playfield */
385*4882a593Smuzhiyun #define BPC0_COLOR	(0x0200) /* Enable colorburst */
386*4882a593Smuzhiyun #define BPC0_GAUD	(0x0100) /* Genlock audio enable */
387*4882a593Smuzhiyun #define BPC0_UHRES	(0x0080) /* Ultrahi res enable */
388*4882a593Smuzhiyun #define BPC0_SHRES	(0x0040) /* Super hi res mode */
389*4882a593Smuzhiyun #define BPC0_BYPASS	(0x0020) /* Bypass LUT - AGA */
390*4882a593Smuzhiyun #define BPC0_BPU3	(0x0010) /* AGA */
391*4882a593Smuzhiyun #define BPC0_LPEN	(0x0008) /* Light pen enable */
392*4882a593Smuzhiyun #define BPC0_LACE	(0x0004) /* Interlace */
393*4882a593Smuzhiyun #define BPC0_ERSY	(0x0002) /* External resync */
394*4882a593Smuzhiyun #define BPC0_ECSENA	(0x0001) /* ECS enable */
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	/*
397*4882a593Smuzhiyun 	 * BPLCON2 -- Bitplane Control Register 2
398*4882a593Smuzhiyun 	 */
399*4882a593Smuzhiyun 
400*4882a593Smuzhiyun #define BPC2_ZDBPSEL2	(0x4000) /* Bitplane to be used for ZD - AGA */
401*4882a593Smuzhiyun #define BPC2_ZDBPSEL1	(0x2000)
402*4882a593Smuzhiyun #define BPC2_ZDBPSEL0	(0x1000)
403*4882a593Smuzhiyun #define BPC2_ZDBPEN	(0x0800) /* Enable ZD with ZDBPSELx - AGA */
404*4882a593Smuzhiyun #define BPC2_ZDCTEN	(0x0400) /* Enable ZD with palette bit #31 - AGA */
405*4882a593Smuzhiyun #define BPC2_KILLEHB	(0x0200) /* Kill EHB mode - AGA */
406*4882a593Smuzhiyun #define BPC2_RDRAM	(0x0100) /* Color table accesses read, not write - AGA */
407*4882a593Smuzhiyun #define BPC2_SOGEN	(0x0080) /* SOG output pin high - AGA */
408*4882a593Smuzhiyun #define BPC2_PF2PRI	(0x0040) /* PF2 priority over PF1 */
409*4882a593Smuzhiyun #define BPC2_PF2P2	(0x0020) /* PF2 priority wrt sprites */
410*4882a593Smuzhiyun #define BPC2_PF2P1	(0x0010)
411*4882a593Smuzhiyun #define BPC2_PF2P0	(0x0008)
412*4882a593Smuzhiyun #define BPC2_PF1P2	(0x0004) /* ditto PF1 */
413*4882a593Smuzhiyun #define BPC2_PF1P1	(0x0002)
414*4882a593Smuzhiyun #define BPC2_PF1P0	(0x0001)
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	/*
417*4882a593Smuzhiyun 	 * BPLCON3 -- Bitplane Control Register 3 (AGA)
418*4882a593Smuzhiyun 	 */
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun #define BPC3_BANK2	(0x8000) /* Bits to select color register bank */
421*4882a593Smuzhiyun #define BPC3_BANK1	(0x4000)
422*4882a593Smuzhiyun #define BPC3_BANK0	(0x2000)
423*4882a593Smuzhiyun #define BPC3_PF2OF2	(0x1000) /* Bits for color table offset when PF2 */
424*4882a593Smuzhiyun #define BPC3_PF2OF1	(0x0800)
425*4882a593Smuzhiyun #define BPC3_PF2OF0	(0x0400)
426*4882a593Smuzhiyun #define BPC3_LOCT	(0x0200) /* Color register writes go to low bits */
427*4882a593Smuzhiyun #define BPC3_SPRES1	(0x0080) /* Sprite resolution bits */
428*4882a593Smuzhiyun #define BPC3_SPRES0	(0x0040)
429*4882a593Smuzhiyun #define BPC3_BRDRBLNK	(0x0020) /* Border blanked? */
430*4882a593Smuzhiyun #define BPC3_BRDRTRAN	(0x0010) /* Border transparent? */
431*4882a593Smuzhiyun #define BPC3_ZDCLKEN	(0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
432*4882a593Smuzhiyun #define BPC3_BRDRSPRT	(0x0002) /* Sprites in border? */
433*4882a593Smuzhiyun #define BPC3_EXTBLKEN	(0x0001) /* BLANK programmable */
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	/*
436*4882a593Smuzhiyun 	 * BPLCON4 -- Bitplane Control Register 4 (AGA)
437*4882a593Smuzhiyun 	 */
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun #define BPC4_BPLAM7	(0x8000) /* bitplane color XOR field */
440*4882a593Smuzhiyun #define BPC4_BPLAM6	(0x4000)
441*4882a593Smuzhiyun #define BPC4_BPLAM5	(0x2000)
442*4882a593Smuzhiyun #define BPC4_BPLAM4	(0x1000)
443*4882a593Smuzhiyun #define BPC4_BPLAM3	(0x0800)
444*4882a593Smuzhiyun #define BPC4_BPLAM2	(0x0400)
445*4882a593Smuzhiyun #define BPC4_BPLAM1	(0x0200)
446*4882a593Smuzhiyun #define BPC4_BPLAM0	(0x0100)
447*4882a593Smuzhiyun #define BPC4_ESPRM7	(0x0080) /* 4 high bits for even sprite colors */
448*4882a593Smuzhiyun #define BPC4_ESPRM6	(0x0040)
449*4882a593Smuzhiyun #define BPC4_ESPRM5	(0x0020)
450*4882a593Smuzhiyun #define BPC4_ESPRM4	(0x0010)
451*4882a593Smuzhiyun #define BPC4_OSPRM7	(0x0008) /* 4 high bits for odd sprite colors */
452*4882a593Smuzhiyun #define BPC4_OSPRM6	(0x0004)
453*4882a593Smuzhiyun #define BPC4_OSPRM5	(0x0002)
454*4882a593Smuzhiyun #define BPC4_OSPRM4	(0x0001)
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	/*
457*4882a593Smuzhiyun 	 * BEAMCON0 -- Beam Control Register
458*4882a593Smuzhiyun 	 */
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun #define BMC0_HARDDIS	(0x4000) /* Disable hardware limits */
461*4882a593Smuzhiyun #define BMC0_LPENDIS	(0x2000) /* Disable light pen latch */
462*4882a593Smuzhiyun #define BMC0_VARVBEN	(0x1000) /* Enable variable vertical blank */
463*4882a593Smuzhiyun #define BMC0_LOLDIS	(0x0800) /* Disable long/short line toggle */
464*4882a593Smuzhiyun #define BMC0_CSCBEN	(0x0400) /* Composite sync/blank */
465*4882a593Smuzhiyun #define BMC0_VARVSYEN	(0x0200) /* Enable variable vertical sync */
466*4882a593Smuzhiyun #define BMC0_VARHSYEN	(0x0100) /* Enable variable horizontal sync */
467*4882a593Smuzhiyun #define BMC0_VARBEAMEN	(0x0080) /* Enable variable beam counters */
468*4882a593Smuzhiyun #define BMC0_DUAL	(0x0040) /* Enable alternate horizontal beam counter */
469*4882a593Smuzhiyun #define BMC0_PAL	(0x0020) /* Set decodes for PAL */
470*4882a593Smuzhiyun #define BMC0_VARCSYEN	(0x0010) /* Enable variable composite sync */
471*4882a593Smuzhiyun #define BMC0_BLANKEN	(0x0008) /* Blank enable (no longer used on AGA) */
472*4882a593Smuzhiyun #define BMC0_CSYTRUE	(0x0004) /* CSY polarity */
473*4882a593Smuzhiyun #define BMC0_VSYTRUE	(0x0002) /* VSY polarity */
474*4882a593Smuzhiyun #define BMC0_HSYTRUE	(0x0001) /* HSY polarity */
475*4882a593Smuzhiyun 
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	/*
478*4882a593Smuzhiyun 	 * FMODE -- Fetch Mode Control Register (AGA)
479*4882a593Smuzhiyun 	 */
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun #define FMODE_SSCAN2	(0x8000) /* Sprite scan-doubling */
482*4882a593Smuzhiyun #define FMODE_BSCAN2	(0x4000) /* Use PF2 modulus every other line */
483*4882a593Smuzhiyun #define FMODE_SPAGEM	(0x0008) /* Sprite page mode */
484*4882a593Smuzhiyun #define FMODE_SPR32	(0x0004) /* Sprite 32 bit fetch */
485*4882a593Smuzhiyun #define FMODE_BPAGEM	(0x0002) /* Bitplane page mode */
486*4882a593Smuzhiyun #define FMODE_BPL32	(0x0001) /* Bitplane 32 bit fetch */
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	/*
489*4882a593Smuzhiyun 	 * Tags used to indicate a specific Pixel Clock
490*4882a593Smuzhiyun 	 *
491*4882a593Smuzhiyun 	 * clk_shift is the shift value to get the timings in 35 ns units
492*4882a593Smuzhiyun 	 */
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	/*
497*4882a593Smuzhiyun 	 * Tags used to indicate the specific chipset
498*4882a593Smuzhiyun 	 */
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun enum { TAG_OCS, TAG_ECS, TAG_AGA };
501*4882a593Smuzhiyun 
502*4882a593Smuzhiyun 	/*
503*4882a593Smuzhiyun 	 * Tags used to indicate the memory bandwidth
504*4882a593Smuzhiyun 	 */
505*4882a593Smuzhiyun 
506*4882a593Smuzhiyun enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 };
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	/*
510*4882a593Smuzhiyun 	 * Clock Definitions, Maximum Display Depth
511*4882a593Smuzhiyun 	 *
512*4882a593Smuzhiyun 	 * These depend on the E-Clock or the Chipset, so they are filled in
513*4882a593Smuzhiyun 	 * dynamically
514*4882a593Smuzhiyun 	 */
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun static u_long pixclock[3];	/* SHRES/HIRES/LORES: index = clk_shift */
517*4882a593Smuzhiyun static u_short maxdepth[3];	/* SHRES/HIRES/LORES: index = clk_shift */
518*4882a593Smuzhiyun static u_short maxfmode, chipset;
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 
521*4882a593Smuzhiyun 	/*
522*4882a593Smuzhiyun 	 * Broadcast Video Timings
523*4882a593Smuzhiyun 	 *
524*4882a593Smuzhiyun 	 * Horizontal values are in 35 ns (SHRES) units
525*4882a593Smuzhiyun 	 * Vertical values are in interlaced scanlines
526*4882a593Smuzhiyun 	 */
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun #define PAL_DIWSTRT_H	(360)	/* PAL Window Limits */
529*4882a593Smuzhiyun #define PAL_DIWSTRT_V	(48)
530*4882a593Smuzhiyun #define PAL_HTOTAL	(1816)
531*4882a593Smuzhiyun #define PAL_VTOTAL	(625)
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun #define NTSC_DIWSTRT_H	(360)	/* NTSC Window Limits */
534*4882a593Smuzhiyun #define NTSC_DIWSTRT_V	(40)
535*4882a593Smuzhiyun #define NTSC_HTOTAL	(1816)
536*4882a593Smuzhiyun #define NTSC_VTOTAL	(525)
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	/*
540*4882a593Smuzhiyun 	 * Various macros
541*4882a593Smuzhiyun 	 */
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun #define up2(v)		(((v) + 1) & -2)
544*4882a593Smuzhiyun #define down2(v)	((v) & -2)
545*4882a593Smuzhiyun #define div2(v)		((v)>>1)
546*4882a593Smuzhiyun #define mod2(v)		((v) & 1)
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun #define up4(v)		(((v) + 3) & -4)
549*4882a593Smuzhiyun #define down4(v)	((v) & -4)
550*4882a593Smuzhiyun #define mul4(v)		((v) << 2)
551*4882a593Smuzhiyun #define div4(v)		((v)>>2)
552*4882a593Smuzhiyun #define mod4(v)		((v) & 3)
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun #define up8(v)		(((v) + 7) & -8)
555*4882a593Smuzhiyun #define down8(v)	((v) & -8)
556*4882a593Smuzhiyun #define div8(v)		((v)>>3)
557*4882a593Smuzhiyun #define mod8(v)		((v) & 7)
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun #define up16(v)		(((v) + 15) & -16)
560*4882a593Smuzhiyun #define down16(v)	((v) & -16)
561*4882a593Smuzhiyun #define div16(v)	((v)>>4)
562*4882a593Smuzhiyun #define mod16(v)	((v) & 15)
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun #define up32(v)		(((v) + 31) & -32)
565*4882a593Smuzhiyun #define down32(v)	((v) & -32)
566*4882a593Smuzhiyun #define div32(v)	((v)>>5)
567*4882a593Smuzhiyun #define mod32(v)	((v) & 31)
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun #define up64(v)		(((v) + 63) & -64)
570*4882a593Smuzhiyun #define down64(v)	((v) & -64)
571*4882a593Smuzhiyun #define div64(v)	((v)>>6)
572*4882a593Smuzhiyun #define mod64(v)	((v) & 63)
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun #define upx(x, v)	(((v) + (x) - 1) & -(x))
575*4882a593Smuzhiyun #define downx(x, v)	((v) & -(x))
576*4882a593Smuzhiyun #define modx(x, v)	((v) & ((x) - 1))
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun /*
579*4882a593Smuzhiyun  * FIXME: Use C variants of the code marked with #ifdef __mc68000__
580*4882a593Smuzhiyun  * in the driver. It shouldn't negatively affect the performance and
581*4882a593Smuzhiyun  * is required for APUS support (once it is re-added to the kernel).
582*4882a593Smuzhiyun  * Needs to be tested on the hardware though..
583*4882a593Smuzhiyun  */
584*4882a593Smuzhiyun /* if x1 is not a constant, this macro won't make real sense :-) */
585*4882a593Smuzhiyun #ifdef __mc68000__
586*4882a593Smuzhiyun #define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \
587*4882a593Smuzhiyun 	"d" (x2), "d" ((long)((x1) / 0x100000000ULL)), "0" ((long)(x1))); res;})
588*4882a593Smuzhiyun #else
589*4882a593Smuzhiyun /* We know a bit about the numbers, so we can do it this way */
590*4882a593Smuzhiyun #define DIVUL(x1, x2) ((((long)((unsigned long long)x1 >> 8) / x2) << 8) + \
591*4882a593Smuzhiyun 	((((long)((unsigned long long)x1 >> 8) % x2) << 8) / x2))
592*4882a593Smuzhiyun #endif
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun #define highw(x)	((u_long)(x)>>16 & 0xffff)
595*4882a593Smuzhiyun #define loww(x)		((u_long)(x) & 0xffff)
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun #define custom		amiga_custom
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun #define VBlankOn()	custom.intena = IF_SETCLR|IF_COPER
600*4882a593Smuzhiyun #define VBlankOff()	custom.intena = IF_COPER
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 
603*4882a593Smuzhiyun 	/*
604*4882a593Smuzhiyun 	 * Chip RAM we reserve for the Frame Buffer
605*4882a593Smuzhiyun 	 *
606*4882a593Smuzhiyun 	 * This defines the Maximum Virtual Screen Size
607*4882a593Smuzhiyun 	 * (Setable per kernel options?)
608*4882a593Smuzhiyun 	 */
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun #define VIDEOMEMSIZE_AGA_2M	(1310720) /* AGA (2MB) : max 1280*1024*256  */
611*4882a593Smuzhiyun #define VIDEOMEMSIZE_AGA_1M	(786432)  /* AGA (1MB) : max 1024*768*256   */
612*4882a593Smuzhiyun #define VIDEOMEMSIZE_ECS_2M	(655360)  /* ECS (2MB) : max 1280*1024*16   */
613*4882a593Smuzhiyun #define VIDEOMEMSIZE_ECS_1M	(393216)  /* ECS (1MB) : max 1024*768*16    */
614*4882a593Smuzhiyun #define VIDEOMEMSIZE_OCS	(262144)  /* OCS       : max ca. 800*600*16 */
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun #define SPRITEMEMSIZE		(64 * 64 / 4) /* max 64*64*4 */
617*4882a593Smuzhiyun #define DUMMYSPRITEMEMSIZE	(8)
618*4882a593Smuzhiyun static u_long spritememory;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun #define CHIPRAM_SAFETY_LIMIT	(16384)
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun static u_long videomemory;
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	/*
625*4882a593Smuzhiyun 	 * This is the earliest allowed start of fetching display data.
626*4882a593Smuzhiyun 	 * Only if you really want no hardware cursor and audio,
627*4882a593Smuzhiyun 	 * set this to 128, but let it better at 192
628*4882a593Smuzhiyun 	 */
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun static u_long min_fstrt = 192;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun #define assignchunk(name, type, ptr, size) \
633*4882a593Smuzhiyun { \
634*4882a593Smuzhiyun 	(name) = (type)(ptr); \
635*4882a593Smuzhiyun 	ptr += size; \
636*4882a593Smuzhiyun }
637*4882a593Smuzhiyun 
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	/*
640*4882a593Smuzhiyun 	 * Copper Instructions
641*4882a593Smuzhiyun 	 */
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun #define CMOVE(val, reg)		(CUSTOM_OFS(reg) << 16 | (val))
644*4882a593Smuzhiyun #define CMOVE2(val, reg)	((CUSTOM_OFS(reg) + 2) << 16 | (val))
645*4882a593Smuzhiyun #define CWAIT(x, y)		(((y) & 0x1fe) << 23 | ((x) & 0x7f0) << 13 | 0x0001fffe)
646*4882a593Smuzhiyun #define CEND			(0xfffffffe)
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun typedef union {
650*4882a593Smuzhiyun 	u_long l;
651*4882a593Smuzhiyun 	u_short w[2];
652*4882a593Smuzhiyun } copins;
653*4882a593Smuzhiyun 
654*4882a593Smuzhiyun static struct copdisplay {
655*4882a593Smuzhiyun 	copins *init;
656*4882a593Smuzhiyun 	copins *wait;
657*4882a593Smuzhiyun 	copins *list[2][2];
658*4882a593Smuzhiyun 	copins *rebuild[2];
659*4882a593Smuzhiyun } copdisplay;
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun static u_short currentcop = 0;
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	/*
664*4882a593Smuzhiyun 	 * Hardware Cursor API Definitions
665*4882a593Smuzhiyun 	 * These used to be in linux/fb.h, but were preliminary and used by
666*4882a593Smuzhiyun 	 * amifb only anyway
667*4882a593Smuzhiyun 	 */
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun #define FBIOGET_FCURSORINFO     0x4607
670*4882a593Smuzhiyun #define FBIOGET_VCURSORINFO     0x4608
671*4882a593Smuzhiyun #define FBIOPUT_VCURSORINFO     0x4609
672*4882a593Smuzhiyun #define FBIOGET_CURSORSTATE     0x460A
673*4882a593Smuzhiyun #define FBIOPUT_CURSORSTATE     0x460B
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun struct fb_fix_cursorinfo {
677*4882a593Smuzhiyun 	__u16 crsr_width;		/* width and height of the cursor in */
678*4882a593Smuzhiyun 	__u16 crsr_height;		/* pixels (zero if no cursor)	*/
679*4882a593Smuzhiyun 	__u16 crsr_xsize;		/* cursor size in display pixels */
680*4882a593Smuzhiyun 	__u16 crsr_ysize;
681*4882a593Smuzhiyun 	__u16 crsr_color1;		/* colormap entry for cursor color1 */
682*4882a593Smuzhiyun 	__u16 crsr_color2;		/* colormap entry for cursor color2 */
683*4882a593Smuzhiyun };
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun struct fb_var_cursorinfo {
686*4882a593Smuzhiyun 	__u16 width;
687*4882a593Smuzhiyun 	__u16 height;
688*4882a593Smuzhiyun 	__u16 xspot;
689*4882a593Smuzhiyun 	__u16 yspot;
690*4882a593Smuzhiyun 	__u8 data[1];			/* field with [height][width]        */
691*4882a593Smuzhiyun };
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun struct fb_cursorstate {
694*4882a593Smuzhiyun 	__s16 xoffset;
695*4882a593Smuzhiyun 	__s16 yoffset;
696*4882a593Smuzhiyun 	__u16 mode;
697*4882a593Smuzhiyun };
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun #define FB_CURSOR_OFF		0
700*4882a593Smuzhiyun #define FB_CURSOR_ON		1
701*4882a593Smuzhiyun #define FB_CURSOR_FLASH		2
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun 	/*
705*4882a593Smuzhiyun 	 * Hardware Cursor
706*4882a593Smuzhiyun 	 */
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun static int cursorrate = 20;	/* Number of frames/flash toggle */
709*4882a593Smuzhiyun static u_short cursorstate = -1;
710*4882a593Smuzhiyun static u_short cursormode = FB_CURSOR_OFF;
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun static u_short *lofsprite, *shfsprite, *dummysprite;
713*4882a593Smuzhiyun 
714*4882a593Smuzhiyun 	/*
715*4882a593Smuzhiyun 	 * Current Video Mode
716*4882a593Smuzhiyun 	 */
717*4882a593Smuzhiyun 
718*4882a593Smuzhiyun struct amifb_par {
719*4882a593Smuzhiyun 
720*4882a593Smuzhiyun 	/* General Values */
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 	int xres;		/* vmode */
723*4882a593Smuzhiyun 	int yres;		/* vmode */
724*4882a593Smuzhiyun 	int vxres;		/* vmode */
725*4882a593Smuzhiyun 	int vyres;		/* vmode */
726*4882a593Smuzhiyun 	int xoffset;		/* vmode */
727*4882a593Smuzhiyun 	int yoffset;		/* vmode */
728*4882a593Smuzhiyun 	u_short bpp;		/* vmode */
729*4882a593Smuzhiyun 	u_short clk_shift;	/* vmode */
730*4882a593Smuzhiyun 	u_short line_shift;	/* vmode */
731*4882a593Smuzhiyun 	int vmode;		/* vmode */
732*4882a593Smuzhiyun 	u_short diwstrt_h;	/* vmode */
733*4882a593Smuzhiyun 	u_short diwstop_h;	/* vmode */
734*4882a593Smuzhiyun 	u_short diwstrt_v;	/* vmode */
735*4882a593Smuzhiyun 	u_short diwstop_v;	/* vmode */
736*4882a593Smuzhiyun 	u_long next_line;	/* modulo for next line */
737*4882a593Smuzhiyun 	u_long next_plane;	/* modulo for next plane */
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	/* Cursor Values */
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	struct {
742*4882a593Smuzhiyun 		short crsr_x;	/* movecursor */
743*4882a593Smuzhiyun 		short crsr_y;	/* movecursor */
744*4882a593Smuzhiyun 		short spot_x;
745*4882a593Smuzhiyun 		short spot_y;
746*4882a593Smuzhiyun 		u_short height;
747*4882a593Smuzhiyun 		u_short width;
748*4882a593Smuzhiyun 		u_short fmode;
749*4882a593Smuzhiyun 	} crsr;
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	/* OCS Hardware Registers */
752*4882a593Smuzhiyun 
753*4882a593Smuzhiyun 	u_long bplpt0;		/* vmode, pan (Note: physical address) */
754*4882a593Smuzhiyun 	u_long bplpt0wrap;	/* vmode, pan (Note: physical address) */
755*4882a593Smuzhiyun 	u_short ddfstrt;
756*4882a593Smuzhiyun 	u_short ddfstop;
757*4882a593Smuzhiyun 	u_short bpl1mod;
758*4882a593Smuzhiyun 	u_short bpl2mod;
759*4882a593Smuzhiyun 	u_short bplcon0;	/* vmode */
760*4882a593Smuzhiyun 	u_short bplcon1;	/* vmode */
761*4882a593Smuzhiyun 	u_short htotal;		/* vmode */
762*4882a593Smuzhiyun 	u_short vtotal;		/* vmode */
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	/* Additional ECS Hardware Registers */
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	u_short bplcon3;	/* vmode */
767*4882a593Smuzhiyun 	u_short beamcon0;	/* vmode */
768*4882a593Smuzhiyun 	u_short hsstrt;		/* vmode */
769*4882a593Smuzhiyun 	u_short hsstop;		/* vmode */
770*4882a593Smuzhiyun 	u_short hbstrt;		/* vmode */
771*4882a593Smuzhiyun 	u_short hbstop;		/* vmode */
772*4882a593Smuzhiyun 	u_short vsstrt;		/* vmode */
773*4882a593Smuzhiyun 	u_short vsstop;		/* vmode */
774*4882a593Smuzhiyun 	u_short vbstrt;		/* vmode */
775*4882a593Smuzhiyun 	u_short vbstop;		/* vmode */
776*4882a593Smuzhiyun 	u_short hcenter;	/* vmode */
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun 	/* Additional AGA Hardware Registers */
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	u_short fmode;		/* vmode */
781*4882a593Smuzhiyun };
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	/*
785*4882a593Smuzhiyun 	 *  Saved color entry 0 so we can restore it when unblanking
786*4882a593Smuzhiyun 	 */
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun static u_char red0, green0, blue0;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_ECS)
792*4882a593Smuzhiyun static u_short ecs_palette[32];
793*4882a593Smuzhiyun #endif
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	/*
797*4882a593Smuzhiyun 	 * Latches for Display Changes during VBlank
798*4882a593Smuzhiyun 	 */
799*4882a593Smuzhiyun 
800*4882a593Smuzhiyun static u_short do_vmode_full = 0;	/* Change the Video Mode */
801*4882a593Smuzhiyun static u_short do_vmode_pan = 0;	/* Update the Video Mode */
802*4882a593Smuzhiyun static short do_blank = 0;		/* (Un)Blank the Screen (±1) */
803*4882a593Smuzhiyun static u_short do_cursor = 0;		/* Move the Cursor */
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	/*
807*4882a593Smuzhiyun 	 * Various Flags
808*4882a593Smuzhiyun 	 */
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun static u_short is_blanked = 0;		/* Screen is Blanked */
811*4882a593Smuzhiyun static u_short is_lace = 0;		/* Screen is laced */
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	/*
814*4882a593Smuzhiyun 	 * Predefined Video Modes
815*4882a593Smuzhiyun 	 *
816*4882a593Smuzhiyun 	 */
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun static struct fb_videomode ami_modedb[] __initdata = {
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun 	/*
821*4882a593Smuzhiyun 	 *  AmigaOS Video Modes
822*4882a593Smuzhiyun 	 *
823*4882a593Smuzhiyun 	 *  If you change these, make sure to update DEFMODE_* as well!
824*4882a593Smuzhiyun 	 */
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	{
827*4882a593Smuzhiyun 		/* 640x200, 15 kHz, 60 Hz (NTSC) */
828*4882a593Smuzhiyun 		"ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
829*4882a593Smuzhiyun 		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
830*4882a593Smuzhiyun 	}, {
831*4882a593Smuzhiyun 		/* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
832*4882a593Smuzhiyun 		"ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
833*4882a593Smuzhiyun 		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
834*4882a593Smuzhiyun 	}, {
835*4882a593Smuzhiyun 		/* 640x256, 15 kHz, 50 Hz (PAL) */
836*4882a593Smuzhiyun 		"pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
837*4882a593Smuzhiyun 		FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
838*4882a593Smuzhiyun 	}, {
839*4882a593Smuzhiyun 		/* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
840*4882a593Smuzhiyun 		"pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
841*4882a593Smuzhiyun 		FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
842*4882a593Smuzhiyun 	}, {
843*4882a593Smuzhiyun 		/* 640x480, 29 kHz, 57 Hz */
844*4882a593Smuzhiyun 		"multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
845*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
846*4882a593Smuzhiyun 	}, {
847*4882a593Smuzhiyun 		/* 640x960, 29 kHz, 57 Hz interlaced */
848*4882a593Smuzhiyun 		"multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72,
849*4882a593Smuzhiyun 		16,
850*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
851*4882a593Smuzhiyun 	}, {
852*4882a593Smuzhiyun 		/* 640x200, 15 kHz, 72 Hz */
853*4882a593Smuzhiyun 		"euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
854*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
855*4882a593Smuzhiyun 	}, {
856*4882a593Smuzhiyun 		/* 640x400, 15 kHz, 72 Hz interlaced */
857*4882a593Smuzhiyun 		"euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52,
858*4882a593Smuzhiyun 		10,
859*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
860*4882a593Smuzhiyun 	}, {
861*4882a593Smuzhiyun 		/* 640x400, 29 kHz, 68 Hz */
862*4882a593Smuzhiyun 		"euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
863*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
864*4882a593Smuzhiyun 	}, {
865*4882a593Smuzhiyun 		/* 640x800, 29 kHz, 68 Hz interlaced */
866*4882a593Smuzhiyun 		"euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80,
867*4882a593Smuzhiyun 		16,
868*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
869*4882a593Smuzhiyun 	}, {
870*4882a593Smuzhiyun 		/* 800x300, 23 kHz, 70 Hz */
871*4882a593Smuzhiyun 		"super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
872*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
873*4882a593Smuzhiyun 	}, {
874*4882a593Smuzhiyun 		/* 800x600, 23 kHz, 70 Hz interlaced */
875*4882a593Smuzhiyun 		"super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80,
876*4882a593Smuzhiyun 		14,
877*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
878*4882a593Smuzhiyun 	}, {
879*4882a593Smuzhiyun 		/* 640x200, 27 kHz, 57 Hz doublescan */
880*4882a593Smuzhiyun 		"dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
881*4882a593Smuzhiyun 		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
882*4882a593Smuzhiyun 	}, {
883*4882a593Smuzhiyun 		/* 640x400, 27 kHz, 57 Hz */
884*4882a593Smuzhiyun 		"dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
885*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
886*4882a593Smuzhiyun 	}, {
887*4882a593Smuzhiyun 		/* 640x800, 27 kHz, 57 Hz interlaced */
888*4882a593Smuzhiyun 		"dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80,
889*4882a593Smuzhiyun 		14,
890*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
891*4882a593Smuzhiyun 	}, {
892*4882a593Smuzhiyun 		/* 640x256, 27 kHz, 47 Hz doublescan */
893*4882a593Smuzhiyun 		"dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
894*4882a593Smuzhiyun 		0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
895*4882a593Smuzhiyun 	}, {
896*4882a593Smuzhiyun 		/* 640x512, 27 kHz, 47 Hz */
897*4882a593Smuzhiyun 		"dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
898*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
899*4882a593Smuzhiyun 	}, {
900*4882a593Smuzhiyun 		/* 640x1024, 27 kHz, 47 Hz interlaced */
901*4882a593Smuzhiyun 		"dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80,
902*4882a593Smuzhiyun 		14,
903*4882a593Smuzhiyun 		0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
904*4882a593Smuzhiyun 	},
905*4882a593Smuzhiyun 
906*4882a593Smuzhiyun 	/*
907*4882a593Smuzhiyun 	 *  VGA Video Modes
908*4882a593Smuzhiyun 	 */
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun 	{
911*4882a593Smuzhiyun 		/* 640x480, 31 kHz, 60 Hz (VGA) */
912*4882a593Smuzhiyun 		"vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
913*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
914*4882a593Smuzhiyun 	}, {
915*4882a593Smuzhiyun 		/* 640x400, 31 kHz, 70 Hz (VGA) */
916*4882a593Smuzhiyun 		"vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
917*4882a593Smuzhiyun 		FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT,
918*4882a593Smuzhiyun 		FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
919*4882a593Smuzhiyun 	},
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun #if 0
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	/*
924*4882a593Smuzhiyun 	 *  A2024 video modes
925*4882a593Smuzhiyun 	 *  These modes don't work yet because there's no A2024 driver.
926*4882a593Smuzhiyun 	 */
927*4882a593Smuzhiyun 
928*4882a593Smuzhiyun 	{
929*4882a593Smuzhiyun 		/* 1024x800, 10 Hz */
930*4882a593Smuzhiyun 		"a2024-10", 10, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
931*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
932*4882a593Smuzhiyun 	}, {
933*4882a593Smuzhiyun 		/* 1024x800, 15 Hz */
934*4882a593Smuzhiyun 		"a2024-15", 15, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
935*4882a593Smuzhiyun 		0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
936*4882a593Smuzhiyun 	}
937*4882a593Smuzhiyun #endif
938*4882a593Smuzhiyun };
939*4882a593Smuzhiyun 
940*4882a593Smuzhiyun #define NUM_TOTAL_MODES  ARRAY_SIZE(ami_modedb)
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun static char *mode_option __initdata = NULL;
943*4882a593Smuzhiyun static int round_down_bpp = 1;	/* for mode probing */
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun 	/*
946*4882a593Smuzhiyun 	 * Some default modes
947*4882a593Smuzhiyun 	 */
948*4882a593Smuzhiyun 
949*4882a593Smuzhiyun 
950*4882a593Smuzhiyun #define DEFMODE_PAL	    2	/* "pal" for PAL OCS/ECS */
951*4882a593Smuzhiyun #define DEFMODE_NTSC	    0	/* "ntsc" for NTSC OCS/ECS */
952*4882a593Smuzhiyun #define DEFMODE_AMBER_PAL   3	/* "pal-lace" for flicker fixed PAL (A3000) */
953*4882a593Smuzhiyun #define DEFMODE_AMBER_NTSC  1	/* "ntsc-lace" for flicker fixed NTSC (A3000) */
954*4882a593Smuzhiyun #define DEFMODE_AGA	    19	/* "vga70" for AGA */
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun static int amifb_ilbm = 0;	/* interleaved or normal bitplanes */
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun static u32 amifb_hfmin __initdata;	/* monitor hfreq lower limit (Hz) */
960*4882a593Smuzhiyun static u32 amifb_hfmax __initdata;	/* monitor hfreq upper limit (Hz) */
961*4882a593Smuzhiyun static u16 amifb_vfmin __initdata;	/* monitor vfreq lower limit (Hz) */
962*4882a593Smuzhiyun static u16 amifb_vfmax __initdata;	/* monitor vfreq upper limit (Hz) */
963*4882a593Smuzhiyun 
964*4882a593Smuzhiyun 
965*4882a593Smuzhiyun 	/*
966*4882a593Smuzhiyun 	 * Macros for the conversion from real world values to hardware register
967*4882a593Smuzhiyun 	 * values
968*4882a593Smuzhiyun 	 *
969*4882a593Smuzhiyun 	 * This helps us to keep our attention on the real stuff...
970*4882a593Smuzhiyun 	 *
971*4882a593Smuzhiyun 	 * Hardware limits for AGA:
972*4882a593Smuzhiyun 	 *
973*4882a593Smuzhiyun 	 *	parameter  min    max  step
974*4882a593Smuzhiyun 	 *	---------  ---   ----  ----
975*4882a593Smuzhiyun 	 *	diwstrt_h    0   2047     1
976*4882a593Smuzhiyun 	 *	diwstrt_v    0   2047     1
977*4882a593Smuzhiyun 	 *	diwstop_h    0   4095     1
978*4882a593Smuzhiyun 	 *	diwstop_v    0   4095     1
979*4882a593Smuzhiyun 	 *
980*4882a593Smuzhiyun 	 *	ddfstrt      0   2032    16
981*4882a593Smuzhiyun 	 *	ddfstop      0   2032    16
982*4882a593Smuzhiyun 	 *
983*4882a593Smuzhiyun 	 *	htotal       8   2048     8
984*4882a593Smuzhiyun 	 *	hsstrt       0   2040     8
985*4882a593Smuzhiyun 	 *	hsstop       0   2040     8
986*4882a593Smuzhiyun 	 *	vtotal       1   4096     1
987*4882a593Smuzhiyun 	 *	vsstrt       0   4095     1
988*4882a593Smuzhiyun 	 *	vsstop       0   4095     1
989*4882a593Smuzhiyun 	 *	hcenter      0   2040     8
990*4882a593Smuzhiyun 	 *
991*4882a593Smuzhiyun 	 *	hbstrt       0   2047     1
992*4882a593Smuzhiyun 	 *	hbstop       0   2047     1
993*4882a593Smuzhiyun 	 *	vbstrt       0   4095     1
994*4882a593Smuzhiyun 	 *	vbstop       0   4095     1
995*4882a593Smuzhiyun 	 *
996*4882a593Smuzhiyun 	 * Horizontal values are in 35 ns (SHRES) pixels
997*4882a593Smuzhiyun 	 * Vertical values are in half scanlines
998*4882a593Smuzhiyun 	 */
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun /* bplcon1 (smooth scrolling) */
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun #define hscroll2hw(hscroll) \
1003*4882a593Smuzhiyun 	(((hscroll) << 12 & 0x3000) | ((hscroll) << 8 & 0xc300) | \
1004*4882a593Smuzhiyun 	 ((hscroll) << 4 & 0x0c00) | ((hscroll) << 2 & 0x00f0) | \
1005*4882a593Smuzhiyun 	 ((hscroll)>>2 & 0x000f))
1006*4882a593Smuzhiyun 
1007*4882a593Smuzhiyun /* diwstrt/diwstop/diwhigh (visible display window) */
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun #define diwstrt2hw(diwstrt_h, diwstrt_v) \
1010*4882a593Smuzhiyun 	(((diwstrt_v) << 7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
1011*4882a593Smuzhiyun #define diwstop2hw(diwstop_h, diwstop_v) \
1012*4882a593Smuzhiyun 	(((diwstop_v) << 7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
1013*4882a593Smuzhiyun #define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
1014*4882a593Smuzhiyun 	(((diwstop_h) << 3 & 0x2000) | ((diwstop_h) << 11 & 0x1800) | \
1015*4882a593Smuzhiyun 	 ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
1016*4882a593Smuzhiyun 	 ((diwstrt_h) << 3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
1017*4882a593Smuzhiyun 
1018*4882a593Smuzhiyun /* ddfstrt/ddfstop (display DMA) */
1019*4882a593Smuzhiyun 
1020*4882a593Smuzhiyun #define ddfstrt2hw(ddfstrt)	div8(ddfstrt)
1021*4882a593Smuzhiyun #define ddfstop2hw(ddfstop)	div8(ddfstop)
1022*4882a593Smuzhiyun 
1023*4882a593Smuzhiyun /* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun #define hsstrt2hw(hsstrt)	(div8(hsstrt))
1026*4882a593Smuzhiyun #define hsstop2hw(hsstop)	(div8(hsstop))
1027*4882a593Smuzhiyun #define htotal2hw(htotal)	(div8(htotal) - 1)
1028*4882a593Smuzhiyun #define vsstrt2hw(vsstrt)	(div2(vsstrt))
1029*4882a593Smuzhiyun #define vsstop2hw(vsstop)	(div2(vsstop))
1030*4882a593Smuzhiyun #define vtotal2hw(vtotal)	(div2(vtotal) - 1)
1031*4882a593Smuzhiyun #define hcenter2hw(htotal)	(div8(htotal))
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun /* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
1034*4882a593Smuzhiyun 
1035*4882a593Smuzhiyun #define hbstrt2hw(hbstrt)	(((hbstrt) << 8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
1036*4882a593Smuzhiyun #define hbstop2hw(hbstop)	(((hbstop) << 8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
1037*4882a593Smuzhiyun #define vbstrt2hw(vbstrt)	(div2(vbstrt))
1038*4882a593Smuzhiyun #define vbstop2hw(vbstop)	(div2(vbstop))
1039*4882a593Smuzhiyun 
1040*4882a593Smuzhiyun /* colour */
1041*4882a593Smuzhiyun 
1042*4882a593Smuzhiyun #define rgb2hw8_high(red, green, blue) \
1043*4882a593Smuzhiyun 	(((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
1044*4882a593Smuzhiyun #define rgb2hw8_low(red, green, blue) \
1045*4882a593Smuzhiyun 	(((red & 0x0f) << 8) | ((green & 0x0f) << 4) | (blue & 0x0f))
1046*4882a593Smuzhiyun #define rgb2hw4(red, green, blue) \
1047*4882a593Smuzhiyun 	(((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
1048*4882a593Smuzhiyun #define rgb2hw2(red, green, blue) \
1049*4882a593Smuzhiyun 	(((red & 0xc0) << 4) | (green & 0xc0) | ((blue & 0xc0)>>4))
1050*4882a593Smuzhiyun 
1051*4882a593Smuzhiyun /* sprpos/sprctl (sprite positioning) */
1052*4882a593Smuzhiyun 
1053*4882a593Smuzhiyun #define spr2hw_pos(start_v, start_h) \
1054*4882a593Smuzhiyun 	(((start_v) << 7 & 0xff00) | ((start_h)>>3 & 0x00ff))
1055*4882a593Smuzhiyun #define spr2hw_ctl(start_v, start_h, stop_v) \
1056*4882a593Smuzhiyun 	(((stop_v) << 7 & 0xff00) | ((start_v)>>4 & 0x0040) | \
1057*4882a593Smuzhiyun 	 ((stop_v)>>5 & 0x0020) | ((start_h) << 3 & 0x0018) | \
1058*4882a593Smuzhiyun 	 ((start_v)>>7 & 0x0004) | ((stop_v)>>8 & 0x0002) | \
1059*4882a593Smuzhiyun 	 ((start_h)>>2 & 0x0001))
1060*4882a593Smuzhiyun 
1061*4882a593Smuzhiyun /* get current vertical position of beam */
1062*4882a593Smuzhiyun #define get_vbpos()	((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
1063*4882a593Smuzhiyun 
1064*4882a593Smuzhiyun 	/*
1065*4882a593Smuzhiyun 	 * Copper Initialisation List
1066*4882a593Smuzhiyun 	 */
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun #define COPINITSIZE (sizeof(copins) * 40)
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun enum {
1071*4882a593Smuzhiyun 	cip_bplcon0
1072*4882a593Smuzhiyun };
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun 	/*
1075*4882a593Smuzhiyun 	 * Long Frame/Short Frame Copper List
1076*4882a593Smuzhiyun 	 * Don't change the order, build_copper()/rebuild_copper() rely on this
1077*4882a593Smuzhiyun 	 */
1078*4882a593Smuzhiyun 
1079*4882a593Smuzhiyun #define COPLISTSIZE (sizeof(copins) * 64)
1080*4882a593Smuzhiyun 
1081*4882a593Smuzhiyun enum {
1082*4882a593Smuzhiyun 	cop_wait, cop_bplcon0,
1083*4882a593Smuzhiyun 	cop_spr0ptrh, cop_spr0ptrl,
1084*4882a593Smuzhiyun 	cop_diwstrt, cop_diwstop,
1085*4882a593Smuzhiyun 	cop_diwhigh,
1086*4882a593Smuzhiyun };
1087*4882a593Smuzhiyun 
1088*4882a593Smuzhiyun 	/*
1089*4882a593Smuzhiyun 	 * Pixel modes for Bitplanes and Sprites
1090*4882a593Smuzhiyun 	 */
1091*4882a593Smuzhiyun 
1092*4882a593Smuzhiyun static u_short bplpixmode[3] = {
1093*4882a593Smuzhiyun 	BPC0_SHRES,			/*  35 ns */
1094*4882a593Smuzhiyun 	BPC0_HIRES,			/*  70 ns */
1095*4882a593Smuzhiyun 	0				/* 140 ns */
1096*4882a593Smuzhiyun };
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun static u_short sprpixmode[3] = {
1099*4882a593Smuzhiyun 	BPC3_SPRES1 | BPC3_SPRES0,	/*  35 ns */
1100*4882a593Smuzhiyun 	BPC3_SPRES1,			/*  70 ns */
1101*4882a593Smuzhiyun 	BPC3_SPRES0			/* 140 ns */
1102*4882a593Smuzhiyun };
1103*4882a593Smuzhiyun 
1104*4882a593Smuzhiyun 	/*
1105*4882a593Smuzhiyun 	 * Fetch modes for Bitplanes and Sprites
1106*4882a593Smuzhiyun 	 */
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun static u_short bplfetchmode[3] = {
1109*4882a593Smuzhiyun 	0,				/* 1x */
1110*4882a593Smuzhiyun 	FMODE_BPL32,			/* 2x */
1111*4882a593Smuzhiyun 	FMODE_BPAGEM | FMODE_BPL32	/* 4x */
1112*4882a593Smuzhiyun };
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun static u_short sprfetchmode[3] = {
1115*4882a593Smuzhiyun 	0,				/* 1x */
1116*4882a593Smuzhiyun 	FMODE_SPR32,			/* 2x */
1117*4882a593Smuzhiyun 	FMODE_SPAGEM | FMODE_SPR32	/* 4x */
1118*4882a593Smuzhiyun };
1119*4882a593Smuzhiyun 
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun /* --------------------------- Hardware routines --------------------------- */
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	/*
1124*4882a593Smuzhiyun 	 * Get the video params out of `var'. If a value doesn't fit, round
1125*4882a593Smuzhiyun 	 * it up, if it's too big, return -EINVAL.
1126*4882a593Smuzhiyun 	 */
1127*4882a593Smuzhiyun 
ami_decode_var(struct fb_var_screeninfo * var,struct amifb_par * par,const struct fb_info * info)1128*4882a593Smuzhiyun static int ami_decode_var(struct fb_var_screeninfo *var, struct amifb_par *par,
1129*4882a593Smuzhiyun 			  const struct fb_info *info)
1130*4882a593Smuzhiyun {
1131*4882a593Smuzhiyun 	u_short clk_shift, line_shift;
1132*4882a593Smuzhiyun 	u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
1133*4882a593Smuzhiyun 	u_int htotal, vtotal;
1134*4882a593Smuzhiyun 
1135*4882a593Smuzhiyun 	/*
1136*4882a593Smuzhiyun 	 * Find a matching Pixel Clock
1137*4882a593Smuzhiyun 	 */
1138*4882a593Smuzhiyun 
1139*4882a593Smuzhiyun 	for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
1140*4882a593Smuzhiyun 		if (var->pixclock <= pixclock[clk_shift])
1141*4882a593Smuzhiyun 			break;
1142*4882a593Smuzhiyun 	if (clk_shift > TAG_LORES) {
1143*4882a593Smuzhiyun 		DPRINTK("pixclock too high\n");
1144*4882a593Smuzhiyun 		return -EINVAL;
1145*4882a593Smuzhiyun 	}
1146*4882a593Smuzhiyun 	par->clk_shift = clk_shift;
1147*4882a593Smuzhiyun 
1148*4882a593Smuzhiyun 	/*
1149*4882a593Smuzhiyun 	 * Check the Geometry Values
1150*4882a593Smuzhiyun 	 */
1151*4882a593Smuzhiyun 
1152*4882a593Smuzhiyun 	if ((par->xres = var->xres) < 64)
1153*4882a593Smuzhiyun 		par->xres = 64;
1154*4882a593Smuzhiyun 	if ((par->yres = var->yres) < 64)
1155*4882a593Smuzhiyun 		par->yres = 64;
1156*4882a593Smuzhiyun 	if ((par->vxres = var->xres_virtual) < par->xres)
1157*4882a593Smuzhiyun 		par->vxres = par->xres;
1158*4882a593Smuzhiyun 	if ((par->vyres = var->yres_virtual) < par->yres)
1159*4882a593Smuzhiyun 		par->vyres = par->yres;
1160*4882a593Smuzhiyun 
1161*4882a593Smuzhiyun 	par->bpp = var->bits_per_pixel;
1162*4882a593Smuzhiyun 	if (!var->nonstd) {
1163*4882a593Smuzhiyun 		if (par->bpp < 1)
1164*4882a593Smuzhiyun 			par->bpp = 1;
1165*4882a593Smuzhiyun 		if (par->bpp > maxdepth[clk_shift]) {
1166*4882a593Smuzhiyun 			if (round_down_bpp && maxdepth[clk_shift])
1167*4882a593Smuzhiyun 				par->bpp = maxdepth[clk_shift];
1168*4882a593Smuzhiyun 			else {
1169*4882a593Smuzhiyun 				DPRINTK("invalid bpp\n");
1170*4882a593Smuzhiyun 				return -EINVAL;
1171*4882a593Smuzhiyun 			}
1172*4882a593Smuzhiyun 		}
1173*4882a593Smuzhiyun 	} else if (var->nonstd == FB_NONSTD_HAM) {
1174*4882a593Smuzhiyun 		if (par->bpp < 6)
1175*4882a593Smuzhiyun 			par->bpp = 6;
1176*4882a593Smuzhiyun 		if (par->bpp != 6) {
1177*4882a593Smuzhiyun 			if (par->bpp < 8)
1178*4882a593Smuzhiyun 				par->bpp = 8;
1179*4882a593Smuzhiyun 			if (par->bpp != 8 || !IS_AGA) {
1180*4882a593Smuzhiyun 				DPRINTK("invalid bpp for ham mode\n");
1181*4882a593Smuzhiyun 				return -EINVAL;
1182*4882a593Smuzhiyun 			}
1183*4882a593Smuzhiyun 		}
1184*4882a593Smuzhiyun 	} else {
1185*4882a593Smuzhiyun 		DPRINTK("unknown nonstd mode\n");
1186*4882a593Smuzhiyun 		return -EINVAL;
1187*4882a593Smuzhiyun 	}
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	/*
1190*4882a593Smuzhiyun 	 * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the following
1191*4882a593Smuzhiyun 	 * checks failed and smooth scrolling is not possible
1192*4882a593Smuzhiyun 	 */
1193*4882a593Smuzhiyun 
1194*4882a593Smuzhiyun 	par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
1195*4882a593Smuzhiyun 	switch (par->vmode & FB_VMODE_MASK) {
1196*4882a593Smuzhiyun 	case FB_VMODE_INTERLACED:
1197*4882a593Smuzhiyun 		line_shift = 0;
1198*4882a593Smuzhiyun 		break;
1199*4882a593Smuzhiyun 	case FB_VMODE_NONINTERLACED:
1200*4882a593Smuzhiyun 		line_shift = 1;
1201*4882a593Smuzhiyun 		break;
1202*4882a593Smuzhiyun 	case FB_VMODE_DOUBLE:
1203*4882a593Smuzhiyun 		if (!IS_AGA) {
1204*4882a593Smuzhiyun 			DPRINTK("double mode only possible with aga\n");
1205*4882a593Smuzhiyun 			return -EINVAL;
1206*4882a593Smuzhiyun 		}
1207*4882a593Smuzhiyun 		line_shift = 2;
1208*4882a593Smuzhiyun 		break;
1209*4882a593Smuzhiyun 	default:
1210*4882a593Smuzhiyun 		DPRINTK("unknown video mode\n");
1211*4882a593Smuzhiyun 		return -EINVAL;
1212*4882a593Smuzhiyun 		break;
1213*4882a593Smuzhiyun 	}
1214*4882a593Smuzhiyun 	par->line_shift = line_shift;
1215*4882a593Smuzhiyun 
1216*4882a593Smuzhiyun 	/*
1217*4882a593Smuzhiyun 	 * Vertical and Horizontal Timings
1218*4882a593Smuzhiyun 	 */
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	xres_n = par->xres << clk_shift;
1221*4882a593Smuzhiyun 	yres_n = par->yres << line_shift;
1222*4882a593Smuzhiyun 	par->htotal = down8((var->left_margin + par->xres + var->right_margin +
1223*4882a593Smuzhiyun 			     var->hsync_len) << clk_shift);
1224*4882a593Smuzhiyun 	par->vtotal =
1225*4882a593Smuzhiyun 		down2(((var->upper_margin + par->yres + var->lower_margin +
1226*4882a593Smuzhiyun 			var->vsync_len) << line_shift) + 1);
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	if (IS_AGA)
1229*4882a593Smuzhiyun 		par->bplcon3 = sprpixmode[clk_shift];
1230*4882a593Smuzhiyun 	else
1231*4882a593Smuzhiyun 		par->bplcon3 = 0;
1232*4882a593Smuzhiyun 	if (var->sync & FB_SYNC_BROADCAST) {
1233*4882a593Smuzhiyun 		par->diwstop_h = par->htotal -
1234*4882a593Smuzhiyun 			((var->right_margin - var->hsync_len) << clk_shift);
1235*4882a593Smuzhiyun 		if (IS_AGA)
1236*4882a593Smuzhiyun 			par->diwstop_h += mod4(var->hsync_len);
1237*4882a593Smuzhiyun 		else
1238*4882a593Smuzhiyun 			par->diwstop_h = down4(par->diwstop_h);
1239*4882a593Smuzhiyun 
1240*4882a593Smuzhiyun 		par->diwstrt_h = par->diwstop_h - xres_n;
1241*4882a593Smuzhiyun 		par->diwstop_v = par->vtotal -
1242*4882a593Smuzhiyun 			((var->lower_margin - var->vsync_len) << line_shift);
1243*4882a593Smuzhiyun 		par->diwstrt_v = par->diwstop_v - yres_n;
1244*4882a593Smuzhiyun 		if (par->diwstop_h >= par->htotal + 8) {
1245*4882a593Smuzhiyun 			DPRINTK("invalid diwstop_h\n");
1246*4882a593Smuzhiyun 			return -EINVAL;
1247*4882a593Smuzhiyun 		}
1248*4882a593Smuzhiyun 		if (par->diwstop_v > par->vtotal) {
1249*4882a593Smuzhiyun 			DPRINTK("invalid diwstop_v\n");
1250*4882a593Smuzhiyun 			return -EINVAL;
1251*4882a593Smuzhiyun 		}
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 		if (!IS_OCS) {
1254*4882a593Smuzhiyun 			/* Initialize sync with some reasonable values for pwrsave */
1255*4882a593Smuzhiyun 			par->hsstrt = 160;
1256*4882a593Smuzhiyun 			par->hsstop = 320;
1257*4882a593Smuzhiyun 			par->vsstrt = 30;
1258*4882a593Smuzhiyun 			par->vsstop = 34;
1259*4882a593Smuzhiyun 		} else {
1260*4882a593Smuzhiyun 			par->hsstrt = 0;
1261*4882a593Smuzhiyun 			par->hsstop = 0;
1262*4882a593Smuzhiyun 			par->vsstrt = 0;
1263*4882a593Smuzhiyun 			par->vsstop = 0;
1264*4882a593Smuzhiyun 		}
1265*4882a593Smuzhiyun 		if (par->vtotal > (PAL_VTOTAL + NTSC_VTOTAL) / 2) {
1266*4882a593Smuzhiyun 			/* PAL video mode */
1267*4882a593Smuzhiyun 			if (par->htotal != PAL_HTOTAL) {
1268*4882a593Smuzhiyun 				DPRINTK("htotal invalid for pal\n");
1269*4882a593Smuzhiyun 				return -EINVAL;
1270*4882a593Smuzhiyun 			}
1271*4882a593Smuzhiyun 			if (par->diwstrt_h < PAL_DIWSTRT_H) {
1272*4882a593Smuzhiyun 				DPRINTK("diwstrt_h too low for pal\n");
1273*4882a593Smuzhiyun 				return -EINVAL;
1274*4882a593Smuzhiyun 			}
1275*4882a593Smuzhiyun 			if (par->diwstrt_v < PAL_DIWSTRT_V) {
1276*4882a593Smuzhiyun 				DPRINTK("diwstrt_v too low for pal\n");
1277*4882a593Smuzhiyun 				return -EINVAL;
1278*4882a593Smuzhiyun 			}
1279*4882a593Smuzhiyun 			htotal = PAL_HTOTAL>>clk_shift;
1280*4882a593Smuzhiyun 			vtotal = PAL_VTOTAL>>1;
1281*4882a593Smuzhiyun 			if (!IS_OCS) {
1282*4882a593Smuzhiyun 				par->beamcon0 = BMC0_PAL;
1283*4882a593Smuzhiyun 				par->bplcon3 |= BPC3_BRDRBLNK;
1284*4882a593Smuzhiyun 			} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1285*4882a593Smuzhiyun 				   AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1286*4882a593Smuzhiyun 				par->beamcon0 = BMC0_PAL;
1287*4882a593Smuzhiyun 				par->hsstop = 1;
1288*4882a593Smuzhiyun 			} else if (amiga_vblank != 50) {
1289*4882a593Smuzhiyun 				DPRINTK("pal not supported by this chipset\n");
1290*4882a593Smuzhiyun 				return -EINVAL;
1291*4882a593Smuzhiyun 			}
1292*4882a593Smuzhiyun 		} else {
1293*4882a593Smuzhiyun 			/* NTSC video mode
1294*4882a593Smuzhiyun 			 * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
1295*4882a593Smuzhiyun 			 * and NTSC activated, so than better let diwstop_h <= 1812
1296*4882a593Smuzhiyun 			 */
1297*4882a593Smuzhiyun 			if (par->htotal != NTSC_HTOTAL) {
1298*4882a593Smuzhiyun 				DPRINTK("htotal invalid for ntsc\n");
1299*4882a593Smuzhiyun 				return -EINVAL;
1300*4882a593Smuzhiyun 			}
1301*4882a593Smuzhiyun 			if (par->diwstrt_h < NTSC_DIWSTRT_H) {
1302*4882a593Smuzhiyun 				DPRINTK("diwstrt_h too low for ntsc\n");
1303*4882a593Smuzhiyun 				return -EINVAL;
1304*4882a593Smuzhiyun 			}
1305*4882a593Smuzhiyun 			if (par->diwstrt_v < NTSC_DIWSTRT_V) {
1306*4882a593Smuzhiyun 				DPRINTK("diwstrt_v too low for ntsc\n");
1307*4882a593Smuzhiyun 				return -EINVAL;
1308*4882a593Smuzhiyun 			}
1309*4882a593Smuzhiyun 			htotal = NTSC_HTOTAL>>clk_shift;
1310*4882a593Smuzhiyun 			vtotal = NTSC_VTOTAL>>1;
1311*4882a593Smuzhiyun 			if (!IS_OCS) {
1312*4882a593Smuzhiyun 				par->beamcon0 = 0;
1313*4882a593Smuzhiyun 				par->bplcon3 |= BPC3_BRDRBLNK;
1314*4882a593Smuzhiyun 			} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1315*4882a593Smuzhiyun 				   AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1316*4882a593Smuzhiyun 				par->beamcon0 = 0;
1317*4882a593Smuzhiyun 				par->hsstop = 1;
1318*4882a593Smuzhiyun 			} else if (amiga_vblank != 60) {
1319*4882a593Smuzhiyun 				DPRINTK("ntsc not supported by this chipset\n");
1320*4882a593Smuzhiyun 				return -EINVAL;
1321*4882a593Smuzhiyun 			}
1322*4882a593Smuzhiyun 		}
1323*4882a593Smuzhiyun 		if (IS_OCS) {
1324*4882a593Smuzhiyun 			if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
1325*4882a593Smuzhiyun 			    par->diwstrt_v >=  512 || par->diwstop_v <  256) {
1326*4882a593Smuzhiyun 				DPRINTK("invalid position for display on ocs\n");
1327*4882a593Smuzhiyun 				return -EINVAL;
1328*4882a593Smuzhiyun 			}
1329*4882a593Smuzhiyun 		}
1330*4882a593Smuzhiyun 	} else if (!IS_OCS) {
1331*4882a593Smuzhiyun 		/* Programmable video mode */
1332*4882a593Smuzhiyun 		par->hsstrt = var->right_margin << clk_shift;
1333*4882a593Smuzhiyun 		par->hsstop = (var->right_margin + var->hsync_len) << clk_shift;
1334*4882a593Smuzhiyun 		par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
1335*4882a593Smuzhiyun 		if (!IS_AGA)
1336*4882a593Smuzhiyun 			par->diwstop_h = down4(par->diwstop_h) - 16;
1337*4882a593Smuzhiyun 		par->diwstrt_h = par->diwstop_h - xres_n;
1338*4882a593Smuzhiyun 		par->hbstop = par->diwstrt_h + 4;
1339*4882a593Smuzhiyun 		par->hbstrt = par->diwstop_h + 4;
1340*4882a593Smuzhiyun 		if (par->hbstrt >= par->htotal + 8)
1341*4882a593Smuzhiyun 			par->hbstrt -= par->htotal;
1342*4882a593Smuzhiyun 		par->hcenter = par->hsstrt + (par->htotal >> 1);
1343*4882a593Smuzhiyun 		par->vsstrt = var->lower_margin << line_shift;
1344*4882a593Smuzhiyun 		par->vsstop = (var->lower_margin + var->vsync_len) << line_shift;
1345*4882a593Smuzhiyun 		par->diwstop_v = par->vtotal;
1346*4882a593Smuzhiyun 		if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
1347*4882a593Smuzhiyun 			par->diwstop_v -= 2;
1348*4882a593Smuzhiyun 		par->diwstrt_v = par->diwstop_v - yres_n;
1349*4882a593Smuzhiyun 		par->vbstop = par->diwstrt_v - 2;
1350*4882a593Smuzhiyun 		par->vbstrt = par->diwstop_v - 2;
1351*4882a593Smuzhiyun 		if (par->vtotal > 2048) {
1352*4882a593Smuzhiyun 			DPRINTK("vtotal too high\n");
1353*4882a593Smuzhiyun 			return -EINVAL;
1354*4882a593Smuzhiyun 		}
1355*4882a593Smuzhiyun 		if (par->htotal > 2048) {
1356*4882a593Smuzhiyun 			DPRINTK("htotal too high\n");
1357*4882a593Smuzhiyun 			return -EINVAL;
1358*4882a593Smuzhiyun 		}
1359*4882a593Smuzhiyun 		par->bplcon3 |= BPC3_EXTBLKEN;
1360*4882a593Smuzhiyun 		par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
1361*4882a593Smuzhiyun 				BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
1362*4882a593Smuzhiyun 				BMC0_PAL | BMC0_VARCSYEN;
1363*4882a593Smuzhiyun 		if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1364*4882a593Smuzhiyun 			par->beamcon0 |= BMC0_HSYTRUE;
1365*4882a593Smuzhiyun 		if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1366*4882a593Smuzhiyun 			par->beamcon0 |= BMC0_VSYTRUE;
1367*4882a593Smuzhiyun 		if (var->sync & FB_SYNC_COMP_HIGH_ACT)
1368*4882a593Smuzhiyun 			par->beamcon0 |= BMC0_CSYTRUE;
1369*4882a593Smuzhiyun 		htotal = par->htotal>>clk_shift;
1370*4882a593Smuzhiyun 		vtotal = par->vtotal>>1;
1371*4882a593Smuzhiyun 	} else {
1372*4882a593Smuzhiyun 		DPRINTK("only broadcast modes possible for ocs\n");
1373*4882a593Smuzhiyun 		return -EINVAL;
1374*4882a593Smuzhiyun 	}
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 	/*
1377*4882a593Smuzhiyun 	 * Checking the DMA timing
1378*4882a593Smuzhiyun 	 */
1379*4882a593Smuzhiyun 
1380*4882a593Smuzhiyun 	fconst = 16 << maxfmode << clk_shift;
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	/*
1383*4882a593Smuzhiyun 	 * smallest window start value without turn off other dma cycles
1384*4882a593Smuzhiyun 	 * than sprite1-7, unless you change min_fstrt
1385*4882a593Smuzhiyun 	 */
1386*4882a593Smuzhiyun 
1387*4882a593Smuzhiyun 
1388*4882a593Smuzhiyun 	fsize = ((maxfmode + clk_shift <= 1) ? fconst : 64);
1389*4882a593Smuzhiyun 	fstrt = downx(fconst, par->diwstrt_h - 4) - fsize;
1390*4882a593Smuzhiyun 	if (fstrt < min_fstrt) {
1391*4882a593Smuzhiyun 		DPRINTK("fetch start too low\n");
1392*4882a593Smuzhiyun 		return -EINVAL;
1393*4882a593Smuzhiyun 	}
1394*4882a593Smuzhiyun 
1395*4882a593Smuzhiyun 	/*
1396*4882a593Smuzhiyun 	 * smallest window start value where smooth scrolling is possible
1397*4882a593Smuzhiyun 	 */
1398*4882a593Smuzhiyun 
1399*4882a593Smuzhiyun 	fstrt = downx(fconst, par->diwstrt_h - fconst + (1 << clk_shift) - 4) -
1400*4882a593Smuzhiyun 		fsize;
1401*4882a593Smuzhiyun 	if (fstrt < min_fstrt)
1402*4882a593Smuzhiyun 		par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1403*4882a593Smuzhiyun 
1404*4882a593Smuzhiyun 	maxfetchstop = down16(par->htotal - 80);
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 	fstrt = downx(fconst, par->diwstrt_h - 4) - 64 - fconst;
1407*4882a593Smuzhiyun 	fsize = upx(fconst, xres_n +
1408*4882a593Smuzhiyun 		    modx(fconst, downx(1 << clk_shift, par->diwstrt_h - 4)));
1409*4882a593Smuzhiyun 	if (fstrt + fsize > maxfetchstop)
1410*4882a593Smuzhiyun 		par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1411*4882a593Smuzhiyun 
1412*4882a593Smuzhiyun 	fsize = upx(fconst, xres_n);
1413*4882a593Smuzhiyun 	if (fstrt + fsize > maxfetchstop) {
1414*4882a593Smuzhiyun 		DPRINTK("fetch stop too high\n");
1415*4882a593Smuzhiyun 		return -EINVAL;
1416*4882a593Smuzhiyun 	}
1417*4882a593Smuzhiyun 
1418*4882a593Smuzhiyun 	if (maxfmode + clk_shift <= 1) {
1419*4882a593Smuzhiyun 		fsize = up64(xres_n + fconst - 1);
1420*4882a593Smuzhiyun 		if (min_fstrt + fsize - 64 > maxfetchstop)
1421*4882a593Smuzhiyun 			par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun 		fsize = up64(xres_n);
1424*4882a593Smuzhiyun 		if (min_fstrt + fsize - 64 > maxfetchstop) {
1425*4882a593Smuzhiyun 			DPRINTK("fetch size too high\n");
1426*4882a593Smuzhiyun 			return -EINVAL;
1427*4882a593Smuzhiyun 		}
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 		fsize -= 64;
1430*4882a593Smuzhiyun 	} else
1431*4882a593Smuzhiyun 		fsize -= fconst;
1432*4882a593Smuzhiyun 
1433*4882a593Smuzhiyun 	/*
1434*4882a593Smuzhiyun 	 * Check if there is enough time to update the bitplane pointers for ywrap
1435*4882a593Smuzhiyun 	 */
1436*4882a593Smuzhiyun 
1437*4882a593Smuzhiyun 	if (par->htotal - fsize - 64 < par->bpp * 64)
1438*4882a593Smuzhiyun 		par->vmode &= ~FB_VMODE_YWRAP;
1439*4882a593Smuzhiyun 
1440*4882a593Smuzhiyun 	/*
1441*4882a593Smuzhiyun 	 * Bitplane calculations and check the Memory Requirements
1442*4882a593Smuzhiyun 	 */
1443*4882a593Smuzhiyun 
1444*4882a593Smuzhiyun 	if (amifb_ilbm) {
1445*4882a593Smuzhiyun 		par->next_plane = div8(upx(16 << maxfmode, par->vxres));
1446*4882a593Smuzhiyun 		par->next_line = par->bpp * par->next_plane;
1447*4882a593Smuzhiyun 		if (par->next_line * par->vyres > info->fix.smem_len) {
1448*4882a593Smuzhiyun 			DPRINTK("too few video mem\n");
1449*4882a593Smuzhiyun 			return -EINVAL;
1450*4882a593Smuzhiyun 		}
1451*4882a593Smuzhiyun 	} else {
1452*4882a593Smuzhiyun 		par->next_line = div8(upx(16 << maxfmode, par->vxres));
1453*4882a593Smuzhiyun 		par->next_plane = par->vyres * par->next_line;
1454*4882a593Smuzhiyun 		if (par->next_plane * par->bpp > info->fix.smem_len) {
1455*4882a593Smuzhiyun 			DPRINTK("too few video mem\n");
1456*4882a593Smuzhiyun 			return -EINVAL;
1457*4882a593Smuzhiyun 		}
1458*4882a593Smuzhiyun 	}
1459*4882a593Smuzhiyun 
1460*4882a593Smuzhiyun 	/*
1461*4882a593Smuzhiyun 	 * Hardware Register Values
1462*4882a593Smuzhiyun 	 */
1463*4882a593Smuzhiyun 
1464*4882a593Smuzhiyun 	par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
1465*4882a593Smuzhiyun 	if (!IS_OCS)
1466*4882a593Smuzhiyun 		par->bplcon0 |= BPC0_ECSENA;
1467*4882a593Smuzhiyun 	if (par->bpp == 8)
1468*4882a593Smuzhiyun 		par->bplcon0 |= BPC0_BPU3;
1469*4882a593Smuzhiyun 	else
1470*4882a593Smuzhiyun 		par->bplcon0 |= par->bpp << 12;
1471*4882a593Smuzhiyun 	if (var->nonstd == FB_NONSTD_HAM)
1472*4882a593Smuzhiyun 		par->bplcon0 |= BPC0_HAM;
1473*4882a593Smuzhiyun 	if (var->sync & FB_SYNC_EXT)
1474*4882a593Smuzhiyun 		par->bplcon0 |= BPC0_ERSY;
1475*4882a593Smuzhiyun 
1476*4882a593Smuzhiyun 	if (IS_AGA)
1477*4882a593Smuzhiyun 		par->fmode = bplfetchmode[maxfmode];
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun 	switch (par->vmode & FB_VMODE_MASK) {
1480*4882a593Smuzhiyun 	case FB_VMODE_INTERLACED:
1481*4882a593Smuzhiyun 		par->bplcon0 |= BPC0_LACE;
1482*4882a593Smuzhiyun 		break;
1483*4882a593Smuzhiyun 	case FB_VMODE_DOUBLE:
1484*4882a593Smuzhiyun 		if (IS_AGA)
1485*4882a593Smuzhiyun 			par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
1486*4882a593Smuzhiyun 		break;
1487*4882a593Smuzhiyun 	}
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun 	if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
1490*4882a593Smuzhiyun 		par->xoffset = var->xoffset;
1491*4882a593Smuzhiyun 		par->yoffset = var->yoffset;
1492*4882a593Smuzhiyun 		if (par->vmode & FB_VMODE_YWRAP) {
1493*4882a593Smuzhiyun 			if (par->yoffset >= par->vyres)
1494*4882a593Smuzhiyun 				par->xoffset = par->yoffset = 0;
1495*4882a593Smuzhiyun 		} else {
1496*4882a593Smuzhiyun 			if (par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) ||
1497*4882a593Smuzhiyun 			    par->yoffset > par->vyres - par->yres)
1498*4882a593Smuzhiyun 				par->xoffset = par->yoffset = 0;
1499*4882a593Smuzhiyun 		}
1500*4882a593Smuzhiyun 	} else
1501*4882a593Smuzhiyun 		par->xoffset = par->yoffset = 0;
1502*4882a593Smuzhiyun 
1503*4882a593Smuzhiyun 	par->crsr.crsr_x = par->crsr.crsr_y = 0;
1504*4882a593Smuzhiyun 	par->crsr.spot_x = par->crsr.spot_y = 0;
1505*4882a593Smuzhiyun 	par->crsr.height = par->crsr.width = 0;
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun 	return 0;
1508*4882a593Smuzhiyun }
1509*4882a593Smuzhiyun 
1510*4882a593Smuzhiyun 	/*
1511*4882a593Smuzhiyun 	 * Fill the `var' structure based on the values in `par' and maybe
1512*4882a593Smuzhiyun 	 * other values read out of the hardware.
1513*4882a593Smuzhiyun 	 */
1514*4882a593Smuzhiyun 
ami_encode_var(struct fb_var_screeninfo * var,struct amifb_par * par)1515*4882a593Smuzhiyun static void ami_encode_var(struct fb_var_screeninfo *var,
1516*4882a593Smuzhiyun 			   struct amifb_par *par)
1517*4882a593Smuzhiyun {
1518*4882a593Smuzhiyun 	u_short clk_shift, line_shift;
1519*4882a593Smuzhiyun 
1520*4882a593Smuzhiyun 	memset(var, 0, sizeof(struct fb_var_screeninfo));
1521*4882a593Smuzhiyun 
1522*4882a593Smuzhiyun 	clk_shift = par->clk_shift;
1523*4882a593Smuzhiyun 	line_shift = par->line_shift;
1524*4882a593Smuzhiyun 
1525*4882a593Smuzhiyun 	var->xres = par->xres;
1526*4882a593Smuzhiyun 	var->yres = par->yres;
1527*4882a593Smuzhiyun 	var->xres_virtual = par->vxres;
1528*4882a593Smuzhiyun 	var->yres_virtual = par->vyres;
1529*4882a593Smuzhiyun 	var->xoffset = par->xoffset;
1530*4882a593Smuzhiyun 	var->yoffset = par->yoffset;
1531*4882a593Smuzhiyun 
1532*4882a593Smuzhiyun 	var->bits_per_pixel = par->bpp;
1533*4882a593Smuzhiyun 	var->grayscale = 0;
1534*4882a593Smuzhiyun 
1535*4882a593Smuzhiyun 	var->red.offset = 0;
1536*4882a593Smuzhiyun 	var->red.msb_right = 0;
1537*4882a593Smuzhiyun 	var->red.length = par->bpp;
1538*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_HAM)
1539*4882a593Smuzhiyun 		var->red.length -= 2;
1540*4882a593Smuzhiyun 	var->blue = var->green = var->red;
1541*4882a593Smuzhiyun 	var->transp.offset = 0;
1542*4882a593Smuzhiyun 	var->transp.length = 0;
1543*4882a593Smuzhiyun 	var->transp.msb_right = 0;
1544*4882a593Smuzhiyun 
1545*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_HAM)
1546*4882a593Smuzhiyun 		var->nonstd = FB_NONSTD_HAM;
1547*4882a593Smuzhiyun 	else
1548*4882a593Smuzhiyun 		var->nonstd = 0;
1549*4882a593Smuzhiyun 	var->activate = 0;
1550*4882a593Smuzhiyun 
1551*4882a593Smuzhiyun 	var->height = -1;
1552*4882a593Smuzhiyun 	var->width = -1;
1553*4882a593Smuzhiyun 
1554*4882a593Smuzhiyun 	var->pixclock = pixclock[clk_shift];
1555*4882a593Smuzhiyun 
1556*4882a593Smuzhiyun 	if (IS_AGA && par->fmode & FMODE_BSCAN2)
1557*4882a593Smuzhiyun 		var->vmode = FB_VMODE_DOUBLE;
1558*4882a593Smuzhiyun 	else if (par->bplcon0 & BPC0_LACE)
1559*4882a593Smuzhiyun 		var->vmode = FB_VMODE_INTERLACED;
1560*4882a593Smuzhiyun 	else
1561*4882a593Smuzhiyun 		var->vmode = FB_VMODE_NONINTERLACED;
1562*4882a593Smuzhiyun 
1563*4882a593Smuzhiyun 	if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
1564*4882a593Smuzhiyun 		var->hsync_len = (par->hsstop - par->hsstrt)>>clk_shift;
1565*4882a593Smuzhiyun 		var->right_margin = par->hsstrt>>clk_shift;
1566*4882a593Smuzhiyun 		var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1567*4882a593Smuzhiyun 		var->vsync_len = (par->vsstop - par->vsstrt)>>line_shift;
1568*4882a593Smuzhiyun 		var->lower_margin = par->vsstrt>>line_shift;
1569*4882a593Smuzhiyun 		var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
1570*4882a593Smuzhiyun 		var->sync = 0;
1571*4882a593Smuzhiyun 		if (par->beamcon0 & BMC0_HSYTRUE)
1572*4882a593Smuzhiyun 			var->sync |= FB_SYNC_HOR_HIGH_ACT;
1573*4882a593Smuzhiyun 		if (par->beamcon0 & BMC0_VSYTRUE)
1574*4882a593Smuzhiyun 			var->sync |= FB_SYNC_VERT_HIGH_ACT;
1575*4882a593Smuzhiyun 		if (par->beamcon0 & BMC0_CSYTRUE)
1576*4882a593Smuzhiyun 			var->sync |= FB_SYNC_COMP_HIGH_ACT;
1577*4882a593Smuzhiyun 	} else {
1578*4882a593Smuzhiyun 		var->sync = FB_SYNC_BROADCAST;
1579*4882a593Smuzhiyun 		var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
1580*4882a593Smuzhiyun 		var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
1581*4882a593Smuzhiyun 		var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1582*4882a593Smuzhiyun 		var->vsync_len = 4>>line_shift;
1583*4882a593Smuzhiyun 		var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
1584*4882a593Smuzhiyun 		var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
1585*4882a593Smuzhiyun 				    var->lower_margin - var->vsync_len;
1586*4882a593Smuzhiyun 	}
1587*4882a593Smuzhiyun 
1588*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_ERSY)
1589*4882a593Smuzhiyun 		var->sync |= FB_SYNC_EXT;
1590*4882a593Smuzhiyun 	if (par->vmode & FB_VMODE_YWRAP)
1591*4882a593Smuzhiyun 		var->vmode |= FB_VMODE_YWRAP;
1592*4882a593Smuzhiyun }
1593*4882a593Smuzhiyun 
1594*4882a593Smuzhiyun 
1595*4882a593Smuzhiyun 	/*
1596*4882a593Smuzhiyun 	 * Update hardware
1597*4882a593Smuzhiyun 	 */
1598*4882a593Smuzhiyun 
ami_update_par(struct fb_info * info)1599*4882a593Smuzhiyun static void ami_update_par(struct fb_info *info)
1600*4882a593Smuzhiyun {
1601*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
1602*4882a593Smuzhiyun 	short clk_shift, vshift, fstrt, fsize, fstop, fconst,  shift, move, mod;
1603*4882a593Smuzhiyun 
1604*4882a593Smuzhiyun 	clk_shift = par->clk_shift;
1605*4882a593Smuzhiyun 
1606*4882a593Smuzhiyun 	if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
1607*4882a593Smuzhiyun 		par->xoffset = upx(16 << maxfmode, par->xoffset);
1608*4882a593Smuzhiyun 
1609*4882a593Smuzhiyun 	fconst = 16 << maxfmode << clk_shift;
1610*4882a593Smuzhiyun 	vshift = modx(16 << maxfmode, par->xoffset);
1611*4882a593Smuzhiyun 	fstrt = par->diwstrt_h - (vshift << clk_shift) - 4;
1612*4882a593Smuzhiyun 	fsize = (par->xres + vshift) << clk_shift;
1613*4882a593Smuzhiyun 	shift = modx(fconst, fstrt);
1614*4882a593Smuzhiyun 	move = downx(2 << maxfmode, div8(par->xoffset));
1615*4882a593Smuzhiyun 	if (maxfmode + clk_shift > 1) {
1616*4882a593Smuzhiyun 		fstrt = downx(fconst, fstrt) - 64;
1617*4882a593Smuzhiyun 		fsize = upx(fconst, fsize);
1618*4882a593Smuzhiyun 		fstop = fstrt + fsize - fconst;
1619*4882a593Smuzhiyun 	} else {
1620*4882a593Smuzhiyun 		mod = fstrt = downx(fconst, fstrt) - fconst;
1621*4882a593Smuzhiyun 		fstop = fstrt + upx(fconst, fsize) - 64;
1622*4882a593Smuzhiyun 		fsize = up64(fsize);
1623*4882a593Smuzhiyun 		fstrt = fstop - fsize + 64;
1624*4882a593Smuzhiyun 		if (fstrt < min_fstrt) {
1625*4882a593Smuzhiyun 			fstop += min_fstrt - fstrt;
1626*4882a593Smuzhiyun 			fstrt = min_fstrt;
1627*4882a593Smuzhiyun 		}
1628*4882a593Smuzhiyun 		move = move - div8((mod - fstrt)>>clk_shift);
1629*4882a593Smuzhiyun 	}
1630*4882a593Smuzhiyun 	mod = par->next_line - div8(fsize>>clk_shift);
1631*4882a593Smuzhiyun 	par->ddfstrt = fstrt;
1632*4882a593Smuzhiyun 	par->ddfstop = fstop;
1633*4882a593Smuzhiyun 	par->bplcon1 = hscroll2hw(shift);
1634*4882a593Smuzhiyun 	par->bpl2mod = mod;
1635*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE)
1636*4882a593Smuzhiyun 		par->bpl2mod += par->next_line;
1637*4882a593Smuzhiyun 	if (IS_AGA && (par->fmode & FMODE_BSCAN2))
1638*4882a593Smuzhiyun 		par->bpl1mod = -div8(fsize>>clk_shift);
1639*4882a593Smuzhiyun 	else
1640*4882a593Smuzhiyun 		par->bpl1mod = par->bpl2mod;
1641*4882a593Smuzhiyun 
1642*4882a593Smuzhiyun 	if (par->yoffset) {
1643*4882a593Smuzhiyun 		par->bplpt0 = info->fix.smem_start +
1644*4882a593Smuzhiyun 			      par->next_line * par->yoffset + move;
1645*4882a593Smuzhiyun 		if (par->vmode & FB_VMODE_YWRAP) {
1646*4882a593Smuzhiyun 			if (par->yoffset > par->vyres - par->yres) {
1647*4882a593Smuzhiyun 				par->bplpt0wrap = info->fix.smem_start + move;
1648*4882a593Smuzhiyun 				if (par->bplcon0 & BPC0_LACE &&
1649*4882a593Smuzhiyun 				    mod2(par->diwstrt_v + par->vyres -
1650*4882a593Smuzhiyun 					 par->yoffset))
1651*4882a593Smuzhiyun 					par->bplpt0wrap += par->next_line;
1652*4882a593Smuzhiyun 			}
1653*4882a593Smuzhiyun 		}
1654*4882a593Smuzhiyun 	} else
1655*4882a593Smuzhiyun 		par->bplpt0 = info->fix.smem_start + move;
1656*4882a593Smuzhiyun 
1657*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
1658*4882a593Smuzhiyun 		par->bplpt0 += par->next_line;
1659*4882a593Smuzhiyun }
1660*4882a593Smuzhiyun 
1661*4882a593Smuzhiyun 
1662*4882a593Smuzhiyun 	/*
1663*4882a593Smuzhiyun 	 * Pan or Wrap the Display
1664*4882a593Smuzhiyun 	 *
1665*4882a593Smuzhiyun 	 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
1666*4882a593Smuzhiyun 	 * in `var'.
1667*4882a593Smuzhiyun 	 */
1668*4882a593Smuzhiyun 
ami_pan_var(struct fb_var_screeninfo * var,struct fb_info * info)1669*4882a593Smuzhiyun static void ami_pan_var(struct fb_var_screeninfo *var, struct fb_info *info)
1670*4882a593Smuzhiyun {
1671*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
1672*4882a593Smuzhiyun 
1673*4882a593Smuzhiyun 	par->xoffset = var->xoffset;
1674*4882a593Smuzhiyun 	par->yoffset = var->yoffset;
1675*4882a593Smuzhiyun 	if (var->vmode & FB_VMODE_YWRAP)
1676*4882a593Smuzhiyun 		par->vmode |= FB_VMODE_YWRAP;
1677*4882a593Smuzhiyun 	else
1678*4882a593Smuzhiyun 		par->vmode &= ~FB_VMODE_YWRAP;
1679*4882a593Smuzhiyun 
1680*4882a593Smuzhiyun 	do_vmode_pan = 0;
1681*4882a593Smuzhiyun 	ami_update_par(info);
1682*4882a593Smuzhiyun 	do_vmode_pan = 1;
1683*4882a593Smuzhiyun }
1684*4882a593Smuzhiyun 
1685*4882a593Smuzhiyun 
ami_update_display(const struct amifb_par * par)1686*4882a593Smuzhiyun static void ami_update_display(const struct amifb_par *par)
1687*4882a593Smuzhiyun {
1688*4882a593Smuzhiyun 	custom.bplcon1 = par->bplcon1;
1689*4882a593Smuzhiyun 	custom.bpl1mod = par->bpl1mod;
1690*4882a593Smuzhiyun 	custom.bpl2mod = par->bpl2mod;
1691*4882a593Smuzhiyun 	custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
1692*4882a593Smuzhiyun 	custom.ddfstop = ddfstop2hw(par->ddfstop);
1693*4882a593Smuzhiyun }
1694*4882a593Smuzhiyun 
1695*4882a593Smuzhiyun 	/*
1696*4882a593Smuzhiyun 	 * Change the video mode (called by VBlank interrupt)
1697*4882a593Smuzhiyun 	 */
1698*4882a593Smuzhiyun 
ami_init_display(const struct amifb_par * par)1699*4882a593Smuzhiyun static void ami_init_display(const struct amifb_par *par)
1700*4882a593Smuzhiyun {
1701*4882a593Smuzhiyun 	int i;
1702*4882a593Smuzhiyun 
1703*4882a593Smuzhiyun 	custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
1704*4882a593Smuzhiyun 	custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
1705*4882a593Smuzhiyun 	if (!IS_OCS) {
1706*4882a593Smuzhiyun 		custom.bplcon3 = par->bplcon3;
1707*4882a593Smuzhiyun 		if (IS_AGA)
1708*4882a593Smuzhiyun 			custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
1709*4882a593Smuzhiyun 		if (par->beamcon0 & BMC0_VARBEAMEN) {
1710*4882a593Smuzhiyun 			custom.htotal = htotal2hw(par->htotal);
1711*4882a593Smuzhiyun 			custom.hbstrt = hbstrt2hw(par->hbstrt);
1712*4882a593Smuzhiyun 			custom.hbstop = hbstop2hw(par->hbstop);
1713*4882a593Smuzhiyun 			custom.hsstrt = hsstrt2hw(par->hsstrt);
1714*4882a593Smuzhiyun 			custom.hsstop = hsstop2hw(par->hsstop);
1715*4882a593Smuzhiyun 			custom.hcenter = hcenter2hw(par->hcenter);
1716*4882a593Smuzhiyun 			custom.vtotal = vtotal2hw(par->vtotal);
1717*4882a593Smuzhiyun 			custom.vbstrt = vbstrt2hw(par->vbstrt);
1718*4882a593Smuzhiyun 			custom.vbstop = vbstop2hw(par->vbstop);
1719*4882a593Smuzhiyun 			custom.vsstrt = vsstrt2hw(par->vsstrt);
1720*4882a593Smuzhiyun 			custom.vsstop = vsstop2hw(par->vsstop);
1721*4882a593Smuzhiyun 		}
1722*4882a593Smuzhiyun 	}
1723*4882a593Smuzhiyun 	if (!IS_OCS || par->hsstop)
1724*4882a593Smuzhiyun 		custom.beamcon0 = par->beamcon0;
1725*4882a593Smuzhiyun 	if (IS_AGA)
1726*4882a593Smuzhiyun 		custom.fmode = par->fmode;
1727*4882a593Smuzhiyun 
1728*4882a593Smuzhiyun 	/*
1729*4882a593Smuzhiyun 	 * The minimum period for audio depends on htotal
1730*4882a593Smuzhiyun 	 */
1731*4882a593Smuzhiyun 
1732*4882a593Smuzhiyun 	amiga_audio_min_period = div16(par->htotal);
1733*4882a593Smuzhiyun 
1734*4882a593Smuzhiyun 	is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
1735*4882a593Smuzhiyun #if 1
1736*4882a593Smuzhiyun 	if (is_lace) {
1737*4882a593Smuzhiyun 		i = custom.vposr >> 15;
1738*4882a593Smuzhiyun 	} else {
1739*4882a593Smuzhiyun 		custom.vposw = custom.vposr | 0x8000;
1740*4882a593Smuzhiyun 		i = 1;
1741*4882a593Smuzhiyun 	}
1742*4882a593Smuzhiyun #else
1743*4882a593Smuzhiyun 	i = 1;
1744*4882a593Smuzhiyun 	custom.vposw = custom.vposr | 0x8000;
1745*4882a593Smuzhiyun #endif
1746*4882a593Smuzhiyun 	custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][i]);
1747*4882a593Smuzhiyun }
1748*4882a593Smuzhiyun 
1749*4882a593Smuzhiyun 	/*
1750*4882a593Smuzhiyun 	 * (Un)Blank the screen (called by VBlank interrupt)
1751*4882a593Smuzhiyun 	 */
1752*4882a593Smuzhiyun 
ami_do_blank(const struct amifb_par * par)1753*4882a593Smuzhiyun static void ami_do_blank(const struct amifb_par *par)
1754*4882a593Smuzhiyun {
1755*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_AGA)
1756*4882a593Smuzhiyun 	u_short bplcon3 = par->bplcon3;
1757*4882a593Smuzhiyun #endif
1758*4882a593Smuzhiyun 	u_char red, green, blue;
1759*4882a593Smuzhiyun 
1760*4882a593Smuzhiyun 	if (do_blank > 0) {
1761*4882a593Smuzhiyun 		custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
1762*4882a593Smuzhiyun 		red = green = blue = 0;
1763*4882a593Smuzhiyun 		if (!IS_OCS && do_blank > 1) {
1764*4882a593Smuzhiyun 			switch (do_blank) {
1765*4882a593Smuzhiyun 			case FB_BLANK_VSYNC_SUSPEND:
1766*4882a593Smuzhiyun 				custom.hsstrt = hsstrt2hw(par->hsstrt);
1767*4882a593Smuzhiyun 				custom.hsstop = hsstop2hw(par->hsstop);
1768*4882a593Smuzhiyun 				custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1769*4882a593Smuzhiyun 				custom.vsstop = vsstop2hw(par->vtotal + 4);
1770*4882a593Smuzhiyun 				break;
1771*4882a593Smuzhiyun 			case FB_BLANK_HSYNC_SUSPEND:
1772*4882a593Smuzhiyun 				custom.hsstrt = hsstrt2hw(par->htotal + 16);
1773*4882a593Smuzhiyun 				custom.hsstop = hsstop2hw(par->htotal + 16);
1774*4882a593Smuzhiyun 				custom.vsstrt = vsstrt2hw(par->vsstrt);
1775*4882a593Smuzhiyun 				custom.vsstop = vsstrt2hw(par->vsstop);
1776*4882a593Smuzhiyun 				break;
1777*4882a593Smuzhiyun 			case FB_BLANK_POWERDOWN:
1778*4882a593Smuzhiyun 				custom.hsstrt = hsstrt2hw(par->htotal + 16);
1779*4882a593Smuzhiyun 				custom.hsstop = hsstop2hw(par->htotal + 16);
1780*4882a593Smuzhiyun 				custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1781*4882a593Smuzhiyun 				custom.vsstop = vsstop2hw(par->vtotal + 4);
1782*4882a593Smuzhiyun 				break;
1783*4882a593Smuzhiyun 			}
1784*4882a593Smuzhiyun 			if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
1785*4882a593Smuzhiyun 				custom.htotal = htotal2hw(par->htotal);
1786*4882a593Smuzhiyun 				custom.vtotal = vtotal2hw(par->vtotal);
1787*4882a593Smuzhiyun 				custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
1788*4882a593Smuzhiyun 						  BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
1789*4882a593Smuzhiyun 			}
1790*4882a593Smuzhiyun 		}
1791*4882a593Smuzhiyun 	} else {
1792*4882a593Smuzhiyun 		custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
1793*4882a593Smuzhiyun 		red = red0;
1794*4882a593Smuzhiyun 		green = green0;
1795*4882a593Smuzhiyun 		blue = blue0;
1796*4882a593Smuzhiyun 		if (!IS_OCS) {
1797*4882a593Smuzhiyun 			custom.hsstrt = hsstrt2hw(par->hsstrt);
1798*4882a593Smuzhiyun 			custom.hsstop = hsstop2hw(par->hsstop);
1799*4882a593Smuzhiyun 			custom.vsstrt = vsstrt2hw(par->vsstrt);
1800*4882a593Smuzhiyun 			custom.vsstop = vsstop2hw(par->vsstop);
1801*4882a593Smuzhiyun 			custom.beamcon0 = par->beamcon0;
1802*4882a593Smuzhiyun 		}
1803*4882a593Smuzhiyun 	}
1804*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_AGA)
1805*4882a593Smuzhiyun 	if (IS_AGA) {
1806*4882a593Smuzhiyun 		custom.bplcon3 = bplcon3;
1807*4882a593Smuzhiyun 		custom.color[0] = rgb2hw8_high(red, green, blue);
1808*4882a593Smuzhiyun 		custom.bplcon3 = bplcon3 | BPC3_LOCT;
1809*4882a593Smuzhiyun 		custom.color[0] = rgb2hw8_low(red, green, blue);
1810*4882a593Smuzhiyun 		custom.bplcon3 = bplcon3;
1811*4882a593Smuzhiyun 	} else
1812*4882a593Smuzhiyun #endif
1813*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_ECS)
1814*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_SHRES) {
1815*4882a593Smuzhiyun 		u_short color, mask;
1816*4882a593Smuzhiyun 		int i;
1817*4882a593Smuzhiyun 
1818*4882a593Smuzhiyun 		mask = 0x3333;
1819*4882a593Smuzhiyun 		color = rgb2hw2(red, green, blue);
1820*4882a593Smuzhiyun 		for (i = 12; i >= 0; i -= 4)
1821*4882a593Smuzhiyun 			custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1822*4882a593Smuzhiyun 		mask <<= 2; color >>= 2;
1823*4882a593Smuzhiyun 		for (i = 3; i >= 0; i--)
1824*4882a593Smuzhiyun 			custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1825*4882a593Smuzhiyun 	} else
1826*4882a593Smuzhiyun #endif
1827*4882a593Smuzhiyun 		custom.color[0] = rgb2hw4(red, green, blue);
1828*4882a593Smuzhiyun 	is_blanked = do_blank > 0 ? do_blank : 0;
1829*4882a593Smuzhiyun }
1830*4882a593Smuzhiyun 
ami_get_fix_cursorinfo(struct fb_fix_cursorinfo * fix,const struct amifb_par * par)1831*4882a593Smuzhiyun static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix,
1832*4882a593Smuzhiyun 				  const struct amifb_par *par)
1833*4882a593Smuzhiyun {
1834*4882a593Smuzhiyun 	fix->crsr_width = fix->crsr_xsize = par->crsr.width;
1835*4882a593Smuzhiyun 	fix->crsr_height = fix->crsr_ysize = par->crsr.height;
1836*4882a593Smuzhiyun 	fix->crsr_color1 = 17;
1837*4882a593Smuzhiyun 	fix->crsr_color2 = 18;
1838*4882a593Smuzhiyun 	return 0;
1839*4882a593Smuzhiyun }
1840*4882a593Smuzhiyun 
ami_get_var_cursorinfo(struct fb_var_cursorinfo * var,u_char __user * data,const struct amifb_par * par)1841*4882a593Smuzhiyun static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var,
1842*4882a593Smuzhiyun 				  u_char __user *data,
1843*4882a593Smuzhiyun 				  const struct amifb_par *par)
1844*4882a593Smuzhiyun {
1845*4882a593Smuzhiyun 	register u_short *lspr, *sspr;
1846*4882a593Smuzhiyun #ifdef __mc68000__
1847*4882a593Smuzhiyun 	register u_long datawords asm ("d2");
1848*4882a593Smuzhiyun #else
1849*4882a593Smuzhiyun 	register u_long datawords;
1850*4882a593Smuzhiyun #endif
1851*4882a593Smuzhiyun 	register short delta;
1852*4882a593Smuzhiyun 	register u_char color;
1853*4882a593Smuzhiyun 	short height, width, bits, words;
1854*4882a593Smuzhiyun 	int size, alloc;
1855*4882a593Smuzhiyun 
1856*4882a593Smuzhiyun 	size = par->crsr.height * par->crsr.width;
1857*4882a593Smuzhiyun 	alloc = var->height * var->width;
1858*4882a593Smuzhiyun 	var->height = par->crsr.height;
1859*4882a593Smuzhiyun 	var->width = par->crsr.width;
1860*4882a593Smuzhiyun 	var->xspot = par->crsr.spot_x;
1861*4882a593Smuzhiyun 	var->yspot = par->crsr.spot_y;
1862*4882a593Smuzhiyun 	if (size > var->height * var->width)
1863*4882a593Smuzhiyun 		return -ENAMETOOLONG;
1864*4882a593Smuzhiyun 	delta = 1 << par->crsr.fmode;
1865*4882a593Smuzhiyun 	lspr = lofsprite + (delta << 1);
1866*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE)
1867*4882a593Smuzhiyun 		sspr = shfsprite + (delta << 1);
1868*4882a593Smuzhiyun 	else
1869*4882a593Smuzhiyun 		sspr = NULL;
1870*4882a593Smuzhiyun 	for (height = (short)var->height - 1; height >= 0; height--) {
1871*4882a593Smuzhiyun 		bits = 0; words = delta; datawords = 0;
1872*4882a593Smuzhiyun 		for (width = (short)var->width - 1; width >= 0; width--) {
1873*4882a593Smuzhiyun 			if (bits == 0) {
1874*4882a593Smuzhiyun 				bits = 16; --words;
1875*4882a593Smuzhiyun #ifdef __mc68000__
1876*4882a593Smuzhiyun 				asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
1877*4882a593Smuzhiyun 					: "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
1878*4882a593Smuzhiyun #else
1879*4882a593Smuzhiyun 				datawords = (*(lspr + delta) << 16) | (*lspr++);
1880*4882a593Smuzhiyun #endif
1881*4882a593Smuzhiyun 			}
1882*4882a593Smuzhiyun 			--bits;
1883*4882a593Smuzhiyun #ifdef __mc68000__
1884*4882a593Smuzhiyun 			asm volatile (
1885*4882a593Smuzhiyun 				"clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
1886*4882a593Smuzhiyun 				"swap %1 ; lslw #1,%1 ; roxlb #1,%0"
1887*4882a593Smuzhiyun 				: "=d" (color), "=d" (datawords) : "1" (datawords));
1888*4882a593Smuzhiyun #else
1889*4882a593Smuzhiyun 			color = (((datawords >> 30) & 2)
1890*4882a593Smuzhiyun 				 | ((datawords >> 15) & 1));
1891*4882a593Smuzhiyun 			datawords <<= 1;
1892*4882a593Smuzhiyun #endif
1893*4882a593Smuzhiyun 			/* FIXME: check the return value + test the change */
1894*4882a593Smuzhiyun 			put_user(color, data++);
1895*4882a593Smuzhiyun 		}
1896*4882a593Smuzhiyun 		if (bits > 0) {
1897*4882a593Smuzhiyun 			--words; ++lspr;
1898*4882a593Smuzhiyun 		}
1899*4882a593Smuzhiyun 		while (--words >= 0)
1900*4882a593Smuzhiyun 			++lspr;
1901*4882a593Smuzhiyun #ifdef __mc68000__
1902*4882a593Smuzhiyun 		asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
1903*4882a593Smuzhiyun 			: "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
1904*4882a593Smuzhiyun #else
1905*4882a593Smuzhiyun 		lspr += delta;
1906*4882a593Smuzhiyun 		if (sspr) {
1907*4882a593Smuzhiyun 			u_short *tmp = lspr;
1908*4882a593Smuzhiyun 			lspr = sspr;
1909*4882a593Smuzhiyun 			sspr = tmp;
1910*4882a593Smuzhiyun 		}
1911*4882a593Smuzhiyun #endif
1912*4882a593Smuzhiyun 	}
1913*4882a593Smuzhiyun 	return 0;
1914*4882a593Smuzhiyun }
1915*4882a593Smuzhiyun 
ami_set_var_cursorinfo(struct fb_var_cursorinfo * var,u_char __user * data,struct amifb_par * par)1916*4882a593Smuzhiyun static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var,
1917*4882a593Smuzhiyun 				  u_char __user *data, struct amifb_par *par)
1918*4882a593Smuzhiyun {
1919*4882a593Smuzhiyun 	register u_short *lspr, *sspr;
1920*4882a593Smuzhiyun #ifdef __mc68000__
1921*4882a593Smuzhiyun 	register u_long datawords asm ("d2");
1922*4882a593Smuzhiyun #else
1923*4882a593Smuzhiyun 	register u_long datawords;
1924*4882a593Smuzhiyun #endif
1925*4882a593Smuzhiyun 	register short delta;
1926*4882a593Smuzhiyun 	u_short fmode;
1927*4882a593Smuzhiyun 	short height, width, bits, words;
1928*4882a593Smuzhiyun 
1929*4882a593Smuzhiyun 	if (!var->width)
1930*4882a593Smuzhiyun 		return -EINVAL;
1931*4882a593Smuzhiyun 	else if (var->width <= 16)
1932*4882a593Smuzhiyun 		fmode = TAG_FMODE_1;
1933*4882a593Smuzhiyun 	else if (var->width <= 32)
1934*4882a593Smuzhiyun 		fmode = TAG_FMODE_2;
1935*4882a593Smuzhiyun 	else if (var->width <= 64)
1936*4882a593Smuzhiyun 		fmode = TAG_FMODE_4;
1937*4882a593Smuzhiyun 	else
1938*4882a593Smuzhiyun 		return -EINVAL;
1939*4882a593Smuzhiyun 	if (fmode > maxfmode)
1940*4882a593Smuzhiyun 		return -EINVAL;
1941*4882a593Smuzhiyun 	if (!var->height)
1942*4882a593Smuzhiyun 		return -EINVAL;
1943*4882a593Smuzhiyun 	delta = 1 << fmode;
1944*4882a593Smuzhiyun 	lofsprite = shfsprite = (u_short *)spritememory;
1945*4882a593Smuzhiyun 	lspr = lofsprite + (delta << 1);
1946*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE) {
1947*4882a593Smuzhiyun 		if (((var->height + 4) << fmode << 2) > SPRITEMEMSIZE)
1948*4882a593Smuzhiyun 			return -EINVAL;
1949*4882a593Smuzhiyun 		memset(lspr, 0, (var->height + 4) << fmode << 2);
1950*4882a593Smuzhiyun 		shfsprite += ((var->height + 5)&-2) << fmode;
1951*4882a593Smuzhiyun 		sspr = shfsprite + (delta << 1);
1952*4882a593Smuzhiyun 	} else {
1953*4882a593Smuzhiyun 		if (((var->height + 2) << fmode << 2) > SPRITEMEMSIZE)
1954*4882a593Smuzhiyun 			return -EINVAL;
1955*4882a593Smuzhiyun 		memset(lspr, 0, (var->height + 2) << fmode << 2);
1956*4882a593Smuzhiyun 		sspr = NULL;
1957*4882a593Smuzhiyun 	}
1958*4882a593Smuzhiyun 	for (height = (short)var->height - 1; height >= 0; height--) {
1959*4882a593Smuzhiyun 		bits = 16; words = delta; datawords = 0;
1960*4882a593Smuzhiyun 		for (width = (short)var->width - 1; width >= 0; width--) {
1961*4882a593Smuzhiyun 			unsigned long tdata = 0;
1962*4882a593Smuzhiyun 			/* FIXME: check the return value + test the change */
1963*4882a593Smuzhiyun 			get_user(tdata, data);
1964*4882a593Smuzhiyun 			data++;
1965*4882a593Smuzhiyun #ifdef __mc68000__
1966*4882a593Smuzhiyun 			asm volatile (
1967*4882a593Smuzhiyun 				"lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
1968*4882a593Smuzhiyun 				"lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
1969*4882a593Smuzhiyun 				: "=d" (datawords)
1970*4882a593Smuzhiyun 				: "0" (datawords), "d" (tdata));
1971*4882a593Smuzhiyun #else
1972*4882a593Smuzhiyun 			datawords = ((datawords << 1) & 0xfffefffe);
1973*4882a593Smuzhiyun 			datawords |= tdata & 1;
1974*4882a593Smuzhiyun 			datawords |= (tdata & 2) << (16 - 1);
1975*4882a593Smuzhiyun #endif
1976*4882a593Smuzhiyun 			if (--bits == 0) {
1977*4882a593Smuzhiyun 				bits = 16; --words;
1978*4882a593Smuzhiyun #ifdef __mc68000__
1979*4882a593Smuzhiyun 				asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
1980*4882a593Smuzhiyun 					: "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
1981*4882a593Smuzhiyun #else
1982*4882a593Smuzhiyun 				*(lspr + delta) = (u_short) (datawords >> 16);
1983*4882a593Smuzhiyun 				*lspr++ = (u_short) (datawords & 0xffff);
1984*4882a593Smuzhiyun #endif
1985*4882a593Smuzhiyun 			}
1986*4882a593Smuzhiyun 		}
1987*4882a593Smuzhiyun 		if (bits < 16) {
1988*4882a593Smuzhiyun 			--words;
1989*4882a593Smuzhiyun #ifdef __mc68000__
1990*4882a593Smuzhiyun 			asm volatile (
1991*4882a593Smuzhiyun 				"swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
1992*4882a593Smuzhiyun 				"swap %2 ; lslw %4,%2 ; movew %2,%0@+"
1993*4882a593Smuzhiyun 				: "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
1994*4882a593Smuzhiyun #else
1995*4882a593Smuzhiyun 			*(lspr + delta) = (u_short) (datawords >> (16 + bits));
1996*4882a593Smuzhiyun 			*lspr++ = (u_short) ((datawords & 0x0000ffff) >> bits);
1997*4882a593Smuzhiyun #endif
1998*4882a593Smuzhiyun 		}
1999*4882a593Smuzhiyun 		while (--words >= 0) {
2000*4882a593Smuzhiyun #ifdef __mc68000__
2001*4882a593Smuzhiyun 			asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
2002*4882a593Smuzhiyun 				: "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
2003*4882a593Smuzhiyun #else
2004*4882a593Smuzhiyun 			*(lspr + delta) = 0;
2005*4882a593Smuzhiyun 			*lspr++ = 0;
2006*4882a593Smuzhiyun #endif
2007*4882a593Smuzhiyun 		}
2008*4882a593Smuzhiyun #ifdef __mc68000__
2009*4882a593Smuzhiyun 		asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
2010*4882a593Smuzhiyun 			: "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
2011*4882a593Smuzhiyun #else
2012*4882a593Smuzhiyun 		lspr += delta;
2013*4882a593Smuzhiyun 		if (sspr) {
2014*4882a593Smuzhiyun 			u_short *tmp = lspr;
2015*4882a593Smuzhiyun 			lspr = sspr;
2016*4882a593Smuzhiyun 			sspr = tmp;
2017*4882a593Smuzhiyun 		}
2018*4882a593Smuzhiyun #endif
2019*4882a593Smuzhiyun 	}
2020*4882a593Smuzhiyun 	par->crsr.height = var->height;
2021*4882a593Smuzhiyun 	par->crsr.width = var->width;
2022*4882a593Smuzhiyun 	par->crsr.spot_x = var->xspot;
2023*4882a593Smuzhiyun 	par->crsr.spot_y = var->yspot;
2024*4882a593Smuzhiyun 	par->crsr.fmode = fmode;
2025*4882a593Smuzhiyun 	if (IS_AGA) {
2026*4882a593Smuzhiyun 		par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
2027*4882a593Smuzhiyun 		par->fmode |= sprfetchmode[fmode];
2028*4882a593Smuzhiyun 		custom.fmode = par->fmode;
2029*4882a593Smuzhiyun 	}
2030*4882a593Smuzhiyun 	return 0;
2031*4882a593Smuzhiyun }
2032*4882a593Smuzhiyun 
ami_get_cursorstate(struct fb_cursorstate * state,const struct amifb_par * par)2033*4882a593Smuzhiyun static int ami_get_cursorstate(struct fb_cursorstate *state,
2034*4882a593Smuzhiyun 			       const struct amifb_par *par)
2035*4882a593Smuzhiyun {
2036*4882a593Smuzhiyun 	state->xoffset = par->crsr.crsr_x;
2037*4882a593Smuzhiyun 	state->yoffset = par->crsr.crsr_y;
2038*4882a593Smuzhiyun 	state->mode = cursormode;
2039*4882a593Smuzhiyun 	return 0;
2040*4882a593Smuzhiyun }
2041*4882a593Smuzhiyun 
ami_set_cursorstate(struct fb_cursorstate * state,struct amifb_par * par)2042*4882a593Smuzhiyun static int ami_set_cursorstate(struct fb_cursorstate *state,
2043*4882a593Smuzhiyun 			       struct amifb_par *par)
2044*4882a593Smuzhiyun {
2045*4882a593Smuzhiyun 	par->crsr.crsr_x = state->xoffset;
2046*4882a593Smuzhiyun 	par->crsr.crsr_y = state->yoffset;
2047*4882a593Smuzhiyun 	if ((cursormode = state->mode) == FB_CURSOR_OFF)
2048*4882a593Smuzhiyun 		cursorstate = -1;
2049*4882a593Smuzhiyun 	do_cursor = 1;
2050*4882a593Smuzhiyun 	return 0;
2051*4882a593Smuzhiyun }
2052*4882a593Smuzhiyun 
ami_set_sprite(const struct amifb_par * par)2053*4882a593Smuzhiyun static void ami_set_sprite(const struct amifb_par *par)
2054*4882a593Smuzhiyun {
2055*4882a593Smuzhiyun 	copins *copl, *cops;
2056*4882a593Smuzhiyun 	u_short hs, vs, ve;
2057*4882a593Smuzhiyun 	u_long pl, ps;
2058*4882a593Smuzhiyun 	short mx, my;
2059*4882a593Smuzhiyun 
2060*4882a593Smuzhiyun 	cops = copdisplay.list[currentcop][0];
2061*4882a593Smuzhiyun 	copl = copdisplay.list[currentcop][1];
2062*4882a593Smuzhiyun 	ps = pl = ZTWO_PADDR(dummysprite);
2063*4882a593Smuzhiyun 	mx = par->crsr.crsr_x - par->crsr.spot_x;
2064*4882a593Smuzhiyun 	my = par->crsr.crsr_y - par->crsr.spot_y;
2065*4882a593Smuzhiyun 	if (!(par->vmode & FB_VMODE_YWRAP)) {
2066*4882a593Smuzhiyun 		mx -= par->xoffset;
2067*4882a593Smuzhiyun 		my -= par->yoffset;
2068*4882a593Smuzhiyun 	}
2069*4882a593Smuzhiyun 	if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
2070*4882a593Smuzhiyun 	    mx > -(short)par->crsr.width && mx < par->xres &&
2071*4882a593Smuzhiyun 	    my > -(short)par->crsr.height && my < par->yres) {
2072*4882a593Smuzhiyun 		pl = ZTWO_PADDR(lofsprite);
2073*4882a593Smuzhiyun 		hs = par->diwstrt_h + (mx << par->clk_shift) - 4;
2074*4882a593Smuzhiyun 		vs = par->diwstrt_v + (my << par->line_shift);
2075*4882a593Smuzhiyun 		ve = vs + (par->crsr.height << par->line_shift);
2076*4882a593Smuzhiyun 		if (par->bplcon0 & BPC0_LACE) {
2077*4882a593Smuzhiyun 			ps = ZTWO_PADDR(shfsprite);
2078*4882a593Smuzhiyun 			lofsprite[0] = spr2hw_pos(vs, hs);
2079*4882a593Smuzhiyun 			shfsprite[0] = spr2hw_pos(vs + 1, hs);
2080*4882a593Smuzhiyun 			if (mod2(vs)) {
2081*4882a593Smuzhiyun 				lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2082*4882a593Smuzhiyun 				shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
2083*4882a593Smuzhiyun 				swap(pl, ps);
2084*4882a593Smuzhiyun 			} else {
2085*4882a593Smuzhiyun 				lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
2086*4882a593Smuzhiyun 				shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
2087*4882a593Smuzhiyun 			}
2088*4882a593Smuzhiyun 		} else {
2089*4882a593Smuzhiyun 			lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
2090*4882a593Smuzhiyun 			lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2091*4882a593Smuzhiyun 		}
2092*4882a593Smuzhiyun 	}
2093*4882a593Smuzhiyun 	copl[cop_spr0ptrh].w[1] = highw(pl);
2094*4882a593Smuzhiyun 	copl[cop_spr0ptrl].w[1] = loww(pl);
2095*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE) {
2096*4882a593Smuzhiyun 		cops[cop_spr0ptrh].w[1] = highw(ps);
2097*4882a593Smuzhiyun 		cops[cop_spr0ptrl].w[1] = loww(ps);
2098*4882a593Smuzhiyun 	}
2099*4882a593Smuzhiyun }
2100*4882a593Smuzhiyun 
2101*4882a593Smuzhiyun 
2102*4882a593Smuzhiyun 	/*
2103*4882a593Smuzhiyun 	 * Initialise the Copper Initialisation List
2104*4882a593Smuzhiyun 	 */
2105*4882a593Smuzhiyun 
ami_init_copper(void)2106*4882a593Smuzhiyun static void __init ami_init_copper(void)
2107*4882a593Smuzhiyun {
2108*4882a593Smuzhiyun 	copins *cop = copdisplay.init;
2109*4882a593Smuzhiyun 	u_long p;
2110*4882a593Smuzhiyun 	int i;
2111*4882a593Smuzhiyun 
2112*4882a593Smuzhiyun 	if (!IS_OCS) {
2113*4882a593Smuzhiyun 		(cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
2114*4882a593Smuzhiyun 		(cop++)->l = CMOVE(0x0181, diwstrt);
2115*4882a593Smuzhiyun 		(cop++)->l = CMOVE(0x0281, diwstop);
2116*4882a593Smuzhiyun 		(cop++)->l = CMOVE(0x0000, diwhigh);
2117*4882a593Smuzhiyun 	} else
2118*4882a593Smuzhiyun 		(cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
2119*4882a593Smuzhiyun 	p = ZTWO_PADDR(dummysprite);
2120*4882a593Smuzhiyun 	for (i = 0; i < 8; i++) {
2121*4882a593Smuzhiyun 		(cop++)->l = CMOVE(0, spr[i].pos);
2122*4882a593Smuzhiyun 		(cop++)->l = CMOVE(highw(p), sprpt[i]);
2123*4882a593Smuzhiyun 		(cop++)->l = CMOVE2(loww(p), sprpt[i]);
2124*4882a593Smuzhiyun 	}
2125*4882a593Smuzhiyun 
2126*4882a593Smuzhiyun 	(cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
2127*4882a593Smuzhiyun 	copdisplay.wait = cop;
2128*4882a593Smuzhiyun 	(cop++)->l = CEND;
2129*4882a593Smuzhiyun 	(cop++)->l = CMOVE(0, copjmp2);
2130*4882a593Smuzhiyun 	cop->l = CEND;
2131*4882a593Smuzhiyun 
2132*4882a593Smuzhiyun 	custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
2133*4882a593Smuzhiyun 	custom.copjmp1 = 0;
2134*4882a593Smuzhiyun }
2135*4882a593Smuzhiyun 
ami_reinit_copper(const struct amifb_par * par)2136*4882a593Smuzhiyun static void ami_reinit_copper(const struct amifb_par *par)
2137*4882a593Smuzhiyun {
2138*4882a593Smuzhiyun 	copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
2139*4882a593Smuzhiyun 	copdisplay.wait->l = CWAIT(32, par->diwstrt_v - 4);
2140*4882a593Smuzhiyun }
2141*4882a593Smuzhiyun 
2142*4882a593Smuzhiyun 
2143*4882a593Smuzhiyun 	/*
2144*4882a593Smuzhiyun 	 * Rebuild the Copper List
2145*4882a593Smuzhiyun 	 *
2146*4882a593Smuzhiyun 	 * We only change the things that are not static
2147*4882a593Smuzhiyun 	 */
2148*4882a593Smuzhiyun 
ami_rebuild_copper(const struct amifb_par * par)2149*4882a593Smuzhiyun static void ami_rebuild_copper(const struct amifb_par *par)
2150*4882a593Smuzhiyun {
2151*4882a593Smuzhiyun 	copins *copl, *cops;
2152*4882a593Smuzhiyun 	u_short line, h_end1, h_end2;
2153*4882a593Smuzhiyun 	short i;
2154*4882a593Smuzhiyun 	u_long p;
2155*4882a593Smuzhiyun 
2156*4882a593Smuzhiyun 	if (IS_AGA && maxfmode + par->clk_shift == 0)
2157*4882a593Smuzhiyun 		h_end1 = par->diwstrt_h - 64;
2158*4882a593Smuzhiyun 	else
2159*4882a593Smuzhiyun 		h_end1 = par->htotal - 32;
2160*4882a593Smuzhiyun 	h_end2 = par->ddfstop + 64;
2161*4882a593Smuzhiyun 
2162*4882a593Smuzhiyun 	ami_set_sprite(par);
2163*4882a593Smuzhiyun 
2164*4882a593Smuzhiyun 	copl = copdisplay.rebuild[1];
2165*4882a593Smuzhiyun 	p = par->bplpt0;
2166*4882a593Smuzhiyun 	if (par->vmode & FB_VMODE_YWRAP) {
2167*4882a593Smuzhiyun 		if ((par->vyres - par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
2168*4882a593Smuzhiyun 			if (par->yoffset > par->vyres - par->yres) {
2169*4882a593Smuzhiyun 				for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2170*4882a593Smuzhiyun 					(copl++)->l = CMOVE(highw(p), bplpt[i]);
2171*4882a593Smuzhiyun 					(copl++)->l = CMOVE2(loww(p), bplpt[i]);
2172*4882a593Smuzhiyun 				}
2173*4882a593Smuzhiyun 				line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 1;
2174*4882a593Smuzhiyun 				while (line >= 512) {
2175*4882a593Smuzhiyun 					(copl++)->l = CWAIT(h_end1, 510);
2176*4882a593Smuzhiyun 					line -= 512;
2177*4882a593Smuzhiyun 				}
2178*4882a593Smuzhiyun 				if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2179*4882a593Smuzhiyun 					(copl++)->l = CWAIT(h_end1, line);
2180*4882a593Smuzhiyun 				else
2181*4882a593Smuzhiyun 					(copl++)->l = CWAIT(h_end2, line);
2182*4882a593Smuzhiyun 				p = par->bplpt0wrap;
2183*4882a593Smuzhiyun 			}
2184*4882a593Smuzhiyun 		} else
2185*4882a593Smuzhiyun 			p = par->bplpt0wrap;
2186*4882a593Smuzhiyun 	}
2187*4882a593Smuzhiyun 	for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2188*4882a593Smuzhiyun 		(copl++)->l = CMOVE(highw(p), bplpt[i]);
2189*4882a593Smuzhiyun 		(copl++)->l = CMOVE2(loww(p), bplpt[i]);
2190*4882a593Smuzhiyun 	}
2191*4882a593Smuzhiyun 	copl->l = CEND;
2192*4882a593Smuzhiyun 
2193*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE) {
2194*4882a593Smuzhiyun 		cops = copdisplay.rebuild[0];
2195*4882a593Smuzhiyun 		p = par->bplpt0;
2196*4882a593Smuzhiyun 		if (mod2(par->diwstrt_v))
2197*4882a593Smuzhiyun 			p -= par->next_line;
2198*4882a593Smuzhiyun 		else
2199*4882a593Smuzhiyun 			p += par->next_line;
2200*4882a593Smuzhiyun 		if (par->vmode & FB_VMODE_YWRAP) {
2201*4882a593Smuzhiyun 			if ((par->vyres - par->yoffset) != 1 || mod2(par->diwstrt_v)) {
2202*4882a593Smuzhiyun 				if (par->yoffset > par->vyres - par->yres + 1) {
2203*4882a593Smuzhiyun 					for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2204*4882a593Smuzhiyun 						(cops++)->l = CMOVE(highw(p), bplpt[i]);
2205*4882a593Smuzhiyun 						(cops++)->l = CMOVE2(loww(p), bplpt[i]);
2206*4882a593Smuzhiyun 					}
2207*4882a593Smuzhiyun 					line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 2;
2208*4882a593Smuzhiyun 					while (line >= 512) {
2209*4882a593Smuzhiyun 						(cops++)->l = CWAIT(h_end1, 510);
2210*4882a593Smuzhiyun 						line -= 512;
2211*4882a593Smuzhiyun 					}
2212*4882a593Smuzhiyun 					if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2213*4882a593Smuzhiyun 						(cops++)->l = CWAIT(h_end1, line);
2214*4882a593Smuzhiyun 					else
2215*4882a593Smuzhiyun 						(cops++)->l = CWAIT(h_end2, line);
2216*4882a593Smuzhiyun 					p = par->bplpt0wrap;
2217*4882a593Smuzhiyun 					if (mod2(par->diwstrt_v + par->vyres -
2218*4882a593Smuzhiyun 					    par->yoffset))
2219*4882a593Smuzhiyun 						p -= par->next_line;
2220*4882a593Smuzhiyun 					else
2221*4882a593Smuzhiyun 						p += par->next_line;
2222*4882a593Smuzhiyun 				}
2223*4882a593Smuzhiyun 			} else
2224*4882a593Smuzhiyun 				p = par->bplpt0wrap - par->next_line;
2225*4882a593Smuzhiyun 		}
2226*4882a593Smuzhiyun 		for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2227*4882a593Smuzhiyun 			(cops++)->l = CMOVE(highw(p), bplpt[i]);
2228*4882a593Smuzhiyun 			(cops++)->l = CMOVE2(loww(p), bplpt[i]);
2229*4882a593Smuzhiyun 		}
2230*4882a593Smuzhiyun 		cops->l = CEND;
2231*4882a593Smuzhiyun 	}
2232*4882a593Smuzhiyun }
2233*4882a593Smuzhiyun 
2234*4882a593Smuzhiyun 
2235*4882a593Smuzhiyun 	/*
2236*4882a593Smuzhiyun 	 * Build the Copper List
2237*4882a593Smuzhiyun 	 */
2238*4882a593Smuzhiyun 
ami_build_copper(struct fb_info * info)2239*4882a593Smuzhiyun static void ami_build_copper(struct fb_info *info)
2240*4882a593Smuzhiyun {
2241*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
2242*4882a593Smuzhiyun 	copins *copl, *cops;
2243*4882a593Smuzhiyun 	u_long p;
2244*4882a593Smuzhiyun 
2245*4882a593Smuzhiyun 	currentcop = 1 - currentcop;
2246*4882a593Smuzhiyun 
2247*4882a593Smuzhiyun 	copl = copdisplay.list[currentcop][1];
2248*4882a593Smuzhiyun 
2249*4882a593Smuzhiyun 	(copl++)->l = CWAIT(0, 10);
2250*4882a593Smuzhiyun 	(copl++)->l = CMOVE(par->bplcon0, bplcon0);
2251*4882a593Smuzhiyun 	(copl++)->l = CMOVE(0, sprpt[0]);
2252*4882a593Smuzhiyun 	(copl++)->l = CMOVE2(0, sprpt[0]);
2253*4882a593Smuzhiyun 
2254*4882a593Smuzhiyun 	if (par->bplcon0 & BPC0_LACE) {
2255*4882a593Smuzhiyun 		cops = copdisplay.list[currentcop][0];
2256*4882a593Smuzhiyun 
2257*4882a593Smuzhiyun 		(cops++)->l = CWAIT(0, 10);
2258*4882a593Smuzhiyun 		(cops++)->l = CMOVE(par->bplcon0, bplcon0);
2259*4882a593Smuzhiyun 		(cops++)->l = CMOVE(0, sprpt[0]);
2260*4882a593Smuzhiyun 		(cops++)->l = CMOVE2(0, sprpt[0]);
2261*4882a593Smuzhiyun 
2262*4882a593Smuzhiyun 		(copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v + 1), diwstrt);
2263*4882a593Smuzhiyun 		(copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v + 1), diwstop);
2264*4882a593Smuzhiyun 		(cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2265*4882a593Smuzhiyun 		(cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2266*4882a593Smuzhiyun 		if (!IS_OCS) {
2267*4882a593Smuzhiyun 			(copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v + 1,
2268*4882a593Smuzhiyun 					    par->diwstop_h, par->diwstop_v + 1), diwhigh);
2269*4882a593Smuzhiyun 			(cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2270*4882a593Smuzhiyun 					    par->diwstop_h, par->diwstop_v), diwhigh);
2271*4882a593Smuzhiyun #if 0
2272*4882a593Smuzhiyun 			if (par->beamcon0 & BMC0_VARBEAMEN) {
2273*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2274*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vbstrt2hw(par->vbstrt + 1), vbstrt);
2275*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vbstop2hw(par->vbstop + 1), vbstop);
2276*4882a593Smuzhiyun 				(cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2277*4882a593Smuzhiyun 				(cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2278*4882a593Smuzhiyun 				(cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2279*4882a593Smuzhiyun 			}
2280*4882a593Smuzhiyun #endif
2281*4882a593Smuzhiyun 		}
2282*4882a593Smuzhiyun 		p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
2283*4882a593Smuzhiyun 		(copl++)->l = CMOVE(highw(p), cop2lc);
2284*4882a593Smuzhiyun 		(copl++)->l = CMOVE2(loww(p), cop2lc);
2285*4882a593Smuzhiyun 		p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
2286*4882a593Smuzhiyun 		(cops++)->l = CMOVE(highw(p), cop2lc);
2287*4882a593Smuzhiyun 		(cops++)->l = CMOVE2(loww(p), cop2lc);
2288*4882a593Smuzhiyun 		copdisplay.rebuild[0] = cops;
2289*4882a593Smuzhiyun 	} else {
2290*4882a593Smuzhiyun 		(copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2291*4882a593Smuzhiyun 		(copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2292*4882a593Smuzhiyun 		if (!IS_OCS) {
2293*4882a593Smuzhiyun 			(copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2294*4882a593Smuzhiyun 					    par->diwstop_h, par->diwstop_v), diwhigh);
2295*4882a593Smuzhiyun #if 0
2296*4882a593Smuzhiyun 			if (par->beamcon0 & BMC0_VARBEAMEN) {
2297*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2298*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2299*4882a593Smuzhiyun 				(copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2300*4882a593Smuzhiyun 			}
2301*4882a593Smuzhiyun #endif
2302*4882a593Smuzhiyun 		}
2303*4882a593Smuzhiyun 	}
2304*4882a593Smuzhiyun 	copdisplay.rebuild[1] = copl;
2305*4882a593Smuzhiyun 
2306*4882a593Smuzhiyun 	ami_update_par(info);
2307*4882a593Smuzhiyun 	ami_rebuild_copper(info->par);
2308*4882a593Smuzhiyun }
2309*4882a593Smuzhiyun 
2310*4882a593Smuzhiyun #ifndef MODULE
amifb_setup_mcap(char * spec)2311*4882a593Smuzhiyun static void __init amifb_setup_mcap(char *spec)
2312*4882a593Smuzhiyun {
2313*4882a593Smuzhiyun 	char *p;
2314*4882a593Smuzhiyun 	int vmin, vmax, hmin, hmax;
2315*4882a593Smuzhiyun 
2316*4882a593Smuzhiyun 	/* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
2317*4882a593Smuzhiyun 	 * <V*> vertical freq. in Hz
2318*4882a593Smuzhiyun 	 * <H*> horizontal freq. in kHz
2319*4882a593Smuzhiyun 	 */
2320*4882a593Smuzhiyun 
2321*4882a593Smuzhiyun 	if (!(p = strsep(&spec, ";")) || !*p)
2322*4882a593Smuzhiyun 		return;
2323*4882a593Smuzhiyun 	vmin = simple_strtoul(p, NULL, 10);
2324*4882a593Smuzhiyun 	if (vmin <= 0)
2325*4882a593Smuzhiyun 		return;
2326*4882a593Smuzhiyun 	if (!(p = strsep(&spec, ";")) || !*p)
2327*4882a593Smuzhiyun 		return;
2328*4882a593Smuzhiyun 	vmax = simple_strtoul(p, NULL, 10);
2329*4882a593Smuzhiyun 	if (vmax <= 0 || vmax <= vmin)
2330*4882a593Smuzhiyun 		return;
2331*4882a593Smuzhiyun 	if (!(p = strsep(&spec, ";")) || !*p)
2332*4882a593Smuzhiyun 		return;
2333*4882a593Smuzhiyun 	hmin = 1000 * simple_strtoul(p, NULL, 10);
2334*4882a593Smuzhiyun 	if (hmin <= 0)
2335*4882a593Smuzhiyun 		return;
2336*4882a593Smuzhiyun 	if (!(p = strsep(&spec, "")) || !*p)
2337*4882a593Smuzhiyun 		return;
2338*4882a593Smuzhiyun 	hmax = 1000 * simple_strtoul(p, NULL, 10);
2339*4882a593Smuzhiyun 	if (hmax <= 0 || hmax <= hmin)
2340*4882a593Smuzhiyun 		return;
2341*4882a593Smuzhiyun 
2342*4882a593Smuzhiyun 	amifb_hfmin = hmin;
2343*4882a593Smuzhiyun 	amifb_hfmax = hmax;
2344*4882a593Smuzhiyun 	amifb_vfmin = vmin;
2345*4882a593Smuzhiyun 	amifb_vfmax = vmax;
2346*4882a593Smuzhiyun }
2347*4882a593Smuzhiyun 
amifb_setup(char * options)2348*4882a593Smuzhiyun static int __init amifb_setup(char *options)
2349*4882a593Smuzhiyun {
2350*4882a593Smuzhiyun 	char *this_opt;
2351*4882a593Smuzhiyun 
2352*4882a593Smuzhiyun 	if (!options || !*options)
2353*4882a593Smuzhiyun 		return 0;
2354*4882a593Smuzhiyun 
2355*4882a593Smuzhiyun 	while ((this_opt = strsep(&options, ",")) != NULL) {
2356*4882a593Smuzhiyun 		if (!*this_opt)
2357*4882a593Smuzhiyun 			continue;
2358*4882a593Smuzhiyun 		if (!strcmp(this_opt, "inverse")) {
2359*4882a593Smuzhiyun 			fb_invert_cmaps();
2360*4882a593Smuzhiyun 		} else if (!strcmp(this_opt, "ilbm"))
2361*4882a593Smuzhiyun 			amifb_ilbm = 1;
2362*4882a593Smuzhiyun 		else if (!strncmp(this_opt, "monitorcap:", 11))
2363*4882a593Smuzhiyun 			amifb_setup_mcap(this_opt + 11);
2364*4882a593Smuzhiyun 		else if (!strncmp(this_opt, "fstart:", 7))
2365*4882a593Smuzhiyun 			min_fstrt = simple_strtoul(this_opt + 7, NULL, 0);
2366*4882a593Smuzhiyun 		else
2367*4882a593Smuzhiyun 			mode_option = this_opt;
2368*4882a593Smuzhiyun 	}
2369*4882a593Smuzhiyun 
2370*4882a593Smuzhiyun 	if (min_fstrt < 48)
2371*4882a593Smuzhiyun 		min_fstrt = 48;
2372*4882a593Smuzhiyun 
2373*4882a593Smuzhiyun 	return 0;
2374*4882a593Smuzhiyun }
2375*4882a593Smuzhiyun #endif
2376*4882a593Smuzhiyun 
amifb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)2377*4882a593Smuzhiyun static int amifb_check_var(struct fb_var_screeninfo *var,
2378*4882a593Smuzhiyun 			   struct fb_info *info)
2379*4882a593Smuzhiyun {
2380*4882a593Smuzhiyun 	int err;
2381*4882a593Smuzhiyun 	struct amifb_par par;
2382*4882a593Smuzhiyun 
2383*4882a593Smuzhiyun 	/* Validate wanted screen parameters */
2384*4882a593Smuzhiyun 	err = ami_decode_var(var, &par, info);
2385*4882a593Smuzhiyun 	if (err)
2386*4882a593Smuzhiyun 		return err;
2387*4882a593Smuzhiyun 
2388*4882a593Smuzhiyun 	/* Encode (possibly rounded) screen parameters */
2389*4882a593Smuzhiyun 	ami_encode_var(var, &par);
2390*4882a593Smuzhiyun 	return 0;
2391*4882a593Smuzhiyun }
2392*4882a593Smuzhiyun 
2393*4882a593Smuzhiyun 
amifb_set_par(struct fb_info * info)2394*4882a593Smuzhiyun static int amifb_set_par(struct fb_info *info)
2395*4882a593Smuzhiyun {
2396*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
2397*4882a593Smuzhiyun 	int error;
2398*4882a593Smuzhiyun 
2399*4882a593Smuzhiyun 	do_vmode_pan = 0;
2400*4882a593Smuzhiyun 	do_vmode_full = 0;
2401*4882a593Smuzhiyun 
2402*4882a593Smuzhiyun 	/* Decode wanted screen parameters */
2403*4882a593Smuzhiyun 	error = ami_decode_var(&info->var, par, info);
2404*4882a593Smuzhiyun 	if (error)
2405*4882a593Smuzhiyun 		return error;
2406*4882a593Smuzhiyun 
2407*4882a593Smuzhiyun 	/* Set new videomode */
2408*4882a593Smuzhiyun 	ami_build_copper(info);
2409*4882a593Smuzhiyun 
2410*4882a593Smuzhiyun 	/* Set VBlank trigger */
2411*4882a593Smuzhiyun 	do_vmode_full = 1;
2412*4882a593Smuzhiyun 
2413*4882a593Smuzhiyun 	/* Update fix for new screen parameters */
2414*4882a593Smuzhiyun 	if (par->bpp == 1) {
2415*4882a593Smuzhiyun 		info->fix.type = FB_TYPE_PACKED_PIXELS;
2416*4882a593Smuzhiyun 		info->fix.type_aux = 0;
2417*4882a593Smuzhiyun 	} else if (amifb_ilbm) {
2418*4882a593Smuzhiyun 		info->fix.type = FB_TYPE_INTERLEAVED_PLANES;
2419*4882a593Smuzhiyun 		info->fix.type_aux = par->next_line;
2420*4882a593Smuzhiyun 	} else {
2421*4882a593Smuzhiyun 		info->fix.type = FB_TYPE_PLANES;
2422*4882a593Smuzhiyun 		info->fix.type_aux = 0;
2423*4882a593Smuzhiyun 	}
2424*4882a593Smuzhiyun 	info->fix.line_length = div8(upx(16 << maxfmode, par->vxres));
2425*4882a593Smuzhiyun 
2426*4882a593Smuzhiyun 	if (par->vmode & FB_VMODE_YWRAP) {
2427*4882a593Smuzhiyun 		info->fix.ywrapstep = 1;
2428*4882a593Smuzhiyun 		info->fix.xpanstep = 0;
2429*4882a593Smuzhiyun 		info->fix.ypanstep = 0;
2430*4882a593Smuzhiyun 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YWRAP |
2431*4882a593Smuzhiyun 			FBINFO_READS_FAST; /* override SCROLL_REDRAW */
2432*4882a593Smuzhiyun 	} else {
2433*4882a593Smuzhiyun 		info->fix.ywrapstep = 0;
2434*4882a593Smuzhiyun 		if (par->vmode & FB_VMODE_SMOOTH_XPAN)
2435*4882a593Smuzhiyun 			info->fix.xpanstep = 1;
2436*4882a593Smuzhiyun 		else
2437*4882a593Smuzhiyun 			info->fix.xpanstep = 16 << maxfmode;
2438*4882a593Smuzhiyun 		info->fix.ypanstep = 1;
2439*4882a593Smuzhiyun 		info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
2440*4882a593Smuzhiyun 	}
2441*4882a593Smuzhiyun 	return 0;
2442*4882a593Smuzhiyun }
2443*4882a593Smuzhiyun 
2444*4882a593Smuzhiyun 
2445*4882a593Smuzhiyun 	/*
2446*4882a593Smuzhiyun 	 * Set a single color register. The values supplied are already
2447*4882a593Smuzhiyun 	 * rounded down to the hardware's capabilities (according to the
2448*4882a593Smuzhiyun 	 * entries in the var structure). Return != 0 for invalid regno.
2449*4882a593Smuzhiyun 	 */
2450*4882a593Smuzhiyun 
amifb_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * info)2451*4882a593Smuzhiyun static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2452*4882a593Smuzhiyun 			   u_int transp, struct fb_info *info)
2453*4882a593Smuzhiyun {
2454*4882a593Smuzhiyun 	const struct amifb_par *par = info->par;
2455*4882a593Smuzhiyun 
2456*4882a593Smuzhiyun 	if (IS_AGA) {
2457*4882a593Smuzhiyun 		if (regno > 255)
2458*4882a593Smuzhiyun 			return 1;
2459*4882a593Smuzhiyun 	} else if (par->bplcon0 & BPC0_SHRES) {
2460*4882a593Smuzhiyun 		if (regno > 3)
2461*4882a593Smuzhiyun 			return 1;
2462*4882a593Smuzhiyun 	} else {
2463*4882a593Smuzhiyun 		if (regno > 31)
2464*4882a593Smuzhiyun 			return 1;
2465*4882a593Smuzhiyun 	}
2466*4882a593Smuzhiyun 	red >>= 8;
2467*4882a593Smuzhiyun 	green >>= 8;
2468*4882a593Smuzhiyun 	blue >>= 8;
2469*4882a593Smuzhiyun 	if (!regno) {
2470*4882a593Smuzhiyun 		red0 = red;
2471*4882a593Smuzhiyun 		green0 = green;
2472*4882a593Smuzhiyun 		blue0 = blue;
2473*4882a593Smuzhiyun 	}
2474*4882a593Smuzhiyun 
2475*4882a593Smuzhiyun 	/*
2476*4882a593Smuzhiyun 	 * Update the corresponding Hardware Color Register, unless it's Color
2477*4882a593Smuzhiyun 	 * Register 0 and the screen is blanked.
2478*4882a593Smuzhiyun 	 *
2479*4882a593Smuzhiyun 	 * VBlank is switched off to protect bplcon3 or ecs_palette[] from
2480*4882a593Smuzhiyun 	 * being changed by ami_do_blank() during the VBlank.
2481*4882a593Smuzhiyun 	 */
2482*4882a593Smuzhiyun 
2483*4882a593Smuzhiyun 	if (regno || !is_blanked) {
2484*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_AGA)
2485*4882a593Smuzhiyun 		if (IS_AGA) {
2486*4882a593Smuzhiyun 			u_short bplcon3 = par->bplcon3;
2487*4882a593Smuzhiyun 			VBlankOff();
2488*4882a593Smuzhiyun 			custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000);
2489*4882a593Smuzhiyun 			custom.color[regno & 31] = rgb2hw8_high(red, green,
2490*4882a593Smuzhiyun 								blue);
2491*4882a593Smuzhiyun 			custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000) |
2492*4882a593Smuzhiyun 					 BPC3_LOCT;
2493*4882a593Smuzhiyun 			custom.color[regno & 31] = rgb2hw8_low(red, green,
2494*4882a593Smuzhiyun 							       blue);
2495*4882a593Smuzhiyun 			custom.bplcon3 = bplcon3;
2496*4882a593Smuzhiyun 			VBlankOn();
2497*4882a593Smuzhiyun 		} else
2498*4882a593Smuzhiyun #endif
2499*4882a593Smuzhiyun #if defined(CONFIG_FB_AMIGA_ECS)
2500*4882a593Smuzhiyun 		if (par->bplcon0 & BPC0_SHRES) {
2501*4882a593Smuzhiyun 			u_short color, mask;
2502*4882a593Smuzhiyun 			int i;
2503*4882a593Smuzhiyun 
2504*4882a593Smuzhiyun 			mask = 0x3333;
2505*4882a593Smuzhiyun 			color = rgb2hw2(red, green, blue);
2506*4882a593Smuzhiyun 			VBlankOff();
2507*4882a593Smuzhiyun 			for (i = regno + 12; i >= (int)regno; i -= 4)
2508*4882a593Smuzhiyun 				custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2509*4882a593Smuzhiyun 			mask <<= 2; color >>= 2;
2510*4882a593Smuzhiyun 			regno = down16(regno) + mul4(mod4(regno));
2511*4882a593Smuzhiyun 			for (i = regno + 3; i >= (int)regno; i--)
2512*4882a593Smuzhiyun 				custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2513*4882a593Smuzhiyun 			VBlankOn();
2514*4882a593Smuzhiyun 		} else
2515*4882a593Smuzhiyun #endif
2516*4882a593Smuzhiyun 			custom.color[regno] = rgb2hw4(red, green, blue);
2517*4882a593Smuzhiyun 	}
2518*4882a593Smuzhiyun 	return 0;
2519*4882a593Smuzhiyun }
2520*4882a593Smuzhiyun 
2521*4882a593Smuzhiyun 
2522*4882a593Smuzhiyun 	/*
2523*4882a593Smuzhiyun 	 * Blank the display.
2524*4882a593Smuzhiyun 	 */
2525*4882a593Smuzhiyun 
amifb_blank(int blank,struct fb_info * info)2526*4882a593Smuzhiyun static int amifb_blank(int blank, struct fb_info *info)
2527*4882a593Smuzhiyun {
2528*4882a593Smuzhiyun 	do_blank = blank ? blank : -1;
2529*4882a593Smuzhiyun 
2530*4882a593Smuzhiyun 	return 0;
2531*4882a593Smuzhiyun }
2532*4882a593Smuzhiyun 
2533*4882a593Smuzhiyun 
2534*4882a593Smuzhiyun 	/*
2535*4882a593Smuzhiyun 	 * Pan or Wrap the Display
2536*4882a593Smuzhiyun 	 *
2537*4882a593Smuzhiyun 	 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
2538*4882a593Smuzhiyun 	 */
2539*4882a593Smuzhiyun 
amifb_pan_display(struct fb_var_screeninfo * var,struct fb_info * info)2540*4882a593Smuzhiyun static int amifb_pan_display(struct fb_var_screeninfo *var,
2541*4882a593Smuzhiyun 			     struct fb_info *info)
2542*4882a593Smuzhiyun {
2543*4882a593Smuzhiyun 	if (var->vmode & FB_VMODE_YWRAP) {
2544*4882a593Smuzhiyun 		if (var->yoffset < 0 ||
2545*4882a593Smuzhiyun 			var->yoffset >= info->var.yres_virtual || var->xoffset)
2546*4882a593Smuzhiyun 				return -EINVAL;
2547*4882a593Smuzhiyun 	} else {
2548*4882a593Smuzhiyun 		/*
2549*4882a593Smuzhiyun 		 * TODO: There will be problems when xpan!=1, so some columns
2550*4882a593Smuzhiyun 		 * on the right side will never be seen
2551*4882a593Smuzhiyun 		 */
2552*4882a593Smuzhiyun 		if (var->xoffset + info->var.xres >
2553*4882a593Smuzhiyun 		    upx(16 << maxfmode, info->var.xres_virtual) ||
2554*4882a593Smuzhiyun 		    var->yoffset + info->var.yres > info->var.yres_virtual)
2555*4882a593Smuzhiyun 			return -EINVAL;
2556*4882a593Smuzhiyun 	}
2557*4882a593Smuzhiyun 	ami_pan_var(var, info);
2558*4882a593Smuzhiyun 	info->var.xoffset = var->xoffset;
2559*4882a593Smuzhiyun 	info->var.yoffset = var->yoffset;
2560*4882a593Smuzhiyun 	if (var->vmode & FB_VMODE_YWRAP)
2561*4882a593Smuzhiyun 		info->var.vmode |= FB_VMODE_YWRAP;
2562*4882a593Smuzhiyun 	else
2563*4882a593Smuzhiyun 		info->var.vmode &= ~FB_VMODE_YWRAP;
2564*4882a593Smuzhiyun 	return 0;
2565*4882a593Smuzhiyun }
2566*4882a593Smuzhiyun 
2567*4882a593Smuzhiyun 
2568*4882a593Smuzhiyun #if BITS_PER_LONG == 32
2569*4882a593Smuzhiyun #define BYTES_PER_LONG	4
2570*4882a593Smuzhiyun #define SHIFT_PER_LONG	5
2571*4882a593Smuzhiyun #elif BITS_PER_LONG == 64
2572*4882a593Smuzhiyun #define BYTES_PER_LONG	8
2573*4882a593Smuzhiyun #define SHIFT_PER_LONG	6
2574*4882a593Smuzhiyun #else
2575*4882a593Smuzhiyun #define Please update me
2576*4882a593Smuzhiyun #endif
2577*4882a593Smuzhiyun 
2578*4882a593Smuzhiyun 
2579*4882a593Smuzhiyun 	/*
2580*4882a593Smuzhiyun 	 *  Compose two values, using a bitmask as decision value
2581*4882a593Smuzhiyun 	 *  This is equivalent to (a & mask) | (b & ~mask)
2582*4882a593Smuzhiyun 	 */
2583*4882a593Smuzhiyun 
comp(unsigned long a,unsigned long b,unsigned long mask)2584*4882a593Smuzhiyun static inline unsigned long comp(unsigned long a, unsigned long b,
2585*4882a593Smuzhiyun 				 unsigned long mask)
2586*4882a593Smuzhiyun {
2587*4882a593Smuzhiyun 	return ((a ^ b) & mask) ^ b;
2588*4882a593Smuzhiyun }
2589*4882a593Smuzhiyun 
2590*4882a593Smuzhiyun 
xor(unsigned long a,unsigned long b,unsigned long mask)2591*4882a593Smuzhiyun static inline unsigned long xor(unsigned long a, unsigned long b,
2592*4882a593Smuzhiyun 				unsigned long mask)
2593*4882a593Smuzhiyun {
2594*4882a593Smuzhiyun 	return (a & mask) ^ b;
2595*4882a593Smuzhiyun }
2596*4882a593Smuzhiyun 
2597*4882a593Smuzhiyun 
2598*4882a593Smuzhiyun 	/*
2599*4882a593Smuzhiyun 	 *  Unaligned forward bit copy using 32-bit or 64-bit memory accesses
2600*4882a593Smuzhiyun 	 */
2601*4882a593Smuzhiyun 
bitcpy(unsigned long * dst,int dst_idx,const unsigned long * src,int src_idx,u32 n)2602*4882a593Smuzhiyun static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
2603*4882a593Smuzhiyun 		   int src_idx, u32 n)
2604*4882a593Smuzhiyun {
2605*4882a593Smuzhiyun 	unsigned long first, last;
2606*4882a593Smuzhiyun 	int shift = dst_idx - src_idx, left, right;
2607*4882a593Smuzhiyun 	unsigned long d0, d1;
2608*4882a593Smuzhiyun 	int m;
2609*4882a593Smuzhiyun 
2610*4882a593Smuzhiyun 	if (!n)
2611*4882a593Smuzhiyun 		return;
2612*4882a593Smuzhiyun 
2613*4882a593Smuzhiyun 	shift = dst_idx - src_idx;
2614*4882a593Smuzhiyun 	first = ~0UL >> dst_idx;
2615*4882a593Smuzhiyun 	last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
2616*4882a593Smuzhiyun 
2617*4882a593Smuzhiyun 	if (!shift) {
2618*4882a593Smuzhiyun 		// Same alignment for source and dest
2619*4882a593Smuzhiyun 
2620*4882a593Smuzhiyun 		if (dst_idx + n <= BITS_PER_LONG) {
2621*4882a593Smuzhiyun 			// Single word
2622*4882a593Smuzhiyun 			if (last)
2623*4882a593Smuzhiyun 				first &= last;
2624*4882a593Smuzhiyun 			*dst = comp(*src, *dst, first);
2625*4882a593Smuzhiyun 		} else {
2626*4882a593Smuzhiyun 			// Multiple destination words
2627*4882a593Smuzhiyun 			// Leading bits
2628*4882a593Smuzhiyun 			if (first) {
2629*4882a593Smuzhiyun 				*dst = comp(*src, *dst, first);
2630*4882a593Smuzhiyun 				dst++;
2631*4882a593Smuzhiyun 				src++;
2632*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2633*4882a593Smuzhiyun 			}
2634*4882a593Smuzhiyun 
2635*4882a593Smuzhiyun 			// Main chunk
2636*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2637*4882a593Smuzhiyun 			while (n >= 8) {
2638*4882a593Smuzhiyun 				*dst++ = *src++;
2639*4882a593Smuzhiyun 				*dst++ = *src++;
2640*4882a593Smuzhiyun 				*dst++ = *src++;
2641*4882a593Smuzhiyun 				*dst++ = *src++;
2642*4882a593Smuzhiyun 				*dst++ = *src++;
2643*4882a593Smuzhiyun 				*dst++ = *src++;
2644*4882a593Smuzhiyun 				*dst++ = *src++;
2645*4882a593Smuzhiyun 				*dst++ = *src++;
2646*4882a593Smuzhiyun 				n -= 8;
2647*4882a593Smuzhiyun 			}
2648*4882a593Smuzhiyun 			while (n--)
2649*4882a593Smuzhiyun 				*dst++ = *src++;
2650*4882a593Smuzhiyun 
2651*4882a593Smuzhiyun 			// Trailing bits
2652*4882a593Smuzhiyun 			if (last)
2653*4882a593Smuzhiyun 				*dst = comp(*src, *dst, last);
2654*4882a593Smuzhiyun 		}
2655*4882a593Smuzhiyun 	} else {
2656*4882a593Smuzhiyun 		// Different alignment for source and dest
2657*4882a593Smuzhiyun 
2658*4882a593Smuzhiyun 		right = shift & (BITS_PER_LONG - 1);
2659*4882a593Smuzhiyun 		left = -shift & (BITS_PER_LONG - 1);
2660*4882a593Smuzhiyun 
2661*4882a593Smuzhiyun 		if (dst_idx + n <= BITS_PER_LONG) {
2662*4882a593Smuzhiyun 			// Single destination word
2663*4882a593Smuzhiyun 			if (last)
2664*4882a593Smuzhiyun 				first &= last;
2665*4882a593Smuzhiyun 			if (shift > 0) {
2666*4882a593Smuzhiyun 				// Single source word
2667*4882a593Smuzhiyun 				*dst = comp(*src >> right, *dst, first);
2668*4882a593Smuzhiyun 			} else if (src_idx + n <= BITS_PER_LONG) {
2669*4882a593Smuzhiyun 				// Single source word
2670*4882a593Smuzhiyun 				*dst = comp(*src << left, *dst, first);
2671*4882a593Smuzhiyun 			} else {
2672*4882a593Smuzhiyun 				// 2 source words
2673*4882a593Smuzhiyun 				d0 = *src++;
2674*4882a593Smuzhiyun 				d1 = *src;
2675*4882a593Smuzhiyun 				*dst = comp(d0 << left | d1 >> right, *dst,
2676*4882a593Smuzhiyun 					    first);
2677*4882a593Smuzhiyun 			}
2678*4882a593Smuzhiyun 		} else {
2679*4882a593Smuzhiyun 			// Multiple destination words
2680*4882a593Smuzhiyun 			d0 = *src++;
2681*4882a593Smuzhiyun 			// Leading bits
2682*4882a593Smuzhiyun 			if (shift > 0) {
2683*4882a593Smuzhiyun 				// Single source word
2684*4882a593Smuzhiyun 				*dst = comp(d0 >> right, *dst, first);
2685*4882a593Smuzhiyun 				dst++;
2686*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2687*4882a593Smuzhiyun 			} else {
2688*4882a593Smuzhiyun 				// 2 source words
2689*4882a593Smuzhiyun 				d1 = *src++;
2690*4882a593Smuzhiyun 				*dst = comp(d0 << left | d1 >> right, *dst,
2691*4882a593Smuzhiyun 					    first);
2692*4882a593Smuzhiyun 				d0 = d1;
2693*4882a593Smuzhiyun 				dst++;
2694*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2695*4882a593Smuzhiyun 			}
2696*4882a593Smuzhiyun 
2697*4882a593Smuzhiyun 			// Main chunk
2698*4882a593Smuzhiyun 			m = n % BITS_PER_LONG;
2699*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2700*4882a593Smuzhiyun 			while (n >= 4) {
2701*4882a593Smuzhiyun 				d1 = *src++;
2702*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2703*4882a593Smuzhiyun 				d0 = d1;
2704*4882a593Smuzhiyun 				d1 = *src++;
2705*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2706*4882a593Smuzhiyun 				d0 = d1;
2707*4882a593Smuzhiyun 				d1 = *src++;
2708*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2709*4882a593Smuzhiyun 				d0 = d1;
2710*4882a593Smuzhiyun 				d1 = *src++;
2711*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2712*4882a593Smuzhiyun 				d0 = d1;
2713*4882a593Smuzhiyun 				n -= 4;
2714*4882a593Smuzhiyun 			}
2715*4882a593Smuzhiyun 			while (n--) {
2716*4882a593Smuzhiyun 				d1 = *src++;
2717*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2718*4882a593Smuzhiyun 				d0 = d1;
2719*4882a593Smuzhiyun 			}
2720*4882a593Smuzhiyun 
2721*4882a593Smuzhiyun 			// Trailing bits
2722*4882a593Smuzhiyun 			if (last) {
2723*4882a593Smuzhiyun 				if (m <= right) {
2724*4882a593Smuzhiyun 					// Single source word
2725*4882a593Smuzhiyun 					*dst = comp(d0 << left, *dst, last);
2726*4882a593Smuzhiyun 				} else {
2727*4882a593Smuzhiyun 					// 2 source words
2728*4882a593Smuzhiyun 					d1 = *src;
2729*4882a593Smuzhiyun 					*dst = comp(d0 << left | d1 >> right,
2730*4882a593Smuzhiyun 						    *dst, last);
2731*4882a593Smuzhiyun 				}
2732*4882a593Smuzhiyun 			}
2733*4882a593Smuzhiyun 		}
2734*4882a593Smuzhiyun 	}
2735*4882a593Smuzhiyun }
2736*4882a593Smuzhiyun 
2737*4882a593Smuzhiyun 
2738*4882a593Smuzhiyun 	/*
2739*4882a593Smuzhiyun 	 *  Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
2740*4882a593Smuzhiyun 	 */
2741*4882a593Smuzhiyun 
bitcpy_rev(unsigned long * dst,int dst_idx,const unsigned long * src,int src_idx,u32 n)2742*4882a593Smuzhiyun static void bitcpy_rev(unsigned long *dst, int dst_idx,
2743*4882a593Smuzhiyun 		       const unsigned long *src, int src_idx, u32 n)
2744*4882a593Smuzhiyun {
2745*4882a593Smuzhiyun 	unsigned long first, last;
2746*4882a593Smuzhiyun 	int shift = dst_idx - src_idx, left, right;
2747*4882a593Smuzhiyun 	unsigned long d0, d1;
2748*4882a593Smuzhiyun 	int m;
2749*4882a593Smuzhiyun 
2750*4882a593Smuzhiyun 	if (!n)
2751*4882a593Smuzhiyun 		return;
2752*4882a593Smuzhiyun 
2753*4882a593Smuzhiyun 	dst += (n - 1) / BITS_PER_LONG;
2754*4882a593Smuzhiyun 	src += (n - 1) / BITS_PER_LONG;
2755*4882a593Smuzhiyun 	if ((n - 1) % BITS_PER_LONG) {
2756*4882a593Smuzhiyun 		dst_idx += (n - 1) % BITS_PER_LONG;
2757*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
2758*4882a593Smuzhiyun 		dst_idx &= BITS_PER_LONG - 1;
2759*4882a593Smuzhiyun 		src_idx += (n - 1) % BITS_PER_LONG;
2760*4882a593Smuzhiyun 		src += src_idx >> SHIFT_PER_LONG;
2761*4882a593Smuzhiyun 		src_idx &= BITS_PER_LONG - 1;
2762*4882a593Smuzhiyun 	}
2763*4882a593Smuzhiyun 
2764*4882a593Smuzhiyun 	shift = dst_idx - src_idx;
2765*4882a593Smuzhiyun 	first = ~0UL << (BITS_PER_LONG - 1 - dst_idx);
2766*4882a593Smuzhiyun 	last = ~(~0UL << (BITS_PER_LONG - 1 - ((dst_idx - n) % BITS_PER_LONG)));
2767*4882a593Smuzhiyun 
2768*4882a593Smuzhiyun 	if (!shift) {
2769*4882a593Smuzhiyun 		// Same alignment for source and dest
2770*4882a593Smuzhiyun 
2771*4882a593Smuzhiyun 		if ((unsigned long)dst_idx + 1 >= n) {
2772*4882a593Smuzhiyun 			// Single word
2773*4882a593Smuzhiyun 			if (last)
2774*4882a593Smuzhiyun 				first &= last;
2775*4882a593Smuzhiyun 			*dst = comp(*src, *dst, first);
2776*4882a593Smuzhiyun 		} else {
2777*4882a593Smuzhiyun 			// Multiple destination words
2778*4882a593Smuzhiyun 			// Leading bits
2779*4882a593Smuzhiyun 			if (first) {
2780*4882a593Smuzhiyun 				*dst = comp(*src, *dst, first);
2781*4882a593Smuzhiyun 				dst--;
2782*4882a593Smuzhiyun 				src--;
2783*4882a593Smuzhiyun 				n -= dst_idx + 1;
2784*4882a593Smuzhiyun 			}
2785*4882a593Smuzhiyun 
2786*4882a593Smuzhiyun 			// Main chunk
2787*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2788*4882a593Smuzhiyun 			while (n >= 8) {
2789*4882a593Smuzhiyun 				*dst-- = *src--;
2790*4882a593Smuzhiyun 				*dst-- = *src--;
2791*4882a593Smuzhiyun 				*dst-- = *src--;
2792*4882a593Smuzhiyun 				*dst-- = *src--;
2793*4882a593Smuzhiyun 				*dst-- = *src--;
2794*4882a593Smuzhiyun 				*dst-- = *src--;
2795*4882a593Smuzhiyun 				*dst-- = *src--;
2796*4882a593Smuzhiyun 				*dst-- = *src--;
2797*4882a593Smuzhiyun 				n -= 8;
2798*4882a593Smuzhiyun 			}
2799*4882a593Smuzhiyun 			while (n--)
2800*4882a593Smuzhiyun 				*dst-- = *src--;
2801*4882a593Smuzhiyun 
2802*4882a593Smuzhiyun 			// Trailing bits
2803*4882a593Smuzhiyun 			if (last)
2804*4882a593Smuzhiyun 				*dst = comp(*src, *dst, last);
2805*4882a593Smuzhiyun 		}
2806*4882a593Smuzhiyun 	} else {
2807*4882a593Smuzhiyun 		// Different alignment for source and dest
2808*4882a593Smuzhiyun 
2809*4882a593Smuzhiyun 		right = shift & (BITS_PER_LONG - 1);
2810*4882a593Smuzhiyun 		left = -shift & (BITS_PER_LONG - 1);
2811*4882a593Smuzhiyun 
2812*4882a593Smuzhiyun 		if ((unsigned long)dst_idx + 1 >= n) {
2813*4882a593Smuzhiyun 			// Single destination word
2814*4882a593Smuzhiyun 			if (last)
2815*4882a593Smuzhiyun 				first &= last;
2816*4882a593Smuzhiyun 			if (shift < 0) {
2817*4882a593Smuzhiyun 				// Single source word
2818*4882a593Smuzhiyun 				*dst = comp(*src << left, *dst, first);
2819*4882a593Smuzhiyun 			} else if (1 + (unsigned long)src_idx >= n) {
2820*4882a593Smuzhiyun 				// Single source word
2821*4882a593Smuzhiyun 				*dst = comp(*src >> right, *dst, first);
2822*4882a593Smuzhiyun 			} else {
2823*4882a593Smuzhiyun 				// 2 source words
2824*4882a593Smuzhiyun 				d0 = *src--;
2825*4882a593Smuzhiyun 				d1 = *src;
2826*4882a593Smuzhiyun 				*dst = comp(d0 >> right | d1 << left, *dst,
2827*4882a593Smuzhiyun 					    first);
2828*4882a593Smuzhiyun 			}
2829*4882a593Smuzhiyun 		} else {
2830*4882a593Smuzhiyun 			// Multiple destination words
2831*4882a593Smuzhiyun 			d0 = *src--;
2832*4882a593Smuzhiyun 			// Leading bits
2833*4882a593Smuzhiyun 			if (shift < 0) {
2834*4882a593Smuzhiyun 				// Single source word
2835*4882a593Smuzhiyun 				*dst = comp(d0 << left, *dst, first);
2836*4882a593Smuzhiyun 				dst--;
2837*4882a593Smuzhiyun 				n -= dst_idx + 1;
2838*4882a593Smuzhiyun 			} else {
2839*4882a593Smuzhiyun 				// 2 source words
2840*4882a593Smuzhiyun 				d1 = *src--;
2841*4882a593Smuzhiyun 				*dst = comp(d0 >> right | d1 << left, *dst,
2842*4882a593Smuzhiyun 					    first);
2843*4882a593Smuzhiyun 				d0 = d1;
2844*4882a593Smuzhiyun 				dst--;
2845*4882a593Smuzhiyun 				n -= dst_idx + 1;
2846*4882a593Smuzhiyun 			}
2847*4882a593Smuzhiyun 
2848*4882a593Smuzhiyun 			// Main chunk
2849*4882a593Smuzhiyun 			m = n % BITS_PER_LONG;
2850*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2851*4882a593Smuzhiyun 			while (n >= 4) {
2852*4882a593Smuzhiyun 				d1 = *src--;
2853*4882a593Smuzhiyun 				*dst-- = d0 >> right | d1 << left;
2854*4882a593Smuzhiyun 				d0 = d1;
2855*4882a593Smuzhiyun 				d1 = *src--;
2856*4882a593Smuzhiyun 				*dst-- = d0 >> right | d1 << left;
2857*4882a593Smuzhiyun 				d0 = d1;
2858*4882a593Smuzhiyun 				d1 = *src--;
2859*4882a593Smuzhiyun 				*dst-- = d0 >> right | d1 << left;
2860*4882a593Smuzhiyun 				d0 = d1;
2861*4882a593Smuzhiyun 				d1 = *src--;
2862*4882a593Smuzhiyun 				*dst-- = d0 >> right | d1 << left;
2863*4882a593Smuzhiyun 				d0 = d1;
2864*4882a593Smuzhiyun 				n -= 4;
2865*4882a593Smuzhiyun 			}
2866*4882a593Smuzhiyun 			while (n--) {
2867*4882a593Smuzhiyun 				d1 = *src--;
2868*4882a593Smuzhiyun 				*dst-- = d0 >> right | d1 << left;
2869*4882a593Smuzhiyun 				d0 = d1;
2870*4882a593Smuzhiyun 			}
2871*4882a593Smuzhiyun 
2872*4882a593Smuzhiyun 			// Trailing bits
2873*4882a593Smuzhiyun 			if (last) {
2874*4882a593Smuzhiyun 				if (m <= left) {
2875*4882a593Smuzhiyun 					// Single source word
2876*4882a593Smuzhiyun 					*dst = comp(d0 >> right, *dst, last);
2877*4882a593Smuzhiyun 				} else {
2878*4882a593Smuzhiyun 					// 2 source words
2879*4882a593Smuzhiyun 					d1 = *src;
2880*4882a593Smuzhiyun 					*dst = comp(d0 >> right | d1 << left,
2881*4882a593Smuzhiyun 						    *dst, last);
2882*4882a593Smuzhiyun 				}
2883*4882a593Smuzhiyun 			}
2884*4882a593Smuzhiyun 		}
2885*4882a593Smuzhiyun 	}
2886*4882a593Smuzhiyun }
2887*4882a593Smuzhiyun 
2888*4882a593Smuzhiyun 
2889*4882a593Smuzhiyun 	/*
2890*4882a593Smuzhiyun 	 *  Unaligned forward inverting bit copy using 32-bit or 64-bit memory
2891*4882a593Smuzhiyun 	 *  accesses
2892*4882a593Smuzhiyun 	 */
2893*4882a593Smuzhiyun 
bitcpy_not(unsigned long * dst,int dst_idx,const unsigned long * src,int src_idx,u32 n)2894*4882a593Smuzhiyun static void bitcpy_not(unsigned long *dst, int dst_idx,
2895*4882a593Smuzhiyun 		       const unsigned long *src, int src_idx, u32 n)
2896*4882a593Smuzhiyun {
2897*4882a593Smuzhiyun 	unsigned long first, last;
2898*4882a593Smuzhiyun 	int shift = dst_idx - src_idx, left, right;
2899*4882a593Smuzhiyun 	unsigned long d0, d1;
2900*4882a593Smuzhiyun 	int m;
2901*4882a593Smuzhiyun 
2902*4882a593Smuzhiyun 	if (!n)
2903*4882a593Smuzhiyun 		return;
2904*4882a593Smuzhiyun 
2905*4882a593Smuzhiyun 	shift = dst_idx - src_idx;
2906*4882a593Smuzhiyun 	first = ~0UL >> dst_idx;
2907*4882a593Smuzhiyun 	last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
2908*4882a593Smuzhiyun 
2909*4882a593Smuzhiyun 	if (!shift) {
2910*4882a593Smuzhiyun 		// Same alignment for source and dest
2911*4882a593Smuzhiyun 
2912*4882a593Smuzhiyun 		if (dst_idx + n <= BITS_PER_LONG) {
2913*4882a593Smuzhiyun 			// Single word
2914*4882a593Smuzhiyun 			if (last)
2915*4882a593Smuzhiyun 				first &= last;
2916*4882a593Smuzhiyun 			*dst = comp(~*src, *dst, first);
2917*4882a593Smuzhiyun 		} else {
2918*4882a593Smuzhiyun 			// Multiple destination words
2919*4882a593Smuzhiyun 			// Leading bits
2920*4882a593Smuzhiyun 			if (first) {
2921*4882a593Smuzhiyun 				*dst = comp(~*src, *dst, first);
2922*4882a593Smuzhiyun 				dst++;
2923*4882a593Smuzhiyun 				src++;
2924*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2925*4882a593Smuzhiyun 			}
2926*4882a593Smuzhiyun 
2927*4882a593Smuzhiyun 			// Main chunk
2928*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2929*4882a593Smuzhiyun 			while (n >= 8) {
2930*4882a593Smuzhiyun 				*dst++ = ~*src++;
2931*4882a593Smuzhiyun 				*dst++ = ~*src++;
2932*4882a593Smuzhiyun 				*dst++ = ~*src++;
2933*4882a593Smuzhiyun 				*dst++ = ~*src++;
2934*4882a593Smuzhiyun 				*dst++ = ~*src++;
2935*4882a593Smuzhiyun 				*dst++ = ~*src++;
2936*4882a593Smuzhiyun 				*dst++ = ~*src++;
2937*4882a593Smuzhiyun 				*dst++ = ~*src++;
2938*4882a593Smuzhiyun 				n -= 8;
2939*4882a593Smuzhiyun 			}
2940*4882a593Smuzhiyun 			while (n--)
2941*4882a593Smuzhiyun 				*dst++ = ~*src++;
2942*4882a593Smuzhiyun 
2943*4882a593Smuzhiyun 			// Trailing bits
2944*4882a593Smuzhiyun 			if (last)
2945*4882a593Smuzhiyun 				*dst = comp(~*src, *dst, last);
2946*4882a593Smuzhiyun 		}
2947*4882a593Smuzhiyun 	} else {
2948*4882a593Smuzhiyun 		// Different alignment for source and dest
2949*4882a593Smuzhiyun 
2950*4882a593Smuzhiyun 		right = shift & (BITS_PER_LONG - 1);
2951*4882a593Smuzhiyun 		left = -shift & (BITS_PER_LONG - 1);
2952*4882a593Smuzhiyun 
2953*4882a593Smuzhiyun 		if (dst_idx + n <= BITS_PER_LONG) {
2954*4882a593Smuzhiyun 			// Single destination word
2955*4882a593Smuzhiyun 			if (last)
2956*4882a593Smuzhiyun 				first &= last;
2957*4882a593Smuzhiyun 			if (shift > 0) {
2958*4882a593Smuzhiyun 				// Single source word
2959*4882a593Smuzhiyun 				*dst = comp(~*src >> right, *dst, first);
2960*4882a593Smuzhiyun 			} else if (src_idx + n <= BITS_PER_LONG) {
2961*4882a593Smuzhiyun 				// Single source word
2962*4882a593Smuzhiyun 				*dst = comp(~*src << left, *dst, first);
2963*4882a593Smuzhiyun 			} else {
2964*4882a593Smuzhiyun 				// 2 source words
2965*4882a593Smuzhiyun 				d0 = ~*src++;
2966*4882a593Smuzhiyun 				d1 = ~*src;
2967*4882a593Smuzhiyun 				*dst = comp(d0 << left | d1 >> right, *dst,
2968*4882a593Smuzhiyun 					    first);
2969*4882a593Smuzhiyun 			}
2970*4882a593Smuzhiyun 		} else {
2971*4882a593Smuzhiyun 			// Multiple destination words
2972*4882a593Smuzhiyun 			d0 = ~*src++;
2973*4882a593Smuzhiyun 			// Leading bits
2974*4882a593Smuzhiyun 			if (shift > 0) {
2975*4882a593Smuzhiyun 				// Single source word
2976*4882a593Smuzhiyun 				*dst = comp(d0 >> right, *dst, first);
2977*4882a593Smuzhiyun 				dst++;
2978*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2979*4882a593Smuzhiyun 			} else {
2980*4882a593Smuzhiyun 				// 2 source words
2981*4882a593Smuzhiyun 				d1 = ~*src++;
2982*4882a593Smuzhiyun 				*dst = comp(d0 << left | d1 >> right, *dst,
2983*4882a593Smuzhiyun 					    first);
2984*4882a593Smuzhiyun 				d0 = d1;
2985*4882a593Smuzhiyun 				dst++;
2986*4882a593Smuzhiyun 				n -= BITS_PER_LONG - dst_idx;
2987*4882a593Smuzhiyun 			}
2988*4882a593Smuzhiyun 
2989*4882a593Smuzhiyun 			// Main chunk
2990*4882a593Smuzhiyun 			m = n % BITS_PER_LONG;
2991*4882a593Smuzhiyun 			n /= BITS_PER_LONG;
2992*4882a593Smuzhiyun 			while (n >= 4) {
2993*4882a593Smuzhiyun 				d1 = ~*src++;
2994*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2995*4882a593Smuzhiyun 				d0 = d1;
2996*4882a593Smuzhiyun 				d1 = ~*src++;
2997*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
2998*4882a593Smuzhiyun 				d0 = d1;
2999*4882a593Smuzhiyun 				d1 = ~*src++;
3000*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
3001*4882a593Smuzhiyun 				d0 = d1;
3002*4882a593Smuzhiyun 				d1 = ~*src++;
3003*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
3004*4882a593Smuzhiyun 				d0 = d1;
3005*4882a593Smuzhiyun 				n -= 4;
3006*4882a593Smuzhiyun 			}
3007*4882a593Smuzhiyun 			while (n--) {
3008*4882a593Smuzhiyun 				d1 = ~*src++;
3009*4882a593Smuzhiyun 				*dst++ = d0 << left | d1 >> right;
3010*4882a593Smuzhiyun 				d0 = d1;
3011*4882a593Smuzhiyun 			}
3012*4882a593Smuzhiyun 
3013*4882a593Smuzhiyun 			// Trailing bits
3014*4882a593Smuzhiyun 			if (last) {
3015*4882a593Smuzhiyun 				if (m <= right) {
3016*4882a593Smuzhiyun 					// Single source word
3017*4882a593Smuzhiyun 					*dst = comp(d0 << left, *dst, last);
3018*4882a593Smuzhiyun 				} else {
3019*4882a593Smuzhiyun 					// 2 source words
3020*4882a593Smuzhiyun 					d1 = ~*src;
3021*4882a593Smuzhiyun 					*dst = comp(d0 << left | d1 >> right,
3022*4882a593Smuzhiyun 						    *dst, last);
3023*4882a593Smuzhiyun 				}
3024*4882a593Smuzhiyun 			}
3025*4882a593Smuzhiyun 		}
3026*4882a593Smuzhiyun 	}
3027*4882a593Smuzhiyun }
3028*4882a593Smuzhiyun 
3029*4882a593Smuzhiyun 
3030*4882a593Smuzhiyun 	/*
3031*4882a593Smuzhiyun 	 *  Unaligned 32-bit pattern fill using 32/64-bit memory accesses
3032*4882a593Smuzhiyun 	 */
3033*4882a593Smuzhiyun 
bitfill32(unsigned long * dst,int dst_idx,u32 pat,u32 n)3034*4882a593Smuzhiyun static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3035*4882a593Smuzhiyun {
3036*4882a593Smuzhiyun 	unsigned long val = pat;
3037*4882a593Smuzhiyun 	unsigned long first, last;
3038*4882a593Smuzhiyun 
3039*4882a593Smuzhiyun 	if (!n)
3040*4882a593Smuzhiyun 		return;
3041*4882a593Smuzhiyun 
3042*4882a593Smuzhiyun #if BITS_PER_LONG == 64
3043*4882a593Smuzhiyun 	val |= val << 32;
3044*4882a593Smuzhiyun #endif
3045*4882a593Smuzhiyun 
3046*4882a593Smuzhiyun 	first = ~0UL >> dst_idx;
3047*4882a593Smuzhiyun 	last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
3048*4882a593Smuzhiyun 
3049*4882a593Smuzhiyun 	if (dst_idx + n <= BITS_PER_LONG) {
3050*4882a593Smuzhiyun 		// Single word
3051*4882a593Smuzhiyun 		if (last)
3052*4882a593Smuzhiyun 			first &= last;
3053*4882a593Smuzhiyun 		*dst = comp(val, *dst, first);
3054*4882a593Smuzhiyun 	} else {
3055*4882a593Smuzhiyun 		// Multiple destination words
3056*4882a593Smuzhiyun 		// Leading bits
3057*4882a593Smuzhiyun 		if (first) {
3058*4882a593Smuzhiyun 			*dst = comp(val, *dst, first);
3059*4882a593Smuzhiyun 			dst++;
3060*4882a593Smuzhiyun 			n -= BITS_PER_LONG - dst_idx;
3061*4882a593Smuzhiyun 		}
3062*4882a593Smuzhiyun 
3063*4882a593Smuzhiyun 		// Main chunk
3064*4882a593Smuzhiyun 		n /= BITS_PER_LONG;
3065*4882a593Smuzhiyun 		while (n >= 8) {
3066*4882a593Smuzhiyun 			*dst++ = val;
3067*4882a593Smuzhiyun 			*dst++ = val;
3068*4882a593Smuzhiyun 			*dst++ = val;
3069*4882a593Smuzhiyun 			*dst++ = val;
3070*4882a593Smuzhiyun 			*dst++ = val;
3071*4882a593Smuzhiyun 			*dst++ = val;
3072*4882a593Smuzhiyun 			*dst++ = val;
3073*4882a593Smuzhiyun 			*dst++ = val;
3074*4882a593Smuzhiyun 			n -= 8;
3075*4882a593Smuzhiyun 		}
3076*4882a593Smuzhiyun 		while (n--)
3077*4882a593Smuzhiyun 			*dst++ = val;
3078*4882a593Smuzhiyun 
3079*4882a593Smuzhiyun 		// Trailing bits
3080*4882a593Smuzhiyun 		if (last)
3081*4882a593Smuzhiyun 			*dst = comp(val, *dst, last);
3082*4882a593Smuzhiyun 	}
3083*4882a593Smuzhiyun }
3084*4882a593Smuzhiyun 
3085*4882a593Smuzhiyun 
3086*4882a593Smuzhiyun 	/*
3087*4882a593Smuzhiyun 	 *  Unaligned 32-bit pattern xor using 32/64-bit memory accesses
3088*4882a593Smuzhiyun 	 */
3089*4882a593Smuzhiyun 
bitxor32(unsigned long * dst,int dst_idx,u32 pat,u32 n)3090*4882a593Smuzhiyun static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3091*4882a593Smuzhiyun {
3092*4882a593Smuzhiyun 	unsigned long val = pat;
3093*4882a593Smuzhiyun 	unsigned long first, last;
3094*4882a593Smuzhiyun 
3095*4882a593Smuzhiyun 	if (!n)
3096*4882a593Smuzhiyun 		return;
3097*4882a593Smuzhiyun 
3098*4882a593Smuzhiyun #if BITS_PER_LONG == 64
3099*4882a593Smuzhiyun 	val |= val << 32;
3100*4882a593Smuzhiyun #endif
3101*4882a593Smuzhiyun 
3102*4882a593Smuzhiyun 	first = ~0UL >> dst_idx;
3103*4882a593Smuzhiyun 	last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
3104*4882a593Smuzhiyun 
3105*4882a593Smuzhiyun 	if (dst_idx + n <= BITS_PER_LONG) {
3106*4882a593Smuzhiyun 		// Single word
3107*4882a593Smuzhiyun 		if (last)
3108*4882a593Smuzhiyun 			first &= last;
3109*4882a593Smuzhiyun 		*dst = xor(val, *dst, first);
3110*4882a593Smuzhiyun 	} else {
3111*4882a593Smuzhiyun 		// Multiple destination words
3112*4882a593Smuzhiyun 		// Leading bits
3113*4882a593Smuzhiyun 		if (first) {
3114*4882a593Smuzhiyun 			*dst = xor(val, *dst, first);
3115*4882a593Smuzhiyun 			dst++;
3116*4882a593Smuzhiyun 			n -= BITS_PER_LONG - dst_idx;
3117*4882a593Smuzhiyun 		}
3118*4882a593Smuzhiyun 
3119*4882a593Smuzhiyun 		// Main chunk
3120*4882a593Smuzhiyun 		n /= BITS_PER_LONG;
3121*4882a593Smuzhiyun 		while (n >= 4) {
3122*4882a593Smuzhiyun 			*dst++ ^= val;
3123*4882a593Smuzhiyun 			*dst++ ^= val;
3124*4882a593Smuzhiyun 			*dst++ ^= val;
3125*4882a593Smuzhiyun 			*dst++ ^= val;
3126*4882a593Smuzhiyun 			n -= 4;
3127*4882a593Smuzhiyun 		}
3128*4882a593Smuzhiyun 		while (n--)
3129*4882a593Smuzhiyun 			*dst++ ^= val;
3130*4882a593Smuzhiyun 
3131*4882a593Smuzhiyun 		// Trailing bits
3132*4882a593Smuzhiyun 		if (last)
3133*4882a593Smuzhiyun 			*dst = xor(val, *dst, last);
3134*4882a593Smuzhiyun 	}
3135*4882a593Smuzhiyun }
3136*4882a593Smuzhiyun 
fill_one_line(int bpp,unsigned long next_plane,unsigned long * dst,int dst_idx,u32 n,u32 color)3137*4882a593Smuzhiyun static inline void fill_one_line(int bpp, unsigned long next_plane,
3138*4882a593Smuzhiyun 				 unsigned long *dst, int dst_idx, u32 n,
3139*4882a593Smuzhiyun 				 u32 color)
3140*4882a593Smuzhiyun {
3141*4882a593Smuzhiyun 	while (1) {
3142*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
3143*4882a593Smuzhiyun 		dst_idx &= (BITS_PER_LONG - 1);
3144*4882a593Smuzhiyun 		bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3145*4882a593Smuzhiyun 		if (!--bpp)
3146*4882a593Smuzhiyun 			break;
3147*4882a593Smuzhiyun 		color >>= 1;
3148*4882a593Smuzhiyun 		dst_idx += next_plane * 8;
3149*4882a593Smuzhiyun 	}
3150*4882a593Smuzhiyun }
3151*4882a593Smuzhiyun 
xor_one_line(int bpp,unsigned long next_plane,unsigned long * dst,int dst_idx,u32 n,u32 color)3152*4882a593Smuzhiyun static inline void xor_one_line(int bpp, unsigned long next_plane,
3153*4882a593Smuzhiyun 				unsigned long *dst, int dst_idx, u32 n,
3154*4882a593Smuzhiyun 				u32 color)
3155*4882a593Smuzhiyun {
3156*4882a593Smuzhiyun 	while (color) {
3157*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
3158*4882a593Smuzhiyun 		dst_idx &= (BITS_PER_LONG - 1);
3159*4882a593Smuzhiyun 		bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3160*4882a593Smuzhiyun 		if (!--bpp)
3161*4882a593Smuzhiyun 			break;
3162*4882a593Smuzhiyun 		color >>= 1;
3163*4882a593Smuzhiyun 		dst_idx += next_plane * 8;
3164*4882a593Smuzhiyun 	}
3165*4882a593Smuzhiyun }
3166*4882a593Smuzhiyun 
3167*4882a593Smuzhiyun 
amifb_fillrect(struct fb_info * info,const struct fb_fillrect * rect)3168*4882a593Smuzhiyun static void amifb_fillrect(struct fb_info *info,
3169*4882a593Smuzhiyun 			   const struct fb_fillrect *rect)
3170*4882a593Smuzhiyun {
3171*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
3172*4882a593Smuzhiyun 	int dst_idx, x2, y2;
3173*4882a593Smuzhiyun 	unsigned long *dst;
3174*4882a593Smuzhiyun 	u32 width, height;
3175*4882a593Smuzhiyun 
3176*4882a593Smuzhiyun 	if (!rect->width || !rect->height)
3177*4882a593Smuzhiyun 		return;
3178*4882a593Smuzhiyun 
3179*4882a593Smuzhiyun 	/*
3180*4882a593Smuzhiyun 	 * We could use hardware clipping but on many cards you get around
3181*4882a593Smuzhiyun 	 * hardware clipping by writing to framebuffer directly.
3182*4882a593Smuzhiyun 	 * */
3183*4882a593Smuzhiyun 	x2 = rect->dx + rect->width;
3184*4882a593Smuzhiyun 	y2 = rect->dy + rect->height;
3185*4882a593Smuzhiyun 	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3186*4882a593Smuzhiyun 	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3187*4882a593Smuzhiyun 	width = x2 - rect->dx;
3188*4882a593Smuzhiyun 	height = y2 - rect->dy;
3189*4882a593Smuzhiyun 
3190*4882a593Smuzhiyun 	dst = (unsigned long *)
3191*4882a593Smuzhiyun 		((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3192*4882a593Smuzhiyun 	dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3193*4882a593Smuzhiyun 	dst_idx += rect->dy * par->next_line * 8 + rect->dx;
3194*4882a593Smuzhiyun 	while (height--) {
3195*4882a593Smuzhiyun 		switch (rect->rop) {
3196*4882a593Smuzhiyun 		case ROP_COPY:
3197*4882a593Smuzhiyun 			fill_one_line(info->var.bits_per_pixel,
3198*4882a593Smuzhiyun 				      par->next_plane, dst, dst_idx, width,
3199*4882a593Smuzhiyun 				      rect->color);
3200*4882a593Smuzhiyun 			break;
3201*4882a593Smuzhiyun 
3202*4882a593Smuzhiyun 		case ROP_XOR:
3203*4882a593Smuzhiyun 			xor_one_line(info->var.bits_per_pixel, par->next_plane,
3204*4882a593Smuzhiyun 				     dst, dst_idx, width, rect->color);
3205*4882a593Smuzhiyun 			break;
3206*4882a593Smuzhiyun 		}
3207*4882a593Smuzhiyun 		dst_idx += par->next_line * 8;
3208*4882a593Smuzhiyun 	}
3209*4882a593Smuzhiyun }
3210*4882a593Smuzhiyun 
copy_one_line(int bpp,unsigned long next_plane,unsigned long * dst,int dst_idx,unsigned long * src,int src_idx,u32 n)3211*4882a593Smuzhiyun static inline void copy_one_line(int bpp, unsigned long next_plane,
3212*4882a593Smuzhiyun 				 unsigned long *dst, int dst_idx,
3213*4882a593Smuzhiyun 				 unsigned long *src, int src_idx, u32 n)
3214*4882a593Smuzhiyun {
3215*4882a593Smuzhiyun 	while (1) {
3216*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
3217*4882a593Smuzhiyun 		dst_idx &= (BITS_PER_LONG - 1);
3218*4882a593Smuzhiyun 		src += src_idx >> SHIFT_PER_LONG;
3219*4882a593Smuzhiyun 		src_idx &= (BITS_PER_LONG - 1);
3220*4882a593Smuzhiyun 		bitcpy(dst, dst_idx, src, src_idx, n);
3221*4882a593Smuzhiyun 		if (!--bpp)
3222*4882a593Smuzhiyun 			break;
3223*4882a593Smuzhiyun 		dst_idx += next_plane * 8;
3224*4882a593Smuzhiyun 		src_idx += next_plane * 8;
3225*4882a593Smuzhiyun 	}
3226*4882a593Smuzhiyun }
3227*4882a593Smuzhiyun 
copy_one_line_rev(int bpp,unsigned long next_plane,unsigned long * dst,int dst_idx,unsigned long * src,int src_idx,u32 n)3228*4882a593Smuzhiyun static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
3229*4882a593Smuzhiyun 				     unsigned long *dst, int dst_idx,
3230*4882a593Smuzhiyun 				     unsigned long *src, int src_idx, u32 n)
3231*4882a593Smuzhiyun {
3232*4882a593Smuzhiyun 	while (1) {
3233*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
3234*4882a593Smuzhiyun 		dst_idx &= (BITS_PER_LONG - 1);
3235*4882a593Smuzhiyun 		src += src_idx >> SHIFT_PER_LONG;
3236*4882a593Smuzhiyun 		src_idx &= (BITS_PER_LONG - 1);
3237*4882a593Smuzhiyun 		bitcpy_rev(dst, dst_idx, src, src_idx, n);
3238*4882a593Smuzhiyun 		if (!--bpp)
3239*4882a593Smuzhiyun 			break;
3240*4882a593Smuzhiyun 		dst_idx += next_plane * 8;
3241*4882a593Smuzhiyun 		src_idx += next_plane * 8;
3242*4882a593Smuzhiyun 	}
3243*4882a593Smuzhiyun }
3244*4882a593Smuzhiyun 
3245*4882a593Smuzhiyun 
amifb_copyarea(struct fb_info * info,const struct fb_copyarea * area)3246*4882a593Smuzhiyun static void amifb_copyarea(struct fb_info *info,
3247*4882a593Smuzhiyun 			   const struct fb_copyarea *area)
3248*4882a593Smuzhiyun {
3249*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
3250*4882a593Smuzhiyun 	int x2, y2;
3251*4882a593Smuzhiyun 	u32 dx, dy, sx, sy, width, height;
3252*4882a593Smuzhiyun 	unsigned long *dst, *src;
3253*4882a593Smuzhiyun 	int dst_idx, src_idx;
3254*4882a593Smuzhiyun 	int rev_copy = 0;
3255*4882a593Smuzhiyun 
3256*4882a593Smuzhiyun 	/* clip the destination */
3257*4882a593Smuzhiyun 	x2 = area->dx + area->width;
3258*4882a593Smuzhiyun 	y2 = area->dy + area->height;
3259*4882a593Smuzhiyun 	dx = area->dx > 0 ? area->dx : 0;
3260*4882a593Smuzhiyun 	dy = area->dy > 0 ? area->dy : 0;
3261*4882a593Smuzhiyun 	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3262*4882a593Smuzhiyun 	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3263*4882a593Smuzhiyun 	width = x2 - dx;
3264*4882a593Smuzhiyun 	height = y2 - dy;
3265*4882a593Smuzhiyun 
3266*4882a593Smuzhiyun 	if (area->sx + dx < area->dx || area->sy + dy < area->dy)
3267*4882a593Smuzhiyun 		return;
3268*4882a593Smuzhiyun 
3269*4882a593Smuzhiyun 	/* update sx,sy */
3270*4882a593Smuzhiyun 	sx = area->sx + (dx - area->dx);
3271*4882a593Smuzhiyun 	sy = area->sy + (dy - area->dy);
3272*4882a593Smuzhiyun 
3273*4882a593Smuzhiyun 	/* the source must be completely inside the virtual screen */
3274*4882a593Smuzhiyun 	if (sx + width > info->var.xres_virtual ||
3275*4882a593Smuzhiyun 			sy + height > info->var.yres_virtual)
3276*4882a593Smuzhiyun 		return;
3277*4882a593Smuzhiyun 
3278*4882a593Smuzhiyun 	if (dy > sy || (dy == sy && dx > sx)) {
3279*4882a593Smuzhiyun 		dy += height;
3280*4882a593Smuzhiyun 		sy += height;
3281*4882a593Smuzhiyun 		rev_copy = 1;
3282*4882a593Smuzhiyun 	}
3283*4882a593Smuzhiyun 	dst = (unsigned long *)
3284*4882a593Smuzhiyun 		((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3285*4882a593Smuzhiyun 	src = dst;
3286*4882a593Smuzhiyun 	dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3287*4882a593Smuzhiyun 	src_idx = dst_idx;
3288*4882a593Smuzhiyun 	dst_idx += dy * par->next_line * 8 + dx;
3289*4882a593Smuzhiyun 	src_idx += sy * par->next_line * 8 + sx;
3290*4882a593Smuzhiyun 	if (rev_copy) {
3291*4882a593Smuzhiyun 		while (height--) {
3292*4882a593Smuzhiyun 			dst_idx -= par->next_line * 8;
3293*4882a593Smuzhiyun 			src_idx -= par->next_line * 8;
3294*4882a593Smuzhiyun 			copy_one_line_rev(info->var.bits_per_pixel,
3295*4882a593Smuzhiyun 					  par->next_plane, dst, dst_idx, src,
3296*4882a593Smuzhiyun 					  src_idx, width);
3297*4882a593Smuzhiyun 		}
3298*4882a593Smuzhiyun 	} else {
3299*4882a593Smuzhiyun 		while (height--) {
3300*4882a593Smuzhiyun 			copy_one_line(info->var.bits_per_pixel,
3301*4882a593Smuzhiyun 				      par->next_plane, dst, dst_idx, src,
3302*4882a593Smuzhiyun 				      src_idx, width);
3303*4882a593Smuzhiyun 			dst_idx += par->next_line * 8;
3304*4882a593Smuzhiyun 			src_idx += par->next_line * 8;
3305*4882a593Smuzhiyun 		}
3306*4882a593Smuzhiyun 	}
3307*4882a593Smuzhiyun }
3308*4882a593Smuzhiyun 
3309*4882a593Smuzhiyun 
expand_one_line(int bpp,unsigned long next_plane,unsigned long * dst,int dst_idx,u32 n,const u8 * data,u32 bgcolor,u32 fgcolor)3310*4882a593Smuzhiyun static inline void expand_one_line(int bpp, unsigned long next_plane,
3311*4882a593Smuzhiyun 				   unsigned long *dst, int dst_idx, u32 n,
3312*4882a593Smuzhiyun 				   const u8 *data, u32 bgcolor, u32 fgcolor)
3313*4882a593Smuzhiyun {
3314*4882a593Smuzhiyun 	const unsigned long *src;
3315*4882a593Smuzhiyun 	int src_idx;
3316*4882a593Smuzhiyun 
3317*4882a593Smuzhiyun 	while (1) {
3318*4882a593Smuzhiyun 		dst += dst_idx >> SHIFT_PER_LONG;
3319*4882a593Smuzhiyun 		dst_idx &= (BITS_PER_LONG - 1);
3320*4882a593Smuzhiyun 		if ((bgcolor ^ fgcolor) & 1) {
3321*4882a593Smuzhiyun 			src = (unsigned long *)
3322*4882a593Smuzhiyun 				((unsigned long)data & ~(BYTES_PER_LONG - 1));
3323*4882a593Smuzhiyun 			src_idx = ((unsigned long)data & (BYTES_PER_LONG - 1)) * 8;
3324*4882a593Smuzhiyun 			if (fgcolor & 1)
3325*4882a593Smuzhiyun 				bitcpy(dst, dst_idx, src, src_idx, n);
3326*4882a593Smuzhiyun 			else
3327*4882a593Smuzhiyun 				bitcpy_not(dst, dst_idx, src, src_idx, n);
3328*4882a593Smuzhiyun 			/* set or clear */
3329*4882a593Smuzhiyun 		} else
3330*4882a593Smuzhiyun 			bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
3331*4882a593Smuzhiyun 		if (!--bpp)
3332*4882a593Smuzhiyun 			break;
3333*4882a593Smuzhiyun 		bgcolor >>= 1;
3334*4882a593Smuzhiyun 		fgcolor >>= 1;
3335*4882a593Smuzhiyun 		dst_idx += next_plane * 8;
3336*4882a593Smuzhiyun 	}
3337*4882a593Smuzhiyun }
3338*4882a593Smuzhiyun 
3339*4882a593Smuzhiyun 
amifb_imageblit(struct fb_info * info,const struct fb_image * image)3340*4882a593Smuzhiyun static void amifb_imageblit(struct fb_info *info, const struct fb_image *image)
3341*4882a593Smuzhiyun {
3342*4882a593Smuzhiyun 	struct amifb_par *par = info->par;
3343*4882a593Smuzhiyun 	int x2, y2;
3344*4882a593Smuzhiyun 	unsigned long *dst;
3345*4882a593Smuzhiyun 	int dst_idx;
3346*4882a593Smuzhiyun 	const char *src;
3347*4882a593Smuzhiyun 	u32 dx, dy, width, height, pitch;
3348*4882a593Smuzhiyun 
3349*4882a593Smuzhiyun 	/*
3350*4882a593Smuzhiyun 	 * We could use hardware clipping but on many cards you get around
3351*4882a593Smuzhiyun 	 * hardware clipping by writing to framebuffer directly like we are
3352*4882a593Smuzhiyun 	 * doing here.
3353*4882a593Smuzhiyun 	 */
3354*4882a593Smuzhiyun 	x2 = image->dx + image->width;
3355*4882a593Smuzhiyun 	y2 = image->dy + image->height;
3356*4882a593Smuzhiyun 	dx = image->dx;
3357*4882a593Smuzhiyun 	dy = image->dy;
3358*4882a593Smuzhiyun 	x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3359*4882a593Smuzhiyun 	y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3360*4882a593Smuzhiyun 	width  = x2 - dx;
3361*4882a593Smuzhiyun 	height = y2 - dy;
3362*4882a593Smuzhiyun 
3363*4882a593Smuzhiyun 	if (image->depth == 1) {
3364*4882a593Smuzhiyun 		dst = (unsigned long *)
3365*4882a593Smuzhiyun 			((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3366*4882a593Smuzhiyun 		dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3367*4882a593Smuzhiyun 		dst_idx += dy * par->next_line * 8 + dx;
3368*4882a593Smuzhiyun 		src = image->data;
3369*4882a593Smuzhiyun 		pitch = (image->width + 7) / 8;
3370*4882a593Smuzhiyun 		while (height--) {
3371*4882a593Smuzhiyun 			expand_one_line(info->var.bits_per_pixel,
3372*4882a593Smuzhiyun 					par->next_plane, dst, dst_idx, width,
3373*4882a593Smuzhiyun 					src, image->bg_color,
3374*4882a593Smuzhiyun 					image->fg_color);
3375*4882a593Smuzhiyun 			dst_idx += par->next_line * 8;
3376*4882a593Smuzhiyun 			src += pitch;
3377*4882a593Smuzhiyun 		}
3378*4882a593Smuzhiyun 	} else {
3379*4882a593Smuzhiyun 		c2p_planar(info->screen_base, image->data, dx, dy, width,
3380*4882a593Smuzhiyun 			   height, par->next_line, par->next_plane,
3381*4882a593Smuzhiyun 			   image->width, info->var.bits_per_pixel);
3382*4882a593Smuzhiyun 	}
3383*4882a593Smuzhiyun }
3384*4882a593Smuzhiyun 
3385*4882a593Smuzhiyun 
3386*4882a593Smuzhiyun 	/*
3387*4882a593Smuzhiyun 	 * Amiga Frame Buffer Specific ioctls
3388*4882a593Smuzhiyun 	 */
3389*4882a593Smuzhiyun 
amifb_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)3390*4882a593Smuzhiyun static int amifb_ioctl(struct fb_info *info,
3391*4882a593Smuzhiyun 		       unsigned int cmd, unsigned long arg)
3392*4882a593Smuzhiyun {
3393*4882a593Smuzhiyun 	union {
3394*4882a593Smuzhiyun 		struct fb_fix_cursorinfo fix;
3395*4882a593Smuzhiyun 		struct fb_var_cursorinfo var;
3396*4882a593Smuzhiyun 		struct fb_cursorstate state;
3397*4882a593Smuzhiyun 	} crsr;
3398*4882a593Smuzhiyun 	void __user *argp = (void __user *)arg;
3399*4882a593Smuzhiyun 	int i;
3400*4882a593Smuzhiyun 
3401*4882a593Smuzhiyun 	switch (cmd) {
3402*4882a593Smuzhiyun 	case FBIOGET_FCURSORINFO:
3403*4882a593Smuzhiyun 		i = ami_get_fix_cursorinfo(&crsr.fix, info->par);
3404*4882a593Smuzhiyun 		if (i)
3405*4882a593Smuzhiyun 			return i;
3406*4882a593Smuzhiyun 		return copy_to_user(argp, &crsr.fix,
3407*4882a593Smuzhiyun 				    sizeof(crsr.fix)) ? -EFAULT : 0;
3408*4882a593Smuzhiyun 
3409*4882a593Smuzhiyun 	case FBIOGET_VCURSORINFO:
3410*4882a593Smuzhiyun 		i = ami_get_var_cursorinfo(&crsr.var,
3411*4882a593Smuzhiyun 			((struct fb_var_cursorinfo __user *)arg)->data,
3412*4882a593Smuzhiyun 			info->par);
3413*4882a593Smuzhiyun 		if (i)
3414*4882a593Smuzhiyun 			return i;
3415*4882a593Smuzhiyun 		return copy_to_user(argp, &crsr.var,
3416*4882a593Smuzhiyun 				    sizeof(crsr.var)) ? -EFAULT : 0;
3417*4882a593Smuzhiyun 
3418*4882a593Smuzhiyun 	case FBIOPUT_VCURSORINFO:
3419*4882a593Smuzhiyun 		if (copy_from_user(&crsr.var, argp, sizeof(crsr.var)))
3420*4882a593Smuzhiyun 			return -EFAULT;
3421*4882a593Smuzhiyun 		return ami_set_var_cursorinfo(&crsr.var,
3422*4882a593Smuzhiyun 			((struct fb_var_cursorinfo __user *)arg)->data,
3423*4882a593Smuzhiyun 			info->par);
3424*4882a593Smuzhiyun 
3425*4882a593Smuzhiyun 	case FBIOGET_CURSORSTATE:
3426*4882a593Smuzhiyun 		i = ami_get_cursorstate(&crsr.state, info->par);
3427*4882a593Smuzhiyun 		if (i)
3428*4882a593Smuzhiyun 			return i;
3429*4882a593Smuzhiyun 		return copy_to_user(argp, &crsr.state,
3430*4882a593Smuzhiyun 				    sizeof(crsr.state)) ? -EFAULT : 0;
3431*4882a593Smuzhiyun 
3432*4882a593Smuzhiyun 	case FBIOPUT_CURSORSTATE:
3433*4882a593Smuzhiyun 		if (copy_from_user(&crsr.state, argp, sizeof(crsr.state)))
3434*4882a593Smuzhiyun 			return -EFAULT;
3435*4882a593Smuzhiyun 		return ami_set_cursorstate(&crsr.state, info->par);
3436*4882a593Smuzhiyun 	}
3437*4882a593Smuzhiyun 	return -EINVAL;
3438*4882a593Smuzhiyun }
3439*4882a593Smuzhiyun 
3440*4882a593Smuzhiyun 
3441*4882a593Smuzhiyun 	/*
3442*4882a593Smuzhiyun 	 * Flash the cursor (called by VBlank interrupt)
3443*4882a593Smuzhiyun 	 */
3444*4882a593Smuzhiyun 
flash_cursor(void)3445*4882a593Smuzhiyun static int flash_cursor(void)
3446*4882a593Smuzhiyun {
3447*4882a593Smuzhiyun 	static int cursorcount = 1;
3448*4882a593Smuzhiyun 
3449*4882a593Smuzhiyun 	if (cursormode == FB_CURSOR_FLASH) {
3450*4882a593Smuzhiyun 		if (!--cursorcount) {
3451*4882a593Smuzhiyun 			cursorstate = -cursorstate;
3452*4882a593Smuzhiyun 			cursorcount = cursorrate;
3453*4882a593Smuzhiyun 			if (!is_blanked)
3454*4882a593Smuzhiyun 				return 1;
3455*4882a593Smuzhiyun 		}
3456*4882a593Smuzhiyun 	}
3457*4882a593Smuzhiyun 	return 0;
3458*4882a593Smuzhiyun }
3459*4882a593Smuzhiyun 
3460*4882a593Smuzhiyun 	/*
3461*4882a593Smuzhiyun 	 * VBlank Display Interrupt
3462*4882a593Smuzhiyun 	 */
3463*4882a593Smuzhiyun 
amifb_interrupt(int irq,void * dev_id)3464*4882a593Smuzhiyun static irqreturn_t amifb_interrupt(int irq, void *dev_id)
3465*4882a593Smuzhiyun {
3466*4882a593Smuzhiyun 	struct amifb_par *par = dev_id;
3467*4882a593Smuzhiyun 
3468*4882a593Smuzhiyun 	if (do_vmode_pan || do_vmode_full)
3469*4882a593Smuzhiyun 		ami_update_display(par);
3470*4882a593Smuzhiyun 
3471*4882a593Smuzhiyun 	if (do_vmode_full)
3472*4882a593Smuzhiyun 		ami_init_display(par);
3473*4882a593Smuzhiyun 
3474*4882a593Smuzhiyun 	if (do_vmode_pan) {
3475*4882a593Smuzhiyun 		flash_cursor();
3476*4882a593Smuzhiyun 		ami_rebuild_copper(par);
3477*4882a593Smuzhiyun 		do_cursor = do_vmode_pan = 0;
3478*4882a593Smuzhiyun 	} else if (do_cursor) {
3479*4882a593Smuzhiyun 		flash_cursor();
3480*4882a593Smuzhiyun 		ami_set_sprite(par);
3481*4882a593Smuzhiyun 		do_cursor = 0;
3482*4882a593Smuzhiyun 	} else {
3483*4882a593Smuzhiyun 		if (flash_cursor())
3484*4882a593Smuzhiyun 			ami_set_sprite(par);
3485*4882a593Smuzhiyun 	}
3486*4882a593Smuzhiyun 
3487*4882a593Smuzhiyun 	if (do_blank) {
3488*4882a593Smuzhiyun 		ami_do_blank(par);
3489*4882a593Smuzhiyun 		do_blank = 0;
3490*4882a593Smuzhiyun 	}
3491*4882a593Smuzhiyun 
3492*4882a593Smuzhiyun 	if (do_vmode_full) {
3493*4882a593Smuzhiyun 		ami_reinit_copper(par);
3494*4882a593Smuzhiyun 		do_vmode_full = 0;
3495*4882a593Smuzhiyun 	}
3496*4882a593Smuzhiyun 	return IRQ_HANDLED;
3497*4882a593Smuzhiyun }
3498*4882a593Smuzhiyun 
3499*4882a593Smuzhiyun 
3500*4882a593Smuzhiyun static const struct fb_ops amifb_ops = {
3501*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
3502*4882a593Smuzhiyun 	.fb_check_var	= amifb_check_var,
3503*4882a593Smuzhiyun 	.fb_set_par	= amifb_set_par,
3504*4882a593Smuzhiyun 	.fb_setcolreg	= amifb_setcolreg,
3505*4882a593Smuzhiyun 	.fb_blank	= amifb_blank,
3506*4882a593Smuzhiyun 	.fb_pan_display	= amifb_pan_display,
3507*4882a593Smuzhiyun 	.fb_fillrect	= amifb_fillrect,
3508*4882a593Smuzhiyun 	.fb_copyarea	= amifb_copyarea,
3509*4882a593Smuzhiyun 	.fb_imageblit	= amifb_imageblit,
3510*4882a593Smuzhiyun 	.fb_ioctl	= amifb_ioctl,
3511*4882a593Smuzhiyun };
3512*4882a593Smuzhiyun 
3513*4882a593Smuzhiyun 
3514*4882a593Smuzhiyun 	/*
3515*4882a593Smuzhiyun 	 * Allocate, Clear and Align a Block of Chip Memory
3516*4882a593Smuzhiyun 	 */
3517*4882a593Smuzhiyun 
3518*4882a593Smuzhiyun static void *aligned_chipptr;
3519*4882a593Smuzhiyun 
chipalloc(u_long size)3520*4882a593Smuzhiyun static inline u_long __init chipalloc(u_long size)
3521*4882a593Smuzhiyun {
3522*4882a593Smuzhiyun 	aligned_chipptr = amiga_chip_alloc(size, "amifb [RAM]");
3523*4882a593Smuzhiyun 	if (!aligned_chipptr) {
3524*4882a593Smuzhiyun 		pr_err("amifb: No Chip RAM for frame buffer");
3525*4882a593Smuzhiyun 		return 0;
3526*4882a593Smuzhiyun 	}
3527*4882a593Smuzhiyun 	memset(aligned_chipptr, 0, size);
3528*4882a593Smuzhiyun 	return (u_long)aligned_chipptr;
3529*4882a593Smuzhiyun }
3530*4882a593Smuzhiyun 
chipfree(void)3531*4882a593Smuzhiyun static inline void chipfree(void)
3532*4882a593Smuzhiyun {
3533*4882a593Smuzhiyun 	if (aligned_chipptr)
3534*4882a593Smuzhiyun 		amiga_chip_free(aligned_chipptr);
3535*4882a593Smuzhiyun }
3536*4882a593Smuzhiyun 
3537*4882a593Smuzhiyun 
3538*4882a593Smuzhiyun 	/*
3539*4882a593Smuzhiyun 	 * Initialisation
3540*4882a593Smuzhiyun 	 */
3541*4882a593Smuzhiyun 
amifb_probe(struct platform_device * pdev)3542*4882a593Smuzhiyun static int __init amifb_probe(struct platform_device *pdev)
3543*4882a593Smuzhiyun {
3544*4882a593Smuzhiyun 	struct fb_info *info;
3545*4882a593Smuzhiyun 	int tag, i, err = 0;
3546*4882a593Smuzhiyun 	u_long chipptr;
3547*4882a593Smuzhiyun 	u_int defmode;
3548*4882a593Smuzhiyun 
3549*4882a593Smuzhiyun #ifndef MODULE
3550*4882a593Smuzhiyun 	char *option = NULL;
3551*4882a593Smuzhiyun 
3552*4882a593Smuzhiyun 	if (fb_get_options("amifb", &option)) {
3553*4882a593Smuzhiyun 		amifb_video_off();
3554*4882a593Smuzhiyun 		return -ENODEV;
3555*4882a593Smuzhiyun 	}
3556*4882a593Smuzhiyun 	amifb_setup(option);
3557*4882a593Smuzhiyun #endif
3558*4882a593Smuzhiyun 	custom.dmacon = DMAF_ALL | DMAF_MASTER;
3559*4882a593Smuzhiyun 
3560*4882a593Smuzhiyun 	info = framebuffer_alloc(sizeof(struct amifb_par), &pdev->dev);
3561*4882a593Smuzhiyun 	if (!info)
3562*4882a593Smuzhiyun 		return -ENOMEM;
3563*4882a593Smuzhiyun 
3564*4882a593Smuzhiyun 	strcpy(info->fix.id, "Amiga ");
3565*4882a593Smuzhiyun 	info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
3566*4882a593Smuzhiyun 	info->fix.accel = FB_ACCEL_AMIGABLITT;
3567*4882a593Smuzhiyun 
3568*4882a593Smuzhiyun 	switch (amiga_chipset) {
3569*4882a593Smuzhiyun #ifdef CONFIG_FB_AMIGA_OCS
3570*4882a593Smuzhiyun 	case CS_OCS:
3571*4882a593Smuzhiyun 		strcat(info->fix.id, "OCS");
3572*4882a593Smuzhiyun default_chipset:
3573*4882a593Smuzhiyun 		chipset = TAG_OCS;
3574*4882a593Smuzhiyun 		maxdepth[TAG_SHRES] = 0;	/* OCS means no SHRES */
3575*4882a593Smuzhiyun 		maxdepth[TAG_HIRES] = 4;
3576*4882a593Smuzhiyun 		maxdepth[TAG_LORES] = 6;
3577*4882a593Smuzhiyun 		maxfmode = TAG_FMODE_1;
3578*4882a593Smuzhiyun 		defmode = amiga_vblank == 50 ? DEFMODE_PAL : DEFMODE_NTSC;
3579*4882a593Smuzhiyun 		info->fix.smem_len = VIDEOMEMSIZE_OCS;
3580*4882a593Smuzhiyun 		break;
3581*4882a593Smuzhiyun #endif /* CONFIG_FB_AMIGA_OCS */
3582*4882a593Smuzhiyun 
3583*4882a593Smuzhiyun #ifdef CONFIG_FB_AMIGA_ECS
3584*4882a593Smuzhiyun 	case CS_ECS:
3585*4882a593Smuzhiyun 		strcat(info->fix.id, "ECS");
3586*4882a593Smuzhiyun 		chipset = TAG_ECS;
3587*4882a593Smuzhiyun 		maxdepth[TAG_SHRES] = 2;
3588*4882a593Smuzhiyun 		maxdepth[TAG_HIRES] = 4;
3589*4882a593Smuzhiyun 		maxdepth[TAG_LORES] = 6;
3590*4882a593Smuzhiyun 		maxfmode = TAG_FMODE_1;
3591*4882a593Smuzhiyun 		if (AMIGAHW_PRESENT(AMBER_FF))
3592*4882a593Smuzhiyun 			defmode = amiga_vblank == 50 ? DEFMODE_AMBER_PAL
3593*4882a593Smuzhiyun 						     : DEFMODE_AMBER_NTSC;
3594*4882a593Smuzhiyun 		else
3595*4882a593Smuzhiyun 			defmode = amiga_vblank == 50 ? DEFMODE_PAL
3596*4882a593Smuzhiyun 						     : DEFMODE_NTSC;
3597*4882a593Smuzhiyun 		if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3598*4882a593Smuzhiyun 		    VIDEOMEMSIZE_ECS_2M)
3599*4882a593Smuzhiyun 			info->fix.smem_len = VIDEOMEMSIZE_ECS_2M;
3600*4882a593Smuzhiyun 		else
3601*4882a593Smuzhiyun 			info->fix.smem_len = VIDEOMEMSIZE_ECS_1M;
3602*4882a593Smuzhiyun 		break;
3603*4882a593Smuzhiyun #endif /* CONFIG_FB_AMIGA_ECS */
3604*4882a593Smuzhiyun 
3605*4882a593Smuzhiyun #ifdef CONFIG_FB_AMIGA_AGA
3606*4882a593Smuzhiyun 	case CS_AGA:
3607*4882a593Smuzhiyun 		strcat(info->fix.id, "AGA");
3608*4882a593Smuzhiyun 		chipset = TAG_AGA;
3609*4882a593Smuzhiyun 		maxdepth[TAG_SHRES] = 8;
3610*4882a593Smuzhiyun 		maxdepth[TAG_HIRES] = 8;
3611*4882a593Smuzhiyun 		maxdepth[TAG_LORES] = 8;
3612*4882a593Smuzhiyun 		maxfmode = TAG_FMODE_4;
3613*4882a593Smuzhiyun 		defmode = DEFMODE_AGA;
3614*4882a593Smuzhiyun 		if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3615*4882a593Smuzhiyun 		    VIDEOMEMSIZE_AGA_2M)
3616*4882a593Smuzhiyun 			info->fix.smem_len = VIDEOMEMSIZE_AGA_2M;
3617*4882a593Smuzhiyun 		else
3618*4882a593Smuzhiyun 			info->fix.smem_len = VIDEOMEMSIZE_AGA_1M;
3619*4882a593Smuzhiyun 		break;
3620*4882a593Smuzhiyun #endif /* CONFIG_FB_AMIGA_AGA */
3621*4882a593Smuzhiyun 
3622*4882a593Smuzhiyun 	default:
3623*4882a593Smuzhiyun #ifdef CONFIG_FB_AMIGA_OCS
3624*4882a593Smuzhiyun 		printk("Unknown graphics chipset, defaulting to OCS\n");
3625*4882a593Smuzhiyun 		strcat(info->fix.id, "Unknown");
3626*4882a593Smuzhiyun 		goto default_chipset;
3627*4882a593Smuzhiyun #else /* CONFIG_FB_AMIGA_OCS */
3628*4882a593Smuzhiyun 		err = -ENODEV;
3629*4882a593Smuzhiyun 		goto release;
3630*4882a593Smuzhiyun #endif /* CONFIG_FB_AMIGA_OCS */
3631*4882a593Smuzhiyun 		break;
3632*4882a593Smuzhiyun 	}
3633*4882a593Smuzhiyun 
3634*4882a593Smuzhiyun 	/*
3635*4882a593Smuzhiyun 	 * Calculate the Pixel Clock Values for this Machine
3636*4882a593Smuzhiyun 	 */
3637*4882a593Smuzhiyun 
3638*4882a593Smuzhiyun 	{
3639*4882a593Smuzhiyun 	u_long tmp = DIVUL(200000000000ULL, amiga_eclock);
3640*4882a593Smuzhiyun 
3641*4882a593Smuzhiyun 	pixclock[TAG_SHRES] = (tmp + 4) / 8;	/* SHRES:  35 ns / 28 MHz */
3642*4882a593Smuzhiyun 	pixclock[TAG_HIRES] = (tmp + 2) / 4;	/* HIRES:  70 ns / 14 MHz */
3643*4882a593Smuzhiyun 	pixclock[TAG_LORES] = (tmp + 1) / 2;	/* LORES: 140 ns /  7 MHz */
3644*4882a593Smuzhiyun 	}
3645*4882a593Smuzhiyun 
3646*4882a593Smuzhiyun 	/*
3647*4882a593Smuzhiyun 	 * Replace the Tag Values with the Real Pixel Clock Values
3648*4882a593Smuzhiyun 	 */
3649*4882a593Smuzhiyun 
3650*4882a593Smuzhiyun 	for (i = 0; i < NUM_TOTAL_MODES; i++) {
3651*4882a593Smuzhiyun 		struct fb_videomode *mode = &ami_modedb[i];
3652*4882a593Smuzhiyun 		tag = mode->pixclock;
3653*4882a593Smuzhiyun 		if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
3654*4882a593Smuzhiyun 			mode->pixclock = pixclock[tag];
3655*4882a593Smuzhiyun 		}
3656*4882a593Smuzhiyun 	}
3657*4882a593Smuzhiyun 
3658*4882a593Smuzhiyun 	if (amifb_hfmin) {
3659*4882a593Smuzhiyun 		info->monspecs.hfmin = amifb_hfmin;
3660*4882a593Smuzhiyun 		info->monspecs.hfmax = amifb_hfmax;
3661*4882a593Smuzhiyun 		info->monspecs.vfmin = amifb_vfmin;
3662*4882a593Smuzhiyun 		info->monspecs.vfmax = amifb_vfmax;
3663*4882a593Smuzhiyun 	} else {
3664*4882a593Smuzhiyun 		/*
3665*4882a593Smuzhiyun 		 *  These are for a typical Amiga monitor (e.g. A1960)
3666*4882a593Smuzhiyun 		 */
3667*4882a593Smuzhiyun 		info->monspecs.hfmin = 15000;
3668*4882a593Smuzhiyun 		info->monspecs.hfmax = 38000;
3669*4882a593Smuzhiyun 		info->monspecs.vfmin = 49;
3670*4882a593Smuzhiyun 		info->monspecs.vfmax = 90;
3671*4882a593Smuzhiyun 	}
3672*4882a593Smuzhiyun 
3673*4882a593Smuzhiyun 	info->fbops = &amifb_ops;
3674*4882a593Smuzhiyun 	info->flags = FBINFO_DEFAULT;
3675*4882a593Smuzhiyun 	info->device = &pdev->dev;
3676*4882a593Smuzhiyun 
3677*4882a593Smuzhiyun 	if (!fb_find_mode(&info->var, info, mode_option, ami_modedb,
3678*4882a593Smuzhiyun 			  NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
3679*4882a593Smuzhiyun 		err = -EINVAL;
3680*4882a593Smuzhiyun 		goto release;
3681*4882a593Smuzhiyun 	}
3682*4882a593Smuzhiyun 
3683*4882a593Smuzhiyun 	fb_videomode_to_modelist(ami_modedb, NUM_TOTAL_MODES,
3684*4882a593Smuzhiyun 				 &info->modelist);
3685*4882a593Smuzhiyun 
3686*4882a593Smuzhiyun 	round_down_bpp = 0;
3687*4882a593Smuzhiyun 	chipptr = chipalloc(info->fix.smem_len + SPRITEMEMSIZE +
3688*4882a593Smuzhiyun 			    DUMMYSPRITEMEMSIZE + COPINITSIZE +
3689*4882a593Smuzhiyun 			    4 * COPLISTSIZE);
3690*4882a593Smuzhiyun 	if (!chipptr) {
3691*4882a593Smuzhiyun 		err = -ENOMEM;
3692*4882a593Smuzhiyun 		goto release;
3693*4882a593Smuzhiyun 	}
3694*4882a593Smuzhiyun 
3695*4882a593Smuzhiyun 	assignchunk(videomemory, u_long, chipptr, info->fix.smem_len);
3696*4882a593Smuzhiyun 	assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
3697*4882a593Smuzhiyun 	assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
3698*4882a593Smuzhiyun 	assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
3699*4882a593Smuzhiyun 	assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
3700*4882a593Smuzhiyun 	assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
3701*4882a593Smuzhiyun 	assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
3702*4882a593Smuzhiyun 	assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
3703*4882a593Smuzhiyun 
3704*4882a593Smuzhiyun 	/*
3705*4882a593Smuzhiyun 	 * access the videomem with writethrough cache
3706*4882a593Smuzhiyun 	 */
3707*4882a593Smuzhiyun 	info->fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
3708*4882a593Smuzhiyun 	videomemory = (u_long)ioremap_wt(info->fix.smem_start,
3709*4882a593Smuzhiyun 					 info->fix.smem_len);
3710*4882a593Smuzhiyun 	if (!videomemory) {
3711*4882a593Smuzhiyun 		dev_warn(&pdev->dev,
3712*4882a593Smuzhiyun 			 "Unable to map videomem cached writethrough\n");
3713*4882a593Smuzhiyun 		info->screen_base = ZTWO_VADDR(info->fix.smem_start);
3714*4882a593Smuzhiyun 	} else
3715*4882a593Smuzhiyun 		info->screen_base = (char *)videomemory;
3716*4882a593Smuzhiyun 
3717*4882a593Smuzhiyun 	memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
3718*4882a593Smuzhiyun 
3719*4882a593Smuzhiyun 	/*
3720*4882a593Smuzhiyun 	 * Make sure the Copper has something to do
3721*4882a593Smuzhiyun 	 */
3722*4882a593Smuzhiyun 	ami_init_copper();
3723*4882a593Smuzhiyun 
3724*4882a593Smuzhiyun 	/*
3725*4882a593Smuzhiyun 	 * Enable Display DMA
3726*4882a593Smuzhiyun 	 */
3727*4882a593Smuzhiyun 	custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
3728*4882a593Smuzhiyun 			DMAF_BLITTER | DMAF_SPRITE;
3729*4882a593Smuzhiyun 
3730*4882a593Smuzhiyun 	err = request_irq(IRQ_AMIGA_COPPER, amifb_interrupt, 0,
3731*4882a593Smuzhiyun 			  "fb vertb handler", info->par);
3732*4882a593Smuzhiyun 	if (err)
3733*4882a593Smuzhiyun 		goto disable_dma;
3734*4882a593Smuzhiyun 
3735*4882a593Smuzhiyun 	err = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
3736*4882a593Smuzhiyun 	if (err)
3737*4882a593Smuzhiyun 		goto free_irq;
3738*4882a593Smuzhiyun 
3739*4882a593Smuzhiyun 	dev_set_drvdata(&pdev->dev, info);
3740*4882a593Smuzhiyun 
3741*4882a593Smuzhiyun 	err = register_framebuffer(info);
3742*4882a593Smuzhiyun 	if (err)
3743*4882a593Smuzhiyun 		goto unset_drvdata;
3744*4882a593Smuzhiyun 
3745*4882a593Smuzhiyun 	fb_info(info, "%s frame buffer device, using %dK of video memory\n",
3746*4882a593Smuzhiyun 		info->fix.id, info->fix.smem_len>>10);
3747*4882a593Smuzhiyun 
3748*4882a593Smuzhiyun 	return 0;
3749*4882a593Smuzhiyun 
3750*4882a593Smuzhiyun unset_drvdata:
3751*4882a593Smuzhiyun 	fb_dealloc_cmap(&info->cmap);
3752*4882a593Smuzhiyun free_irq:
3753*4882a593Smuzhiyun 	free_irq(IRQ_AMIGA_COPPER, info->par);
3754*4882a593Smuzhiyun disable_dma:
3755*4882a593Smuzhiyun 	custom.dmacon = DMAF_ALL | DMAF_MASTER;
3756*4882a593Smuzhiyun 	if (videomemory)
3757*4882a593Smuzhiyun 		iounmap((void *)videomemory);
3758*4882a593Smuzhiyun 	chipfree();
3759*4882a593Smuzhiyun release:
3760*4882a593Smuzhiyun 	framebuffer_release(info);
3761*4882a593Smuzhiyun 	return err;
3762*4882a593Smuzhiyun }
3763*4882a593Smuzhiyun 
3764*4882a593Smuzhiyun 
amifb_remove(struct platform_device * pdev)3765*4882a593Smuzhiyun static int __exit amifb_remove(struct platform_device *pdev)
3766*4882a593Smuzhiyun {
3767*4882a593Smuzhiyun 	struct fb_info *info = dev_get_drvdata(&pdev->dev);
3768*4882a593Smuzhiyun 
3769*4882a593Smuzhiyun 	unregister_framebuffer(info);
3770*4882a593Smuzhiyun 	fb_dealloc_cmap(&info->cmap);
3771*4882a593Smuzhiyun 	free_irq(IRQ_AMIGA_COPPER, info->par);
3772*4882a593Smuzhiyun 	custom.dmacon = DMAF_ALL | DMAF_MASTER;
3773*4882a593Smuzhiyun 	if (videomemory)
3774*4882a593Smuzhiyun 		iounmap((void *)videomemory);
3775*4882a593Smuzhiyun 	chipfree();
3776*4882a593Smuzhiyun 	framebuffer_release(info);
3777*4882a593Smuzhiyun 	amifb_video_off();
3778*4882a593Smuzhiyun 	return 0;
3779*4882a593Smuzhiyun }
3780*4882a593Smuzhiyun 
3781*4882a593Smuzhiyun static struct platform_driver amifb_driver = {
3782*4882a593Smuzhiyun 	.remove = __exit_p(amifb_remove),
3783*4882a593Smuzhiyun 	.driver   = {
3784*4882a593Smuzhiyun 		.name	= "amiga-video",
3785*4882a593Smuzhiyun 	},
3786*4882a593Smuzhiyun };
3787*4882a593Smuzhiyun 
3788*4882a593Smuzhiyun module_platform_driver_probe(amifb_driver, amifb_probe);
3789*4882a593Smuzhiyun 
3790*4882a593Smuzhiyun MODULE_LICENSE("GPL");
3791*4882a593Smuzhiyun MODULE_ALIAS("platform:amiga-video");
3792