File: /usr/src/linux/drivers/video/fbcon-iplan2p2.c

1     /*
2      *  linux/drivers/video/fbcon-iplan2p2.c -- Low level frame buffer operations
3      *				  for interleaved bitplanes à la Atari (2
4      *				  planes, 2 bytes interleave)
5      *
6      *	Created 5 Apr 1997 by Geert Uytterhoeven
7      *
8      *  This file is subject to the terms and conditions of the GNU General Public
9      *  License.  See the file COPYING in the main directory of this archive for
10      *  more details.
11      */
12     
13     #include <linux/module.h>
14     #include <linux/tty.h>
15     #include <linux/console.h>
16     #include <linux/string.h>
17     #include <linux/fb.h>
18     
19     #include <asm/byteorder.h>
20     
21     #ifdef __mc68000__
22     #include <asm/setup.h>
23     #endif
24     
25     #include <video/fbcon.h>
26     #include <video/fbcon-iplan2p2.h>
27     
28     
29         /*
30          *  Interleaved bitplanes à la Atari (2 planes, 2 bytes interleave)
31          */
32     
33     /* Increment/decrement 2 plane addresses */
34     
35     #define	INC_2P(p)	do { if (!((long)(++(p)) & 1)) (p) += 2; } while(0)
36     #define	DEC_2P(p)	do { if ((long)(--(p)) & 1) (p) -= 2; } while(0)
37     
38         /*  Convert a standard 4 bit color to our 2 bit color assignment:
39          *  If at least two RGB channels are active, the low bit is turned on;
40          *  The intensity bit (b3) is shifted into b1.
41          */
42     
43     static const u8 color_2p[] = { 0, 0, 0, 1, 0, 1, 1, 1, 2, 2, 2, 3, 2, 3, 3, 3 };
44     #define	COLOR_2P(c)	color_2p[c]
45     
46     /* Perform the m68k movepw operation.  */
47     static inline void movepw(u8 *d, u16 val)
48     {
49     #if defined __mc68000__ && !defined CPU_M68060_ONLY
50         asm volatile ("movepw %1,%0@(0)" : : "a" (d), "d" (val));
51     #else
52         d[0] = (val >> 16) & 0xff;
53         d[2] = val & 0xff;
54     #endif
55     }
56     
57     /* Sets the bytes in the visible column at d, height h, to the value
58      * val for a 2 plane screen. The bits of the color in 'color' are
59      * moved (8 times) to the respective bytes. This means:
60      *
61      * for(h times; d += bpr)
62      *   *d     = (color & 1) ? 0xff : 0;
63      *   *(d+2) = (color & 2) ? 0xff : 0;
64      */
65     
66     static __inline__ void memclear_2p_col(void *d, size_t h, u16 val, int bpr)
67     {
68         u8 *dd = d;
69         do {
70     	movepw(dd, val);
71     	dd += bpr;
72         } while (--h);
73     }
74     
75     /* Sets a 2 plane region from 'd', length 'count' bytes, to the color
76      * in val1. 'd' has to be an even address and count must be divisible
77      * by 8, because only whole words and all planes are accessed. I.e.:
78      *
79      * for(count/4 times)
80      *   *d     = *(d+1) = (color & 1) ? 0xff : 0;
81      *   *(d+2) = *(d+3) = (color & 2) ? 0xff : 0;
82      */
83     
84     static __inline__ void memset_even_2p(void *d, size_t count, u32 val)
85     {
86         u32 *dd = d;
87     
88         count /= 4;
89         while (count--)
90     	*dd++ = val;
91     }
92     
93     /* Copies a 2 plane column from 's', height 'h', to 'd'. */
94     
95     static __inline__ void memmove_2p_col (void *d, void *s, int h, int bpr)
96     {
97         u8 *dd = d, *ss = s;
98     
99         while (h--) {
100     	dd[0] = ss[0];
101     	dd[2] = ss[2];
102     	dd += bpr;
103     	ss += bpr;
104         }
105     }
106     
107     
108     /* This expands a 2 bit color into a short for movepw (2 plane) operations. */
109     
110     static const u16 two2byte[] = {
111         0x0000, 0xff00, 0x00ff, 0xffff
112     };
113     
114     static __inline__ u16 expand2w(u8 c)
115     {
116         return two2byte[c];
117     }
118     
119     
120     /* This expands a 2 bit color into one long for a movel operation
121      * (2 planes).
122      */
123     
124     static const u32 two2word[] = {
125     #ifndef __LITTLE_ENDIAN
126         0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
127     #else
128         0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
129     #endif
130     };
131     
132     static __inline__ u32 expand2l(u8 c)
133     {
134         return two2word[c];
135     }
136     
137     
138     /* This duplicates a byte 2 times into a short. */
139     
140     static __inline__ u16 dup2w(u8 c)
141     {
142         u16 rv;
143     
144         rv = c;
145         rv |= c << 8;
146         return rv;
147     }
148     
149     
150     void fbcon_iplan2p2_setup(struct display *p)
151     {
152         p->next_line = p->var.xres_virtual>>2;
153         p->next_plane = 2;
154     }
155     
156     void fbcon_iplan2p2_bmove(struct display *p, int sy, int sx, int dy, int dx,
157     			  int height, int width)
158     {
159         /*  bmove() has to distinguish two major cases: If both, source and
160          *  destination, start at even addresses or both are at odd
161          *  addresses, just the first odd and last even column (if present)
162          *  require special treatment (memmove_col()). The rest between
163          *  then can be copied by normal operations, because all adjacent
164          *  bytes are affected and are to be stored in the same order.
165          *    The pathological case is when the move should go from an odd
166          *  address to an even or vice versa. Since the bytes in the plane
167          *  words must be assembled in new order, it seems wisest to make
168          *  all movements by memmove_col().
169          */
170     
171         if (sx == 0 && dx == 0 && width * 2 == p->next_line) {
172     	/*  Special (but often used) case: Moving whole lines can be
173     	 *  done with memmove()
174     	 */
175     	fb_memmove(p->screen_base + dy * p->next_line * fontheight(p),
176     		  p->screen_base + sy * p->next_line * fontheight(p),
177     		  p->next_line * height * fontheight(p));
178         } else {
179     	int rows, cols;
180     	u8 *src;
181     	u8 *dst;
182     	int bytes = p->next_line;
183     	int linesize;
184     	u_int colsize;
185     	u_int upwards  = (dy < sy) || (dy == sy && dx < sx);
186     
187     	if (fontheightlog(p)) {
188     	    linesize = bytes << fontheightlog(p);
189     	    colsize = height << fontheightlog(p);
190     	} else {
191     	    linesize = bytes * fontheight(p);
192     	    colsize = height * fontheight(p);
193     	}
194     	if ((sx & 1) == (dx & 1)) {
195     	    /* odd->odd or even->even */
196     	    if (upwards) {
197     		src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
198     		dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
199     		if (sx & 1) {
200     		    memmove_2p_col(dst, src, colsize, bytes);
201     		    src += 3;
202     		    dst += 3;
203     		    --width;
204     		}
205     		if (width > 1) {
206     		    for (rows = colsize; rows > 0; --rows) {
207     			fb_memmove(dst, src, (width>>1)*4);
208     			src += bytes;
209     			dst += bytes;
210     		    }
211     		}
212     		if (width & 1) {
213     		    src -= colsize * bytes;
214     		    dst -= colsize * bytes;
215     		    memmove_2p_col(dst + (width>>1)*4, src + (width>>1)*4,
216     				   colsize, bytes);
217     		}
218     	    } else {
219     		if (!((sx+width-1) & 1)) {
220     		    src = p->screen_base + sy * linesize + ((sx+width-1)>>1)*4;
221     		    dst = p->screen_base + dy * linesize + ((dx+width-1)>>1)*4;
222     		    memmove_2p_col(dst, src, colsize, bytes);
223     		    --width;
224     		}
225     		src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
226     		dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
227     		if (width > 1) {
228     		    src += colsize * bytes + (sx & 1)*3;
229     		    dst += colsize * bytes + (sx & 1)*3;
230     		    for(rows = colsize; rows > 0; --rows) {
231     			src -= bytes;
232     			dst -= bytes;
233     			fb_memmove(dst, src, (width>>1)*4);
234     		    }
235     		}
236     		if (width & 1)
237     		    memmove_2p_col(dst-3, src-3, colsize, bytes);
238     	    }
239     	} else {
240     	    /* odd->even or even->odd */
241     	    if (upwards) {
242     		src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
243     		dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
244     		for (cols = width; cols > 0; --cols) {
245     		    memmove_2p_col(dst, src, colsize, bytes);
246     		    INC_2P(src);
247     		    INC_2P(dst);
248     		}
249     	    } else {
250     		sx += width-1;
251     		dx += width-1;
252     		src = p->screen_base + sy * linesize + (sx>>1)*4 + (sx & 1);
253     		dst = p->screen_base + dy * linesize + (dx>>1)*4 + (dx & 1);
254     		for(cols = width; cols > 0; --cols) {
255     		    memmove_2p_col(dst, src, colsize, bytes);
256     		    DEC_2P(src);
257     		    DEC_2P(dst);
258     		}
259     	    }
260     	}
261         }
262     }
263     
264     void fbcon_iplan2p2_clear(struct vc_data *conp, struct display *p, int sy,
265     			  int sx, int height, int width)
266     {
267         u32 offset;
268         u8 *start;
269         int rows;
270         int bytes = p->next_line;
271         int lines;
272         u32 size;
273         u32 cval;
274         u16 pcval;
275     
276         cval = expand2l (COLOR_2P (attr_bgcol_ec(p,conp)));
277     
278         if (fontheightlog(p))
279     	lines = height << fontheightlog(p);
280         else
281     	lines = height * fontheight(p);
282     
283         if (sx == 0 && width * 2 == bytes) {
284     	if (fontheightlog(p))
285     	    offset = (sy * bytes) << fontheightlog(p);
286     	else
287     	    offset = sy * bytes * fontheight(p);
288     	size = lines * bytes;
289     	memset_even_2p(p->screen_base+offset, size, cval);
290         } else {
291     	if (fontheightlog(p))
292     	    offset = ((sy * bytes) << fontheightlog(p)) + (sx>>1)*4 + (sx & 1);
293     	else
294     	    offset = sy * bytes * fontheight(p) + (sx>>1)*4 + (sx & 1);
295     	start = p->screen_base + offset;
296     	pcval = expand2w(COLOR_2P(attr_bgcol_ec(p,conp)));
297     
298     	/*  Clears are split if the region starts at an odd column or
299     	 *  end at an even column. These extra columns are spread
300     	 *  across the interleaved planes. All in between can be
301     	 *  cleared by normal fb_memclear_small(), because both bytes of
302     	 *  the single plane words are affected.
303     	 */
304     
305     	if (sx & 1) {
306     	    memclear_2p_col(start, lines, pcval, bytes);
307     	    start += 3;
308     	    width--;
309     	}
310     	if (width & 1) {
311     	    memclear_2p_col(start + (width>>1)*4, lines, pcval, bytes);
312     	    width--;
313     	}
314     	if (width) {
315     	    for (rows = lines; rows-- ; start += bytes)
316     	    memset_even_2p(start, width*2, cval);
317     	}
318         }
319     }
320     
321     void fbcon_iplan2p2_putc(struct vc_data *conp, struct display *p, int c,
322     			 int yy, int xx)
323     {
324         u8 *dest;
325         u8 *cdat;
326         int rows;
327         int bytes = p->next_line;
328         u16 eorx, fgx, bgx, fdx;
329     
330         if (fontheightlog(p)) {
331     	dest = (p->screen_base + ((yy * bytes) << fontheightlog(p)) +
332     		(xx>>1)*4 + (xx & 1));
333     	cdat = p->fontdata + ((c & p->charmask) << fontheightlog(p));
334         } else {
335     	dest = (p->screen_base + yy * bytes * fontheight(p) +
336     		(xx>>1)*4 + (xx & 1));
337     	cdat = p->fontdata + (c & p->charmask) * fontheight(p);
338         }
339     
340         fgx = expand2w(COLOR_2P(attr_fgcol(p,c)));
341         bgx = expand2w(COLOR_2P(attr_bgcol(p,c)));
342         eorx = fgx ^ bgx;
343     
344         for (rows = fontheight(p) ; rows-- ; dest += bytes) {
345     	fdx = dup2w(*cdat++);
346     	movepw(dest, (fdx & eorx) ^ bgx);
347         }
348     }
349     
350     void fbcon_iplan2p2_putcs(struct vc_data *conp, struct display *p,
351     			  const unsigned short *s, int count, int yy, int xx)
352     {
353         u8 *dest, *dest0;
354         u8 *cdat;
355         u16 c;
356         int rows;
357         int bytes;
358         u16 eorx, fgx, bgx, fdx;
359     
360         bytes = p->next_line;
361         if (fontheightlog(p))
362     	dest0 = (p->screen_base + ((yy * bytes) << fontheightlog(p)) +
363     		 (xx>>1)*4 + (xx & 1));
364         else
365     	dest0 = (p->screen_base + yy * bytes * fontheight(p) +
366     		 (xx>>1)*4 + (xx & 1));
367         fgx = expand2w(COLOR_2P(attr_fgcol(p,scr_readw(s))));
368         bgx = expand2w(COLOR_2P(attr_bgcol(p,scr_readw(s))));
369         eorx = fgx ^ bgx;
370     
371         while (count--) {
372     	c = scr_readw(s++) & p->charmask;
373     	if (fontheightlog(p))
374     	    cdat = p->fontdata + (c << fontheightlog(p));
375     	else
376     	    cdat = p->fontdata + c * fontheight(p);
377     
378     	for (rows = fontheight(p), dest = dest0; rows-- ; dest += bytes) {
379     	    fdx = dup2w(*cdat++);
380     	    movepw(dest, (fdx & eorx) ^ bgx);
381     	}
382     	INC_2P(dest0);
383         }
384     }
385     
386     void fbcon_iplan2p2_revc(struct display *p, int xx, int yy)
387     {
388         u8 *dest;
389         int j;
390         int bytes;
391     
392         if (fontheightlog(p))
393     	dest = (p->screen_base + ((yy * p->next_line) << fontheightlog(p)) +
394     		(xx>>1)*4 + (xx & 1));
395         else
396     	dest = (p->screen_base + yy * p->next_line * fontheight(p) +
397     		(xx>>1)*4 + (xx & 1));
398         j = fontheight(p);
399         bytes = p->next_line;
400         while (j--) {
401     	/*  This should really obey the individual character's
402     	 *  background and foreground colors instead of simply
403     	 *  inverting.
404     	 */
405     	dest[0] = ~dest[0];
406     	dest[2] = ~dest[2];
407     	dest += bytes;
408         }
409     }
410     
411     void fbcon_iplan2p2_clear_margins(struct vc_data *conp, struct display *p,
412     				  int bottom_only)
413     {
414         u32 offset;
415         int bytes;
416         int lines;
417         u32 cval;
418     
419     /* No need to handle right margin, cannot occur with fontwidth == 8 */
420     
421         bytes = p->next_line;
422         if (fontheightlog(p)) {
423     	lines = p->var.yres - (conp->vc_rows << fontheightlog(p));
424     	offset = ((p->yscroll + conp->vc_rows) * bytes) << fontheightlog(p);
425         } else {
426     	lines = p->var.yres - conp->vc_rows * fontheight(p);
427     	offset = (p->yscroll + conp->vc_rows) * bytes * fontheight(p);
428         }
429         if (lines) {
430     	cval = expand2l(COLOR_2P(attr_bgcol_ec(p,conp)));
431     	memset_even_2p(p->screen_base+offset, lines * bytes, cval);
432         }
433     }
434     
435     
436         /*
437          *  `switch' for the low level operations
438          */
439     
440     struct display_switch fbcon_iplan2p2 = {
441         setup:		fbcon_iplan2p2_setup,
442         bmove:		fbcon_iplan2p2_bmove,
443         clear:		fbcon_iplan2p2_clear,
444         putc:		fbcon_iplan2p2_putc,
445         putcs:		fbcon_iplan2p2_putcs,
446         revc:		fbcon_iplan2p2_revc,
447         clear_margins:	fbcon_iplan2p2_clear_margins,
448         fontwidthmask:	FONTWIDTH(8)
449     };
450     
451     
452     #ifdef MODULE
453     MODULE_LICENSE("GPL");
454     
455     int init_module(void)
456     {
457         return 0;
458     }
459     
460     void cleanup_module(void)
461     {}
462     #endif /* MODULE */
463     
464     
465         /*
466          *  Visible symbols for modules
467          */
468     
469     EXPORT_SYMBOL(fbcon_iplan2p2);
470     EXPORT_SYMBOL(fbcon_iplan2p2_setup);
471     EXPORT_SYMBOL(fbcon_iplan2p2_bmove);
472     EXPORT_SYMBOL(fbcon_iplan2p2_clear);
473     EXPORT_SYMBOL(fbcon_iplan2p2_putc);
474     EXPORT_SYMBOL(fbcon_iplan2p2_putcs);
475     EXPORT_SYMBOL(fbcon_iplan2p2_revc);
476     EXPORT_SYMBOL(fbcon_iplan2p2_clear_margins);
477