1 /*
2  *  linux/drivers/video/fbcon-vga-planes.c -- Low level frame buffer operations
3  *				  for VGA 4-plane modes
4  *
5  * Copyright 1999 Ben Pfaff <pfaffben@debian.org> and Petr Vandrovec <VANDROVE@vc.cvut.cz>
6  * Based on code by Michael Schmitz
7  * Based on the old macfb.c 4bpp code by Alan Cox
8  *
9  * This file is subject to the terms and conditions of the GNU General
10  * Public License.  See the file COPYING in the main directory of this
11  * archive for more details.  */
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 #include <linux/vt_buffer.h>
19 
20 #include <asm/io.h>
21 
22 #include <video/fbcon.h>
23 #include <video/fbcon-vga-planes.h>
24 
25 #define GRAPHICS_ADDR_REG 0x3ce		/* Graphics address register. */
26 #define GRAPHICS_DATA_REG 0x3cf		/* Graphics data register. */
27 
28 #define SET_RESET_INDEX 0		/* Set/Reset Register index. */
29 #define ENABLE_SET_RESET_INDEX 1	/* Enable Set/Reset Register index. */
30 #define DATA_ROTATE_INDEX 3		/* Data Rotate Register index. */
31 #define GRAPHICS_MODE_INDEX 5		/* Graphics Mode Register index. */
32 #define BIT_MASK_INDEX 8		/* Bit Mask Register index. */
33 
34 /* The VGA's weird architecture often requires that we read a byte and
35    write a byte to the same location.  It doesn't matter *what* byte
36    we write, however.  This is because all the action goes on behind
37    the scenes in the VGA's 32-bit latch register, and reading and writing
38    video memory just invokes latch behavior.
39 
40    To avoid race conditions (is this necessary?), reading and writing
41    the memory byte should be done with a single instruction.  One
42    suitable instruction is the x86 bitwise OR.  The following
43    read-modify-write routine should optimize to one such bitwise
44    OR. */
rmw(volatile char * p)45 static inline void rmw(volatile char *p)
46 {
47 	readb(p);
48 	writeb(1, p);
49 }
50 
51 /* Set the Graphics Mode Register.  Bits 0-1 are write mode, bit 3 is
52    read mode. */
setmode(int mode)53 static inline void setmode(int mode)
54 {
55 	outb(GRAPHICS_MODE_INDEX, GRAPHICS_ADDR_REG);
56 	outb(mode, GRAPHICS_DATA_REG);
57 }
58 
59 /* Select the Bit Mask Register. */
selectmask(void)60 static inline void selectmask(void)
61 {
62 	outb(BIT_MASK_INDEX, GRAPHICS_ADDR_REG);
63 }
64 
65 /* Set the value of the Bit Mask Register.  It must already have been
66    selected with selectmask(). */
setmask(int mask)67 static inline void setmask(int mask)
68 {
69 	outb(mask, GRAPHICS_DATA_REG);
70 }
71 
72 /* Set the Data Rotate Register.  Bits 0-2 are rotate count, bits 3-4
73    are logical operation (0=NOP, 1=AND, 2=OR, 3=XOR). */
setop(int op)74 static inline void setop(int op)
75 {
76 	outb(DATA_ROTATE_INDEX, GRAPHICS_ADDR_REG);
77 	outb(op, GRAPHICS_DATA_REG);
78 }
79 
80 /* Set the Enable Set/Reset Register.  The code here always uses value
81    0xf for this register.  */
setsr(int sr)82 static inline void setsr(int sr)
83 {
84 	outb(ENABLE_SET_RESET_INDEX, GRAPHICS_ADDR_REG);
85 	outb(sr, GRAPHICS_DATA_REG);
86 }
87 
88 /* Set the Set/Reset Register. */
setcolor(int color)89 static inline void setcolor(int color)
90 {
91 	outb(SET_RESET_INDEX, GRAPHICS_ADDR_REG);
92 	outb(color, GRAPHICS_DATA_REG);
93 }
94 
95 /* Set the value in the Graphics Address Register. */
setindex(int index)96 static inline void setindex(int index)
97 {
98 	outb(index, GRAPHICS_ADDR_REG);
99 }
100 
fbcon_vga_planes_setup(struct display * p)101 void fbcon_vga_planes_setup(struct display *p)
102 {
103 }
104 
fbcon_vga_planes_bmove(struct display * p,int sy,int sx,int dy,int dx,int height,int width)105 void fbcon_vga_planes_bmove(struct display *p, int sy, int sx, int dy, int dx,
106 		   int height, int width)
107 {
108 	char *src;
109 	char *dest;
110 	int line_ofs;
111 	int x;
112 
113 	setmode(1);
114 	setop(0);
115 	setsr(0xf);
116 
117 	sy *= fontheight(p);
118 	dy *= fontheight(p);
119 	height *= fontheight(p);
120 
121 	if (dy < sy || (dy == sy && dx < sx)) {
122 		line_ofs = p->line_length - width;
123 		dest = p->screen_base + dx + dy * p->line_length;
124 		src = p->screen_base + sx + sy * p->line_length;
125 		while (height--) {
126 			for (x = 0; x < width; x++) {
127 				readb(src);
128 				writeb(0, dest);
129 				dest++;
130 				src++;
131 			}
132 			src += line_ofs;
133 			dest += line_ofs;
134 		}
135 	} else {
136 		line_ofs = p->line_length - width;
137 		dest = p->screen_base + dx + width + (dy + height - 1) * p->line_length;
138 		src = p->screen_base + sx + width + (sy + height - 1) * p->line_length;
139 		while (height--) {
140 			for (x = 0; x < width; x++) {
141 				dest--;
142 				src--;
143 				readb(src);
144 				writeb(0, dest);
145 			}
146 			src -= line_ofs;
147 			dest -= line_ofs;
148 		}
149 	}
150 }
151 
fbcon_vga_planes_clear(struct vc_data * conp,struct display * p,int sy,int sx,int height,int width)152 void fbcon_vga_planes_clear(struct vc_data *conp, struct display *p, int sy, int sx,
153 		   int height, int width)
154 {
155 	int line_ofs = p->line_length - width;
156 	char *where;
157 	int x;
158 
159 	setmode(0);
160 	setop(0);
161 	setsr(0xf);
162 	setcolor(attr_bgcol_ec(p, conp));
163 	selectmask();
164 
165 	setmask(0xff);
166 
167 	sy *= fontheight(p);
168 	height *= fontheight(p);
169 
170 	where = p->screen_base + sx + sy * p->line_length;
171 	while (height--) {
172 		for (x = 0; x < width; x++) {
173 			writeb(0, where);
174 			where++;
175 		}
176 		where += line_ofs;
177 	}
178 }
179 
fbcon_ega_planes_putc(struct vc_data * conp,struct display * p,int c,int yy,int xx)180 void fbcon_ega_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
181 {
182 	int fg = attr_fgcol(p,c);
183 	int bg = attr_bgcol(p,c);
184 
185 	int y;
186 	u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
187 	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
188 
189 	setmode(0);
190 	setop(0);
191 	setsr(0xf);
192 	setcolor(bg);
193 	selectmask();
194 
195 	setmask(0xff);
196 	for (y = 0; y < fontheight(p); y++, where += p->line_length)
197 		rmw(where);
198 
199 	where -= p->line_length * y;
200 	setcolor(fg);
201 	selectmask();
202 	for (y = 0; y < fontheight(p); y++, where += p->line_length)
203 		if (cdat[y]) {
204 			setmask(cdat[y]);
205 			rmw(where);
206 		}
207 }
208 
fbcon_vga_planes_putc(struct vc_data * conp,struct display * p,int c,int yy,int xx)209 void fbcon_vga_planes_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
210 {
211 	int fg = attr_fgcol(p,c);
212 	int bg = attr_bgcol(p,c);
213 
214 	int y;
215 	u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
216 	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
217 
218 	setmode(2);
219 	setop(0);
220 	setsr(0xf);
221 	setcolor(fg);
222 	selectmask();
223 
224 	setmask(0xff);
225 	writeb(bg, where);
226 	rmb();
227 	readb(where); /* fill latches */
228 	setmode(3);
229 	wmb();
230 	for (y = 0; y < fontheight(p); y++, where += p->line_length)
231 		writeb(cdat[y], where);
232 	wmb();
233 }
234 
235 /* 28.50 in my test */
fbcon_ega_planes_putcs(struct vc_data * conp,struct display * p,const unsigned short * s,int count,int yy,int xx)236 void fbcon_ega_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
237 		   int count, int yy, int xx)
238 {
239 	u16 c = scr_readw(s);
240 	int fg = attr_fgcol(p, c);
241 	int bg = attr_bgcol(p, c);
242 
243 	char *where;
244 	int n;
245 
246 	setmode(2);
247 	setop(0);
248 	selectmask();
249 
250 	setmask(0xff);
251 	where = p->screen_base + xx + yy * p->line_length * fontheight(p);
252 	writeb(bg, where);
253 	rmb();
254 	readb(where); /* fill latches */
255 	wmb();
256 	selectmask();
257 	for (n = 0; n < count; n++) {
258 		int c = scr_readw(s++) & p->charmask;
259 		u8 *cdat = p->fontdata + c * fontheight(p);
260 		u8 *end = cdat + fontheight(p);
261 
262 		while (cdat < end) {
263 			outb(*cdat++, GRAPHICS_DATA_REG);
264 			wmb();
265 			writeb(fg, where);
266 			where += p->line_length;
267 		}
268 		where += 1 - p->line_length * fontheight(p);
269 	}
270 
271 	wmb();
272 }
273 
274 /* 6.96 in my test */
fbcon_vga_planes_putcs(struct vc_data * conp,struct display * p,const unsigned short * s,int count,int yy,int xx)275 void fbcon_vga_planes_putcs(struct vc_data *conp, struct display *p, const unsigned short *s,
276 		   int count, int yy, int xx)
277 {
278 	u16 c = scr_readw(s);
279 	int fg = attr_fgcol(p, c);
280 	int bg = attr_bgcol(p, c);
281 
282 	char *where;
283 	int n;
284 
285 	setmode(2);
286 	setop(0);
287 	setsr(0xf);
288 	setcolor(fg);
289 	selectmask();
290 
291 	setmask(0xff);
292 	where = p->screen_base + xx + yy * p->line_length * fontheight(p);
293 	writeb(bg, where);
294 	rmb();
295 	readb(where); /* fill latches */
296 	setmode(3);
297 	wmb();
298 	for (n = 0; n < count; n++) {
299 		int y;
300 		int c = scr_readw(s++) & p->charmask;
301 		u8 *cdat = p->fontdata + (c & p->charmask) * fontheight(p);
302 
303 		for (y = 0; y < fontheight(p); y++, cdat++) {
304 			writeb (*cdat, where);
305 			where += p->line_length;
306 		}
307 		where += 1 - p->line_length * fontheight(p);
308 	}
309 
310 	wmb();
311 }
312 
fbcon_vga_planes_revc(struct display * p,int xx,int yy)313 void fbcon_vga_planes_revc(struct display *p, int xx, int yy)
314 {
315 	char *where = p->screen_base + xx + yy * p->line_length * fontheight(p);
316 	int y;
317 
318 	setmode(0);
319 	setop(0x18);
320 	setsr(0xf);
321 	setcolor(0xf);
322 	selectmask();
323 
324 	setmask(0xff);
325 	for (y = 0; y < fontheight(p); y++) {
326 		rmw(where);
327 		where += p->line_length;
328 	}
329 }
330 
331 struct display_switch fbcon_vga_planes = {
332     setup:		fbcon_vga_planes_setup,
333     bmove:		fbcon_vga_planes_bmove,
334     clear:		fbcon_vga_planes_clear,
335     putc:		fbcon_vga_planes_putc,
336     putcs:		fbcon_vga_planes_putcs,
337     revc:		fbcon_vga_planes_revc,
338     fontwidthmask:	FONTWIDTH(8)
339 };
340 
341 struct display_switch fbcon_ega_planes = {
342     setup:		fbcon_vga_planes_setup,
343     bmove:		fbcon_vga_planes_bmove,
344     clear:		fbcon_vga_planes_clear,
345     putc:		fbcon_ega_planes_putc,
346     putcs:		fbcon_ega_planes_putcs,
347     revc:		fbcon_vga_planes_revc,
348     fontwidthmask:	FONTWIDTH(8)
349 };
350 
351 #ifdef MODULE
352 MODULE_LICENSE("GPL");
353 
init_module(void)354 int init_module(void)
355 {
356     return 0;
357 }
358 
cleanup_module(void)359 void cleanup_module(void)
360 {}
361 #endif /* MODULE */
362 
363 
364     /*
365      *  Visible symbols for modules
366      */
367 
368 EXPORT_SYMBOL(fbcon_vga_planes);
369 EXPORT_SYMBOL(fbcon_vga_planes_setup);
370 EXPORT_SYMBOL(fbcon_vga_planes_bmove);
371 EXPORT_SYMBOL(fbcon_vga_planes_clear);
372 EXPORT_SYMBOL(fbcon_vga_planes_putc);
373 EXPORT_SYMBOL(fbcon_vga_planes_putcs);
374 EXPORT_SYMBOL(fbcon_vga_planes_revc);
375 
376 EXPORT_SYMBOL(fbcon_ega_planes);
377 EXPORT_SYMBOL(fbcon_ega_planes_putc);
378 EXPORT_SYMBOL(fbcon_ega_planes_putcs);
379 
380 /*
381  * Overrides for Emacs so that we follow Linus's tabbing style.
382  * ---------------------------------------------------------------------------
383  * Local variables:
384  * c-basic-offset: 8
385  * End:
386  */
387 
388