1/*
2 *  linux/drivers/video/afb.c -- Low level frame buffer operations for
3 *				 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-afb.h>
20
21
22    /*
23     *  Bitplanes � la Amiga
24     */
25
26static u8 expand_table[1024] = {
27    /*  bg = fg = 0 */
28    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
29    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
30    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
31    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
39    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
41    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
45    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
50    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
51    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
52    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60    /* bg = 0, fg = 1 */
61    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
62    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
63    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
64    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
65    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
66    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
67    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
68    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
69    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
70    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
71    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
72    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
73    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
74    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
75    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
76    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
77    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
78    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
79    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
80    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
81    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
82    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
83    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
84    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
85    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
86    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
87    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
88    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
89    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
90    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
91    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
92    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
93    /* bg = 1, fg = 0 */
94    0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
95    0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
96    0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
97    0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
98    0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8,
99    0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0,
100    0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8,
101    0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0,
102    0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8,
103    0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0,
104    0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8,
105    0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0,
106    0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98,
107    0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90,
108    0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88,
109    0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80,
110    0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
111    0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
112    0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68,
113    0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60,
114    0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58,
115    0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50,
116    0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48,
117    0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40,
118    0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38,
119    0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30,
120    0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28,
121    0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20,
122    0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
123    0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
124    0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
125    0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
126    /* bg = fg = 1 */
127    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
128    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
129    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
130    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
131    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
132    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
133    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
134    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
135    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
136    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
137    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
138    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
139    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
140    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
141    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
142    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
143    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
144    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
145    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
146    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
147    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
148    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
149    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
150    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
151    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
152    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
153    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
154    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
155    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
156    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
157    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
158    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
159};
160
161void fbcon_afb_setup(struct display *p)
162{
163    if (p->line_length)
164	p->next_line = p->line_length;
165    else
166	p->next_line = p->var.xres_virtual>>3;
167    p->next_plane = p->var.yres_virtual*p->next_line;
168}
169
170void fbcon_afb_bmove(struct display *p, int sy, int sx, int dy, int dx,
171		     int height, int width)
172{
173    u8 *src, *dest, *src0, *dest0;
174    u_short i, j;
175
176    if (sx == 0 && dx == 0 && width == p->next_line) {
177	src = p->screen_base+sy*fontheight(p)*width;
178	dest = p->screen_base+dy*fontheight(p)*width;
179	i = p->var.bits_per_pixel;
180	do {
181	    fb_memmove(dest, src, height*fontheight(p)*width);
182	    src += p->next_plane;
183	    dest += p->next_plane;
184	} while (--i);
185    } else if (dy <= sy) {
186	src0 = p->screen_base+sy*fontheight(p)*p->next_line+sx;
187	dest0 = p->screen_base+dy*fontheight(p)*p->next_line+dx;
188	i = p->var.bits_per_pixel;
189	do {
190	    src = src0;
191	    dest = dest0;
192	    j = height*fontheight(p);
193	    do {
194	        fb_memmove(dest, src, width);
195	        src += p->next_line;
196	        dest += p->next_line;
197	    } while (--j);
198	    src0 += p->next_plane;
199	    dest0 += p->next_plane;
200	} while (--i);
201    } else {
202	src0 = p->screen_base+(sy+height)*fontheight(p)*p->next_line+sx;
203	dest0 = p->screen_base+(dy+height)*fontheight(p)*p->next_line+dx;
204	i = p->var.bits_per_pixel;
205	do {
206	    src = src0;
207	    dest = dest0;
208	    j = height*fontheight(p);
209	    do {
210	        src -= p->next_line;
211	        dest -= p->next_line;
212	        fb_memmove(dest, src, width);
213	    } while (--j);
214	    src0 += p->next_plane;
215	    dest0 += p->next_plane;
216	} while (--i);
217    }
218}
219
220void fbcon_afb_clear(struct vc_data *conp, struct display *p, int sy, int sx,
221		     int height, int width)
222{
223    u8 *dest, *dest0;
224    u_short i, j;
225    int bg;
226
227    dest0 = p->screen_base+sy*fontheight(p)*p->next_line+sx;
228
229    bg = attr_bgcol_ec(p,conp);
230    i = p->var.bits_per_pixel;
231    do {
232	dest = dest0;
233	j = height*fontheight(p);
234	do {
235	    if (bg & 1)
236	        fb_memset255(dest, width);
237	    else
238	        fb_memclear(dest, width);
239	    dest += p->next_line;
240	} while (--j);
241	bg >>= 1;
242	dest0 += p->next_plane;
243    } while (--i);
244}
245
246void fbcon_afb_putc(struct vc_data *conp, struct display *p, int c, int yy,
247		    int xx)
248{
249    u8 *dest, *dest0, *cdat, *cdat0, *expand;
250    u_short i, j;
251    int fg, bg;
252
253    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
254    cdat0 = p->fontdata+(c&p->charmask)*fontheight(p);
255    fg = attr_fgcol(p,c);
256    bg = attr_bgcol(p,c);
257
258    i = p->var.bits_per_pixel;
259    do {
260	dest = dest0;
261	cdat = cdat0;
262	expand = expand_table;
263	if (bg & 1)
264	    expand += 512;
265	if (fg & 1)
266	    expand += 256;
267	j = fontheight(p);
268	do {
269	    *dest = expand[*cdat++];
270	    dest += p->next_line;
271	} while (--j);
272	bg >>= 1;
273	fg >>= 1;
274	dest0 += p->next_plane;
275    } while (--i);
276}
277
278    /*
279     *  I've split the console character loop in two parts
280     *  (cfr. fbcon_putcs_ilbm())
281     */
282
283void fbcon_afb_putcs(struct vc_data *conp, struct display *p,
284		     const unsigned short *s, int count, int yy, int xx)
285{
286    u8 *dest, *dest0, *dest1, *expand;
287    u8 *cdat1, *cdat2, *cdat3, *cdat4, *cdat10, *cdat20, *cdat30, *cdat40;
288    u_short i, j;
289    u16 c1, c2, c3, c4;
290    int fg0, bg0, fg, bg;
291
292    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
293    c1 = scr_readw(s);
294    fg0 = attr_fgcol(p, c1);
295    bg0 = attr_bgcol(p, c1);
296
297    while (count--)
298	if (xx&3 || count < 3) {	/* Slow version */
299	    c1 = scr_readw(s++) & p->charmask;
300	    dest1 = dest0++;
301	    xx++;
302
303	    cdat10 = p->fontdata+c1*fontheight(p);
304	    fg = fg0;
305	    bg = bg0;
306
307	    i = p->var.bits_per_pixel;
308	    do {
309	        dest = dest1;
310	        cdat1 = cdat10;
311		expand = expand_table;
312		if (bg & 1)
313		    expand += 512;
314		if (fg & 1)
315		    expand += 256;
316		j = fontheight(p);
317		do {
318		    *dest = expand[*cdat1++];
319		    dest += p->next_line;
320	        } while (--j);
321	        bg >>= 1;
322	        fg >>= 1;
323		dest1 += p->next_plane;
324	    } while (--i);
325	} else {			/* Fast version */
326	    c1 = scr_readw(&s[0]) & p->charmask;
327	    c2 = scr_readw(&s[1]) & p->charmask;
328	    c3 = scr_readw(&s[2]) & p->charmask;
329	    c4 = scr_readw(&s[3]) & p->charmask;
330
331	    dest1 = dest0;
332	    cdat10 = p->fontdata+c1*fontheight(p);
333	    cdat20 = p->fontdata+c2*fontheight(p);
334	    cdat30 = p->fontdata+c3*fontheight(p);
335	    cdat40 = p->fontdata+c4*fontheight(p);
336	    fg = fg0;
337	    bg = bg0;
338
339	    i = p->var.bits_per_pixel;
340	    do {
341	        dest = dest1;
342	        cdat1 = cdat10;
343	        cdat2 = cdat20;
344	        cdat3 = cdat30;
345	        cdat4 = cdat40;
346		expand = expand_table;
347		if (bg & 1)
348		    expand += 512;
349		if (fg & 1)
350		    expand += 256;
351		j = fontheight(p);
352	        do {
353#if defined(__BIG_ENDIAN)
354		    *(u32 *)dest = expand[*cdat1++]<<24 |
355				   expand[*cdat2++]<<16 |
356				   expand[*cdat3++]<<8 |
357				   expand[*cdat4++];
358#elif defined(__LITTLE_ENDIAN)
359		    *(u32 *)dest = expand[*cdat1++] |
360				   expand[*cdat2++]<<8 |
361				   expand[*cdat3++]<<16 |
362				   expand[*cdat4++]<<24;
363#else
364#error FIXME: No endianness??
365#endif
366		    dest += p->next_line;
367	        } while (--j);
368	        bg >>= 1;
369	        fg >>= 1;
370		dest1 += p->next_plane;
371	    } while (--i);
372	    s += 4;
373	    dest0 += 4;
374	    xx += 4;
375	    count -= 3;
376	}
377}
378
379void fbcon_afb_revc(struct display *p, int xx, int yy)
380{
381    u8 *dest, *dest0;
382    u_short i, j;
383    int mask;
384
385    dest0 = p->screen_base+yy*fontheight(p)*p->next_line+xx;
386    mask = p->fgcol ^ p->bgcol;
387
388    /*
389     *  This should really obey the individual character's
390     *  background and foreground colors instead of simply
391     *  inverting.
392     */
393
394    i = p->var.bits_per_pixel;
395    do {
396	if (mask & 1) {
397	    dest = dest0;
398	    j = fontheight(p);
399	    do {
400	        *dest = ~*dest;
401		dest += p->next_line;
402	    } while (--j);
403	}
404	mask >>= 1;
405	dest0 += p->next_plane;
406    } while (--i);
407}
408
409
410    /*
411     *  `switch' for the low level operations
412     */
413
414struct display_switch fbcon_afb = {
415    setup:		fbcon_afb_setup,
416    bmove:		fbcon_afb_bmove,
417    clear:		fbcon_afb_clear,
418    putc:		fbcon_afb_putc,
419    putcs:		fbcon_afb_putcs,
420    revc:		fbcon_afb_revc,
421    fontwidthmask:	FONTWIDTH(8)
422};
423
424
425#ifdef MODULE
426MODULE_LICENSE("GPL");
427
428int init_module(void)
429{
430    return 0;
431}
432
433void cleanup_module(void)
434{}
435#endif /* MODULE */
436
437
438    /*
439     *  Visible symbols for modules
440     */
441
442EXPORT_SYMBOL(fbcon_afb);
443EXPORT_SYMBOL(fbcon_afb_setup);
444EXPORT_SYMBOL(fbcon_afb_bmove);
445EXPORT_SYMBOL(fbcon_afb_clear);
446EXPORT_SYMBOL(fbcon_afb_putc);
447EXPORT_SYMBOL(fbcon_afb_putcs);
448EXPORT_SYMBOL(fbcon_afb_revc);
449