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