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