1/*
2 *  linux/drivers/video/ilbm.c -- Low level frame buffer operations for
3 *				  interleaved bitplanes � la Amiga
4 *
5 *	Created 5 Apr 1997 by Geert Uytterhoeven
6 *
7 *  This file is subject to the terms and conditions of the GNU General Public
8 *  License.  See the file COPYING in the main directory of this archive for
9 *  more details.
10 */
11
12#include <linux/module.h>
13#include <linux/tty.h>
14#include <linux/console.h>
15#include <linux/string.h>
16#include <linux/fb.h>
17
18#include <video/fbcon.h>
19#include <video/fbcon-ilbm.h>
20
21
22    /*
23     *  Interleaved bitplanes � la Amiga
24     *
25     *  This code heavily relies on the fact that
26     *
27     *      next_line == interleave == next_plane*bits_per_pixel
28     *
29     *  But maybe it can be merged with the code for normal bitplanes without
30     *  much performance loss?
31     */
32
33void fbcon_ilbm_setup(struct display *p)
34{
35    if (p->line_length) {
36	p->next_line = p->line_length*p->var.bits_per_pixel;
37	p->next_plane = p->line_length;
38    } else {
39	p->next_line = p->type_aux;
40	p->next_plane = p->type_aux/p->var.bits_per_pixel;
41    }
42}
43
44void fbcon_ilbm_bmove(struct display *p, int sy, int sx, int dy, int dx,
45		      int height, int width)
46{
47    if (sx == 0 && dx == 0 && width == p->next_plane)
48	fb_memmove(p->screen_base+dy*fontheight(p)*p->next_line,
49		  p->screen_base+sy*fontheight(p)*p->next_line,
50		  height*fontheight(p)*p->next_line);
51    else {
52	u8 *src, *dest;
53	u_int i;
54
55	if (dy <= sy) {
56	    src = p->screen_base+sy*fontheight(p)*p->next_line+sx;
57	    dest = p->screen_base+dy*fontheight(p)*p->next_line+dx;
58	    for (i = p->var.bits_per_pixel*height*fontheight(p); i--;) {
59		fb_memmove(dest, src, width);
60		src += p->next_plane;
61		dest += p->next_plane;
62	    }
63	} else {
64	    src = p->screen_base+(sy+height)*fontheight(p)*p->next_line+sx;
65	    dest = p->screen_base+(dy+height)*fontheight(p)*p->next_line+dx;
66	    for (i = p->var.bits_per_pixel*height*fontheight(p); i--;) {
67		src -= p->next_plane;
68		dest -= p->next_plane;
69		fb_memmove(dest, src, width);
70	    }
71	}
72    }
73}
74
75void fbcon_ilbm_clear(struct vc_data *conp, struct display *p, int sy, int sx,
76		      int height, int width)
77{
78    u8 *dest;
79    u_int i, rows;
80    int bg, bg0;
81
82    dest = p->screen_base+sy*fontheight(p)*p->next_line+sx;
83
84    bg0 = attr_bgcol_ec(p,conp);
85    for (rows = height*fontheight(p); rows--;) {
86	bg = bg0;
87	for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
88	    if (bg & 1)
89		fb_memset255(dest, width);
90	    else
91		fb_memclear(dest, width);
92	    bg >>= 1;
93	}
94    }
95}
96
97void fbcon_ilbm_putc(struct vc_data *conp, struct display *p, int c, int yy,
98		     int xx)
99{
100    u8 *dest, *cdat;
101    u_int rows, i;
102    u8 d;
103    int fg0, bg0, fg, bg;
104
105    dest = p->screen_base+yy*fontheight(p)*p->next_line+xx;
106    cdat = p->fontdata+(c&p->charmask)*fontheight(p);
107    fg0 = attr_fgcol(p,c);
108    bg0 = attr_bgcol(p,c);
109
110    for (rows = fontheight(p); rows--;) {
111	d = *cdat++;
112	fg = fg0;
113	bg = bg0;
114	for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
115	    if (bg & 1){
116		if (fg & 1)
117		    *dest = 0xff;
118		else
119		    *dest = ~d;
120	    }else{
121		if (fg & 1)
122		    *dest = d;
123		else
124		    *dest = 0x00;
125	    }
126	    bg >>= 1;
127	    fg >>= 1;
128	}
129    }
130}
131
132    /*
133     *  I've split the console character loop in two parts:
134     *
135     *      - slow version: this blits one character at a time
136     *
137     *      - fast version: this blits 4 characters at a time at a longword
138     *			    aligned address, to reduce the number of expensive
139     *			    Chip RAM accesses.
140     *
141     *  Experiments on my A4000/040 revealed that this makes a console switch
142     *  on a 640x400 screen with 256 colors about 3 times faster.
143     *
144     *  -- Geert
145     */
146
147void fbcon_ilbm_putcs(struct vc_data *conp, struct display *p,
148		      const unsigned short *s, int count, int yy, int xx)
149{
150    u8 *dest0, *dest, *cdat1, *cdat2, *cdat3, *cdat4;
151    u_int rows, i;
152    u16 c1, c2, c3, c4;
153    u32 d;
154    int fg0, bg0, fg, bg;
155
156    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
157    c1 = scr_readw(s);
158    fg0 = attr_fgcol(p, c1);
159    bg0 = attr_bgcol(p, c1);
160
161    while (count--)
162	if (xx&3 || count < 3) {	/* Slow version */
163	    c1 = scr_readw(s++) & p->charmask;
164	    dest = dest0++;
165	    xx++;
166
167	    cdat1 = p->fontdata+c1*fontheight(p);
168	    for (rows = fontheight(p); rows--;) {
169		d = *cdat1++;
170		fg = fg0;
171		bg = bg0;
172		for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
173		    if (bg & 1){
174			if (fg & 1)
175			    *dest = 0xff;
176			else
177			    *dest = ~d;
178		    }else{
179			if (fg & 1)
180			    *dest = d;
181			else
182			    *dest = 0x00;
183		    }
184		    bg >>= 1;
185		    fg >>= 1;
186		}
187	    }
188	} else {		/* Fast version */
189	    c1 = scr_readw(&s[0]) & p->charmask;
190	    c2 = scr_readw(&s[1]) & p->charmask;
191	    c3 = scr_readw(&s[2]) & p->charmask;
192	    c4 = scr_readw(&s[3]) & p->charmask;
193
194	    dest = dest0;
195	    cdat1 = p->fontdata+c1*fontheight(p);
196	    cdat2 = p->fontdata+c2*fontheight(p);
197	    cdat3 = p->fontdata+c3*fontheight(p);
198	    cdat4 = p->fontdata+c4*fontheight(p);
199	    for (rows = fontheight(p); rows--;) {
200#if defined(__BIG_ENDIAN)
201		d = *cdat1++<<24 | *cdat2++<<16 | *cdat3++<<8 | *cdat4++;
202#elif defined(__LITTLE_ENDIAN)
203		d = *cdat1++ | *cdat2++<<8 | *cdat3++<<16 | *cdat4++<<24;
204#else
205#error FIXME: No endianness??
206#endif
207		fg = fg0;
208		bg = bg0;
209		for (i = p->var.bits_per_pixel; i--; dest += p->next_plane) {
210		    if (bg & 1){
211			if (fg & 1)
212			    *(u32 *)dest = 0xffffffff;
213			else
214			    *(u32 *)dest = ~d;
215		    }else{
216			if (fg & 1)
217			    *(u32 *)dest = d;
218			else
219			    *(u32 *)dest = 0x00000000;
220		    }
221		    bg >>= 1;
222		    fg >>= 1;
223		}
224	    }
225	    s += 4;
226	    dest0 += 4;
227	    xx += 4;
228	    count -= 3;
229	}
230}
231
232void fbcon_ilbm_revc(struct display *p, int xx, int yy)
233{
234    u8 *dest, *dest0;
235    u_int rows, i;
236    int mask;
237
238    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
239    mask = p->fgcol ^ p->bgcol;
240
241    /*
242     *  This should really obey the individual character's
243     *  background and foreground colors instead of simply
244     *  inverting.
245     */
246
247    for (i = p->var.bits_per_pixel; i--; dest0 += p->next_plane) {
248	if (mask & 1) {
249	    dest = dest0;
250	    for (rows = fontheight(p); rows--; dest += p->next_line)
251		*dest = ~*dest;
252	}
253	mask >>= 1;
254    }
255}
256
257
258    /*
259     *  `switch' for the low level operations
260     */
261
262struct display_switch fbcon_ilbm = {
263    setup:		fbcon_ilbm_setup,
264    bmove:		fbcon_ilbm_bmove,
265    clear:		fbcon_ilbm_clear,
266    putc:		fbcon_ilbm_putc,
267    putcs:		fbcon_ilbm_putcs,
268    revc:		fbcon_ilbm_revc,
269    fontwidthmask:	FONTWIDTH(8)
270};
271
272
273#ifdef MODULE
274MODULE_LICENSE("GPL");
275
276int init_module(void)
277{
278    return 0;
279}
280
281void cleanup_module(void)
282{}
283#endif /* MODULE */
284
285
286    /*
287     *  Visible symbols for modules
288     */
289
290EXPORT_SYMBOL(fbcon_ilbm);
291EXPORT_SYMBOL(fbcon_ilbm_setup);
292EXPORT_SYMBOL(fbcon_ilbm_bmove);
293EXPORT_SYMBOL(fbcon_ilbm_clear);
294EXPORT_SYMBOL(fbcon_ilbm_putc);
295EXPORT_SYMBOL(fbcon_ilbm_putcs);
296EXPORT_SYMBOL(fbcon_ilbm_revc);
297