xref: /dragonfly/sys/dev/misc/syscons/sckmsrndr.c (revision fcf6efefc03a35111797b109fa4994034ebe39ba)
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