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