xref: /dragonfly/sys/dev/misc/syscons/sckmsrndr.c (revision fcf6efef)
1 /*-
2  * Copyright (c) 2014 Imre Vadász <imre@vdsz.com>
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The DragonFly Project
6  * by Sascha Wildner <saw@online.de>.
7  *
8  * Simple font scaling code by Sascha Wildner and Matthew Dillon
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer as
15  *    the first lines of this file unmodified.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "opt_syscons.h"
33 
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/thread.h>
38 
39 #include <machine/console.h>
40 #include <machine/framebuffer.h>
41 
42 #include "syscons.h"
43 
44 #include <bus/isa/isareg.h>
45 
46 static vr_draw_border_t		kms_draw_border;
47 static vr_draw_t		kms_draw;
48 static vr_draw_cursor_t		kms_cursor;
49 static vr_blink_cursor_t	kms_blink;
50 #ifndef SC_NO_CUTPASTE
51 static vr_draw_mouse_t		kms_mouse;
52 #endif
53 
54 static void			kms_nop(scr_stat *scp, ...);
55 
56 static sc_rndr_sw_t kmsrndrsw = {
57 	kms_draw_border,
58 	kms_draw,
59 	(vr_set_cursor_t *)kms_nop,
60 	kms_cursor,
61 	kms_blink,
62 #ifndef SC_NO_CUTPASTE
63 	kms_mouse,
64 #else
65 	(vr_draw_mouse_t *)kms_nop,
66 #endif
67 };
68 RENDERER(kms, V_INFO_MM_TEXT, kmsrndrsw, kms_set);
69 
70 #ifndef SC_NO_MODE_CHANGE
71 static sc_rndr_sw_t grrndrsw = {
72 	(vr_draw_border_t *)kms_nop,
73 	(vr_draw_t *)kms_nop,
74 	(vr_set_cursor_t *)kms_nop,
75 	(vr_draw_cursor_t *)kms_nop,
76 	(vr_blink_cursor_t *)kms_nop,
77 	(vr_draw_mouse_t *)kms_nop,
78 };
79 RENDERER(kms, V_INFO_MM_OTHER, grrndrsw, kms_set);
80 #endif /* SC_NO_MODE_CHANGE */
81 
82 RENDERER_MODULE(kms, kms_set);
83 
84 static uint32_t colormap[16] = {
85 	0x00000000,	/* BLACK */
86 	0x000000aa,	/* BLUE */
87 	0x0000aa00,	/* GREEN */
88 	0x0000aaaa,	/* CYAN */
89 	0x00aa0000,	/* RED */
90 	0x00aa00aa,	/* MAGENTA */
91 	0x00aa5500,	/* BROWN */
92 	0x00aaaaaa,	/* WHITE */
93 	0x00555555,	/* HIGHLIGHT BLACK */
94 	0x005555ff,	/* HIGHLIGHT BLUE */
95 	0x0055ff55,	/* HIGHLIGHT GREEN */
96 	0x0055ffff,	/* HIGHLIGHT CYAN */
97 	0x00ff5555,	/* HIGHLIGHT RED */
98 	0x00ff55ff,	/* HIGHLIGHT MAGENTA */
99 	0x00ffff55,	/* HIGHLIGHT BROWN */
100 	0x00ffffff,	/* HIGHLIGHT WHITE */
101 };
102 
103 #ifndef SC_NO_CUTPASTE
104 static u_short mouse_and_mask[16] = {
105 	0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80,
106 	0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000
107 };
108 static u_short mouse_or_mask[16] = {
109 	0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800,
110 	0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
111 };
112 #endif
113 
114 static void
kms_nop(scr_stat * scp,...)115 kms_nop(scr_stat *scp, ...)
116 {
117 }
118 
119 /*
120  * Scaled font rendering.  Simple blit blitter copy operation with bitmap
121  * scaling.  Scales the bitmap char_data(sw x sh) to the output bitmap
122  * draw_pos(dw x dh).
123  *
124  * This function does not do fractional scaling.
125  *
126  * SET	- Sets both the fg and bg pen
127  *
128  * MASK	- Sets only the fg pen based on the source mask and leaves
129  *	  the background untouched.
130  */
131 #define BLIT_SET	0
132 #define BLIT_MASK	1
133 
134 static inline void
blit_blk32(scr_stat * scp,u_char * char_data,int sw,int sh,vm_offset_t draw_pos,int dw,int dh,int line_width,uint32_t fg,uint32_t bg,int how)135 blit_blk32(scr_stat *scp, u_char *char_data, int sw, int sh,
136 	   vm_offset_t draw_pos, int dw, int dh,
137 	   int line_width, uint32_t fg, uint32_t bg, int how)
138 {
139 	vm_offset_t p;
140 	int pos;
141 	int x;		/* destination iterator (whole pixels) */
142 	int y;
143 	int sx, sx_inc;	/* source iterator (fractional) */
144 	int sy, sy_inc;
145 	uint8_t c;
146 
147 	/*
148 	 * Calculate fractional iterator for source
149 	 */
150 	if (dw)
151 		sx_inc = (sw << 16) / dw;
152 	else
153 		sx_inc = 0;
154 
155 	if (dh)
156 		sy_inc = (sh << 16) / dh;
157 	else
158 		sy_inc = 0;
159 
160 	sy = 0;
161 	c = 0;
162 
163 	/*
164 	 * For each pixel row in the target
165 	 */
166 	for (y = 0; y < dh; ++y) {
167 		sx = 0;
168 		p = draw_pos;
169 
170 		/*
171 		 * Render all pixel columns in the target by calculating
172 		 * which bit in the source is applicable.
173 		 */
174 		switch(how) {
175 		case BLIT_SET:
176 			for (x = 0; x < dw; ++x) {
177 				if ((sx & 0x00070000) == 0)
178 					c = char_data[sx >> 19];
179 				pos = ~(sx >> 16) & 7;
180 				writel(p + x * 4, (c & (1 << pos) ? fg : bg));
181 				sx += sx_inc;
182 			}
183 			break;
184 		case BLIT_MASK:
185 			for (x = 0; x < dw; ++x) {
186 				if ((sx & 0x00070000) == 0)
187 					c = char_data[sx >> 19];
188 				pos = ~(sx >> 16) & 7;
189 				if (c & (1 << pos))
190 					writel(p + x * 4, fg);
191 				sx += sx_inc;
192 			}
193 			break;
194 		}
195 		draw_pos += line_width;
196 		sy += sy_inc;
197 		if (sy >= 0x10000) {
198 			char_data += (sy >> 16) * (sw >> 3);
199 			sy &= 0x0FFFF;
200 		}
201 	}
202 }
203 
204 static inline void
blit_blk24(scr_stat * scp,u_char * char_data,int sw,int sh,vm_offset_t draw_pos,int dw,int dh,int line_width,uint32_t fg,uint32_t bg,int how)205 blit_blk24(scr_stat *scp, u_char *char_data, int sw, int sh,
206 	   vm_offset_t draw_pos, int dw, int dh,
207 	   int line_width, uint32_t fg, uint32_t bg, int how)
208 {
209 	vm_offset_t p;
210 	int pos;
211 	int x;		/* destination iterator (whole pixels) */
212 	int y;
213 	int sx, sx_inc;	/* source iterator (fractional) */
214 	int sy, sy_inc;
215 	uint8_t c;
216 
217 	/*
218 	 * Calculate fractional iterator for source
219 	 */
220 	if (dw)
221 		sx_inc = (sw << 16) / dw;
222 	else
223 		sx_inc = 0;
224 
225 	if (dh)
226 		sy_inc = (sh << 16) / dh;
227 	else
228 		sy_inc = 0;
229 
230 	sy = 0;
231 	c = 0;
232 
233 	/*
234 	 * For each pixel row in the target
235 	 */
236 	for (y = 0; y < dh; ++y) {
237 		sx = 0;
238 		p = draw_pos;
239 
240 		/*
241 		 * Render all pixel columns in the target by calculating
242 		 * which bit in the source is applicable.
243 		 */
244 		switch(how) {
245 		case BLIT_SET:
246 			for (x = 0; x < dw; ++x) {
247 				if ((sx & 0x00070000) == 0)
248 					c = char_data[sx >> 19];
249 				pos = ~(sx >> 16) & 7;
250 				uint32_t col = c & (1 << pos) ? fg : bg;
251 				writeb(p, col >> 16);
252 				writeb(p + 1, col >> 8);
253 				writeb(p + 2, col >> 0);
254 				p += 3;
255 				sx += sx_inc;
256 			}
257 			break;
258 		case BLIT_MASK:
259 			for (x = 0; x < dw; ++x) {
260 				if ((sx & 0x00070000) == 0)
261 					c = char_data[sx >> 19];
262 				pos = ~(sx >> 16) & 7;
263 				if (c & (1 << pos)) {
264 					writeb(p, fg >> 16);
265 					writeb(p + 1, fg >> 8);
266 					writeb(p + 2, fg >> 0);
267 				}
268 				p += 3;
269 				sx += sx_inc;
270 			}
271 			break;
272 		}
273 		draw_pos += line_width;
274 		sy += sy_inc;
275 		if (sy >= 0x10000) {
276 			char_data += (sy >> 16) * (sw >> 3);
277 			sy &= 0x0FFFF;
278 		}
279 	}
280 }
281 
282 static void
fill_rect32(scr_stat * scp,vm_offset_t draw_pos,int width,int height,int line_width,uint32_t fg)283 fill_rect32(scr_stat *scp, vm_offset_t draw_pos, int width, int height,
284 	    int line_width, uint32_t fg)
285 {
286 	int i, j;
287 
288 	for (i = 0; i < height; i++) {
289 		for (j = 0; j < width; j++)
290 			writel(draw_pos + j * 4, fg);
291 		draw_pos += line_width;
292 	}
293 }
294 
295 static void
fill_rect24(scr_stat * scp,vm_offset_t draw_pos,int width,int height,int line_width,uint32_t fg)296 fill_rect24(scr_stat *scp, vm_offset_t draw_pos, int width, int height,
297 	    int line_width, uint32_t fg)
298 {
299 	int i, j, d;
300 
301 	d = line_width - width * 3;
302 	KKASSERT(d >= 0);
303 
304 	if ((draw_pos & 0x3) == 0 && (line_width & 0x3) == 0 &&
305 	    (width & 0x3) == 0) {
306 		uint32_t fga = fg | (fg << 24);
307 		uint32_t fgb = (fg << 16) | ((fg >> 8) & 0xffff);
308 		uint32_t fgc = (fg << 8) | ((fg >> 16) & 0xff);
309 		for (i = 0; i < height; i++) {
310 			for (j = 0; j < width; j += 4) {
311 				writel(draw_pos, fga);
312 				writel(draw_pos + 4, fgb);
313 				writel(draw_pos + 8, fgc);
314 				draw_pos += 12;
315 			}
316 			draw_pos += d;
317 		}
318 	} else {
319 		for (i = 0; i < height; i++) {
320 			for (j = 0; j < width; j++) {
321 				writeb(draw_pos, fg >> 16);
322 				writeb(draw_pos + 1, fg >> 8);
323 				writeb(draw_pos + 2, fg >> 0);
324 				draw_pos += 3;
325 			}
326 			draw_pos += line_width;
327 		}
328 	}
329 }
330 
331 /* KMS renderer */
332 
333 static void
kms_draw_border(scr_stat * scp,int color)334 kms_draw_border(scr_stat *scp, int color)
335 {
336 	sc_softc_t *sc = scp->sc;
337 	int line_width, pixel_size;
338 	int rightpixel, bottompixel;
339 	uint32_t fg;
340 	vm_offset_t draw_pos;
341 
342 	if (sc->fbi->vaddr == 0)
343 		return;
344 
345 	fg = colormap[color];
346 	line_width = sc->fbi->stride;
347 	/* sc->fbi->depth may only be 24 or 32 */
348 	pixel_size = sc->fbi->depth == 24 ? 3 : 4;
349 	rightpixel = sc->fbi->width - scp->xsize * scp->blk_width;
350 	bottompixel = sc->fbi->height - scp->ysize * scp->blk_height;
351 
352 	draw_pos = sc->fbi->vaddr + scp->blk_width * pixel_size * scp->xsize;
353 	if (pixel_size == 3) {
354 		fill_rect24(scp, draw_pos, rightpixel,
355 		    scp->blk_height * scp->ysize, line_width, fg);
356 	} else {
357 		fill_rect32(scp, draw_pos, rightpixel,
358 		    scp->blk_height * scp->ysize, line_width, fg);
359 	}
360 
361 	draw_pos = sc->fbi->vaddr + scp->blk_height * scp->ysize * line_width;
362 	if (pixel_size == 3) {
363 		fill_rect24(scp, draw_pos, sc->fbi->width,
364 		    sc->fbi->height - scp->blk_height * scp->ysize, line_width,
365 		    fg);
366 	} else {
367 		fill_rect32(scp, draw_pos, sc->fbi->width,
368 		    sc->fbi->height - scp->blk_height * scp->ysize, line_width,
369 		    fg);
370 	}
371 }
372 
373 static void
kms_draw(scr_stat * scp,int from,int count,int flip)374 kms_draw(scr_stat *scp, int from, int count, int flip)
375 {
376 	sc_softc_t *sc = scp->sc;
377 	u_char *char_data;
378 	int a, i;
379 	uint32_t fg, bg;
380 	vm_offset_t draw_pos, p;
381 	int line_width, pixel_size;
382 
383 	if (sc->fbi->vaddr == 0)
384 		return;
385 
386 	line_width = sc->fbi->stride;
387 	/* sc->fbi->depth may only be 24 or 32 */
388 	pixel_size = sc->fbi->depth == 24 ? 3 : 4;
389 
390 	draw_pos = sc->fbi->vaddr +
391 	    scp->blk_height * line_width * (from / scp->xsize);
392 
393 	if (from + count > scp->xsize * scp->ysize)
394 		count = scp->xsize * scp->ysize - from;
395 
396 	p = draw_pos + scp->blk_width * pixel_size * (from % scp->xsize);
397 	for (i = from; count-- > 0; i++) {
398 		char_data = &(scp->font[sc_vtb_getc(&scp->vtb, i) *
399 					scp->font_height]);
400 
401 		a = sc_vtb_geta(&scp->vtb, i);
402 		if (flip) {
403 			fg = colormap[((a & 0xf000) >> 4) >> 8];
404 			bg = colormap[(a & 0x0f00) >> 8];
405 		} else {
406 			fg = colormap[(a & 0x0f00) >> 8];
407 			bg = colormap[((a & 0xf000) >> 4) >> 8];
408 		}
409 		if (pixel_size == 3) {
410 			blit_blk24(scp, char_data, scp->font_width,
411 				   scp->font_height, p, scp->blk_width,
412 				   scp->blk_height, line_width, fg, bg,
413 				   BLIT_SET);
414 		} else {
415 			blit_blk32(scp, char_data, scp->font_width,
416 				   scp->font_height, p, scp->blk_width,
417 				   scp->blk_height, line_width, fg, bg,
418 				   BLIT_SET);
419 		}
420 		p += scp->blk_width * pixel_size;
421 		if ((i % scp->xsize) == scp->xsize - 1) {
422 			draw_pos += scp->blk_height * line_width;
423 			p = draw_pos;
424 		}
425 	}
426 }
427 
428 static void
draw_kmscursor(scr_stat * scp,int at,int on,int flip)429 draw_kmscursor(scr_stat *scp, int at, int on, int flip)
430 {
431 	sc_softc_t *sc = scp->sc;
432 	int line_width, pixel_size;
433 	int cursor_base;
434 	int blk_base;
435 	int a;
436 	uint32_t fg, bg;
437 	unsigned char *char_data;
438 	vm_offset_t draw_pos;
439 
440 	if (sc->fbi->vaddr == 0)
441 		return;
442 
443 	line_width = sc->fbi->stride;
444 	/* sc->fbi->depth may only be 24 or 32 */
445 	pixel_size = sc->fbi->depth == 24 ? 3 : 4;
446 	cursor_base = /* scp->font_height - */ scp->cursor_base;
447 	blk_base = scp->blk_height * cursor_base / scp->font_height;
448 
449 	draw_pos = sc->fbi->vaddr +
450 	    scp->blk_width * pixel_size * (at % scp->xsize) +
451 	    scp->blk_height * line_width * (at / scp->xsize) +
452 	    blk_base * line_width;
453 
454 	a = sc_vtb_geta(&scp->vtb, at);
455 	if (flip) {
456 		fg = colormap[((on) ? (a & 0x0f00) :
457 		    ((a & 0xf000) >> 4)) >> 8];
458 		bg = colormap[((on) ? ((a & 0xf000) >> 4) :
459 		    (a & 0x0f00)) >> 8];
460 	} else {
461 		fg = colormap[((on) ? ((a & 0xf000) >> 4) :
462 		    (a & 0x0f00)) >> 8];
463 		bg = colormap[((on) ? (a & 0x0f00) :
464 		    ((a & 0xf000) >> 4)) >> 8];
465 	}
466 
467 	char_data = &scp->font[sc_vtb_getc(&scp->vtb, at) * scp->font_height];
468 	char_data += cursor_base;
469 
470 	if (pixel_size == 3) {
471 		blit_blk24(scp, char_data,
472 			   scp->font_width, scp->font_height - cursor_base,
473 			   draw_pos, scp->blk_width, scp->blk_height - blk_base,
474 			   line_width, fg, bg, BLIT_SET);
475 	} else {
476 		blit_blk32(scp, char_data,
477 			   scp->font_width, scp->font_height - cursor_base,
478 			   draw_pos, scp->blk_width, scp->blk_height - blk_base,
479 			   line_width, fg, bg, BLIT_SET);
480 	}
481 }
482 
483 static int pxlblinkrate = 0;
484 
485 static void
kms_cursor(scr_stat * scp,int at,int blink,int on,int flip)486 kms_cursor(scr_stat *scp, int at, int blink, int on, int flip)
487 {
488 	if (scp->cursor_height <= 0)	/* the text cursor is disabled */
489 		return;
490 
491 	if (on) {
492 		if (!blink) {
493 			scp->status |= VR_CURSOR_ON;
494 			draw_kmscursor(scp, at, on, flip);
495 		} else if (++pxlblinkrate & 4) {
496 			pxlblinkrate = 0;
497 			scp->status ^= VR_CURSOR_ON;
498 			draw_kmscursor(scp, at,
499 			    scp->status & VR_CURSOR_ON, flip);
500 		}
501 	} else {
502 		if (scp->status & VR_CURSOR_ON)
503 			draw_kmscursor(scp, at, on, flip);
504 		scp->status &= ~VR_CURSOR_ON;
505 	}
506 	if (blink)
507 		scp->status |= VR_CURSOR_BLINK;
508 	else
509 		scp->status &= ~VR_CURSOR_BLINK;
510 }
511 
512 static void
kms_blink(scr_stat * scp,int at,int flip)513 kms_blink(scr_stat *scp, int at, int flip)
514 {
515 	if (!(scp->status & VR_CURSOR_BLINK))
516 		return;
517 	if (!(++pxlblinkrate & 4))
518 		return;
519 	pxlblinkrate = 0;
520 	scp->status ^= VR_CURSOR_ON;
521 	draw_kmscursor(scp, at, scp->status & VR_CURSOR_ON, flip);
522 }
523 
524 #ifndef SC_NO_CUTPASTE
525 
526 static void
draw_kmsmouse(scr_stat * scp,int x,int y)527 draw_kmsmouse(scr_stat *scp, int x, int y)
528 {
529 	sc_softc_t *sc = scp->sc;
530 	int line_width, pixel_size;
531 	int blk_width, blk_height;
532 	vm_offset_t draw_pos;
533 
534 	if (sc->fbi->vaddr == 0)
535 		return;
536 
537 	line_width = sc->fbi->stride;
538 	/* sc->fbi->depth may only be 24 or 32 */
539 	pixel_size = sc->fbi->depth == 24 ? 3 : 4;
540 
541 	if (x + scp->font_width < scp->font_width * scp->xsize)
542 		blk_width = scp->blk_width;
543 	else
544 		blk_width = scp->font_width * scp->xsize - x;
545 
546 	if (y + scp->font_height < scp->font_height * scp->ysize)
547 		blk_height = scp->blk_height;
548 	else
549 		blk_height = scp->font_height * scp->ysize - y;
550 
551 	draw_pos = sc->fbi->vaddr + y * scp->blk_height / scp->font_height *
552 		   line_width +
553 		   x * scp->blk_width / scp->font_width * pixel_size;
554 	if (pixel_size == 3) {
555 		blit_blk24(scp, (unsigned char *)mouse_and_mask, 16, 16,
556 			   draw_pos, blk_width, blk_height, line_width,
557 			   colormap[0], 0, BLIT_MASK);
558 		blit_blk24(scp, (unsigned char *)mouse_or_mask, 16, 16,
559 			   draw_pos, blk_width, blk_height, line_width,
560 			   colormap[15], 0, BLIT_MASK);
561 	} else {
562 		blit_blk32(scp, (unsigned char *)mouse_and_mask, 16, 16,
563 			   draw_pos, blk_width, blk_height, line_width,
564 			   colormap[0], 0, BLIT_MASK);
565 		blit_blk32(scp, (unsigned char *)mouse_or_mask, 16, 16,
566 			   draw_pos, blk_width, blk_height, line_width,
567 			   colormap[15], 0, BLIT_MASK);
568 	}
569 }
570 
571 static void
remove_kmsmouse(scr_stat * scp,int x,int y)572 remove_kmsmouse(scr_stat *scp, int x, int y)
573 {
574 	int col, row;
575 	int pos;
576 	int i;
577 
578 	/* erase the mouse cursor image */
579 	col = x / scp->font_width - scp->xoff;
580 	row = y / scp->font_height - scp->yoff;
581 	pos = row * scp->xsize + col;
582 	i = (col < scp->xsize - 1) ? 2 : 1;
583 	(*scp->rndr->draw)(scp, pos, i, FALSE);
584 	if (row < scp->ysize - 1)
585 		(*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE);
586 }
587 
588 static void
kms_mouse(scr_stat * scp,int x,int y,int on)589 kms_mouse(scr_stat *scp, int x, int y, int on)
590 {
591 	if (on)
592 		draw_kmsmouse(scp, x, y);
593 	else
594 		remove_kmsmouse(scp, x, y);
595 }
596 
597 #endif /* SC_NO_CUTPASTE */
598