kernel/syscons: Add simple font scaling when using KMS drivers.
[dragonfly.git] / sys / dev / misc / syscons / sckmsrndr.c
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
42 #include <dev/drm/include/linux/fb.h>
43
44 #include "syscons.h"
45
46 #include <bus/isa/isareg.h>
47
48 static vr_draw_t                kms_draw;
49 static vr_draw_cursor_t         kms_cursor;
50 static vr_blink_cursor_t        kms_blink;
51 static vr_draw_mouse_t          kms_mouse;
52
53 static void                     kms_nop(scr_stat *scp, ...);
54
55 static sc_rndr_sw_t kmsrndrsw = {
56         (vr_draw_border_t *)kms_nop,
57         kms_draw,
58         (vr_set_cursor_t *)kms_nop,
59         kms_cursor,
60         kms_blink,
61 #ifndef SC_NO_CUTPASTE
62         kms_mouse,
63 #else
64         (vr_draw_mouse_t *)kms_nop;
65 #endif
66 };
67 RENDERER(kms, V_INFO_MM_TEXT, kmsrndrsw, kms_set);
68
69 #ifndef SC_NO_MODE_CHANGE
70 static sc_rndr_sw_t grrndrsw = {
71         (vr_draw_border_t *)kms_nop,
72         (vr_draw_t *)kms_nop,
73         (vr_set_cursor_t *)kms_nop,
74         (vr_draw_cursor_t *)kms_nop,
75         (vr_blink_cursor_t *)kms_nop,
76         (vr_draw_mouse_t *)kms_nop,
77 };
78 RENDERER(kms, V_INFO_MM_OTHER, grrndrsw, kms_set);
79 #endif /* SC_NO_MODE_CHANGE */
80
81 RENDERER_MODULE(kms, kms_set);
82
83 static uint32_t colormap[16] = {
84         0x00000000,     /* BLACK */
85         0x000000aa,     /* BLUE */
86         0x0000aa00,     /* GREEN */
87         0x0000aaaa,     /* CYAN */
88         0x00aa0000,     /* RED */
89         0x00aa00aa,     /* MAGENTA */
90         0x00aa5500,     /* BROWN */
91         0x00aaaaaa,     /* WHITE */
92         0x00555555,     /* HIGHLIGHT BLACK */
93         0x005555ff,     /* HIGHLIGHT BLUE */
94         0x0055ff55,     /* HIGHLIGHT GREEN */
95         0x0055ffff,     /* HIGHLIGHT CYAN */
96         0x00ff5555,     /* HIGHLIGHT RED */
97         0x00ff55ff,     /* HIGHLIGHT MAGENTA */
98         0x00ffff55,     /* HIGHLIGHT BROWN */
99         0x00ffffff,     /* HIGHLIGHT WHITE */
100 };
101
102 #ifndef SC_NO_CUTPASTE
103 static u_short mouse_and_mask[16] = {
104         0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80,
105         0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000
106 };
107 static u_short mouse_or_mask[16] = {
108         0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800,
109         0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
110 };
111 #endif
112
113 static void
114 kms_nop(scr_stat *scp, ...)
115 {
116 }
117
118 /*
119  * Scaled font rendering.  Simple blit blitter copy operation with bitmap
120  * scaling.  Scales the bitmap char_data(sw x sh) to the output bitmap
121  * draw_pos(dw x dh).
122  *
123  * This function does not do fractional scaling.
124  *
125  * SET  - Sets both the fg and bg pen
126  *
127  * MASK - Sets only the fg pen based on the source mask and leaves
128  *        the background untouched.
129  */
130 #define BLIT_SET        0
131 #define BLIT_MASK       1
132
133 static void
134 blit_blk(scr_stat *scp, u_char *char_data, int sw, int sh,
135          vm_offset_t draw_pos, int pixel_size, int dw, int dh,
136          int line_width, uint32_t fg, uint32_t bg, int how)
137 {
138         vm_offset_t p;
139         int pos;
140         int x;          /* destination iterator (whole pixels) */
141         int y;
142         int sx, sx_inc; /* source iterator (fractional) */
143         int sy, sy_inc;
144         uint8_t c;
145
146         /*
147          * Calculate fractional iterator for source
148          */
149         if (dw)
150                 sx_inc = (sw << 16) / dw;
151         else
152                 sx_inc = 0;
153
154         if (dh)
155                 sy_inc = (sh << 16) / dh;
156         else
157                 sy_inc = 0;
158
159         sy = 0;
160         c = 0;
161
162         /*
163          * For each pixel row in the target
164          */
165         for (y = 0; y < dh; ++y) {
166                 sx = 0;
167                 p = draw_pos;
168
169                 /*
170                  * Render all pixel columns in the target by calculating
171                  * which bit in the source is applicable.
172                  */
173                 switch(how) {
174                 case BLIT_SET:
175                         for (x = 0; x < dw; ++x) {
176                                 if ((sx & 0x00070000) == 0)
177                                         c = char_data[sx >> 19];
178                                 pos = ~(sx >> 16) & 7;
179                                 writel(p, (c & (1 << pos) ? fg : bg));
180                                 p += pixel_size;
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, fg);
191                                 p += pixel_size;
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 /* KMS renderer */
206
207 static void
208 kms_draw(scr_stat *scp, int from, int count, int flip)
209 {
210         sc_softc_t *sc = scp->sc;
211         u_char *char_data;
212         int a, i;
213         uint32_t fg, bg;
214         vm_offset_t draw_pos, p;
215         int line_width, pixel_size;
216
217         line_width = sc->fbi->stride;
218         pixel_size = 4;
219
220         draw_pos = sc->fbi->vaddr +
221             scp->blk_width * pixel_size * (from % scp->xsize) +
222             scp->blk_height * line_width * (from / scp->xsize);
223
224         if (from + count > scp->xsize * scp->ysize)
225                 count = scp->xsize * scp->ysize - from;
226
227         for (i = from; count-- > 0; i++) {
228                 p = draw_pos;
229                 char_data = &(scp->font[sc_vtb_getc(&scp->vtb, i) *
230                                         scp->font_height]);
231
232                 a = sc_vtb_geta(&scp->vtb, i);
233                 if (flip) {
234                         fg = colormap[((a & 0xf000) >> 4) >> 8];
235                         bg = colormap[(a & 0x0f00) >> 8];
236                 } else {
237                         fg = colormap[(a & 0x0f00) >> 8];
238                         bg = colormap[((a & 0xf000) >> 4) >> 8];
239                 }
240                 blit_blk(scp, char_data, scp->font_width, scp->font_height,
241                          p, pixel_size, scp->blk_width, scp->blk_height,
242                          line_width, fg, bg, BLIT_SET);
243                 draw_pos += scp->blk_width * pixel_size;
244                 if ((i % scp->xsize) == scp->xsize - 1) {
245                         draw_pos +=
246                             (scp->blk_height - 1) * line_width +
247                             scp->xpad * pixel_size;
248                 }
249         }
250 }
251
252 static void
253 draw_kmscursor(scr_stat *scp, int at, int on, int flip)
254 {
255         sc_softc_t *sc = scp->sc;
256         int line_width, pixel_size;
257         int cursor_base;
258         int blk_base;
259         int a;
260         uint32_t fg, bg;
261         unsigned char *char_data;
262         vm_offset_t draw_pos;
263
264         line_width = sc->fbi->stride;
265         pixel_size = 4;
266         cursor_base = /* scp->font_height - */ scp->cursor_base;
267         blk_base = scp->blk_height * cursor_base / scp->font_height;
268
269         draw_pos = sc->fbi->vaddr +
270             scp->blk_width * pixel_size * (at % scp->xsize) +
271             scp->blk_height * line_width * (at / scp->xsize) +
272             blk_base * line_width;
273
274         a = sc_vtb_geta(&scp->vtb, at);
275         if (flip) {
276                 fg = colormap[((on) ? (a & 0x0f00) :
277                     ((a & 0xf000) >> 4)) >> 8];
278                 bg = colormap[((on) ? ((a & 0xf000) >> 4) :
279                     (a & 0x0f00)) >> 8];
280         } else {
281                 fg = colormap[((on) ? ((a & 0xf000) >> 4) :
282                     (a & 0x0f00)) >> 8];
283                 bg = colormap[((on) ? (a & 0x0f00) :
284                     ((a & 0xf000) >> 4)) >> 8];
285         }
286
287         char_data = &scp->font[sc_vtb_getc(&scp->vtb, at) * scp->font_height];
288         char_data += cursor_base;
289
290         blit_blk(scp, char_data,
291                  scp->font_width, scp->font_height - cursor_base,
292                  draw_pos, pixel_size,
293                  scp->blk_width, scp->blk_height - blk_base,
294                  line_width, fg, bg, BLIT_SET);
295 }
296
297 static int pxlblinkrate = 0;
298
299 static void
300 kms_cursor(scr_stat *scp, int at, int blink, int on, int flip)
301 {
302         if (scp->cursor_height <= 0)    /* the text cursor is disabled */
303                 return;
304
305         if (on) {
306                 if (!blink) {
307                         scp->status |= VR_CURSOR_ON;
308                         draw_kmscursor(scp, at, on, flip);
309                 } else if (++pxlblinkrate & 4) {
310                         pxlblinkrate = 0;
311                         scp->status ^= VR_CURSOR_ON;
312                         draw_kmscursor(scp, at,
313                             scp->status & VR_CURSOR_ON, flip);
314                 }
315         } else {
316                 if (scp->status & VR_CURSOR_ON)
317                         draw_kmscursor(scp, at, on, flip);
318                 scp->status &= ~VR_CURSOR_ON;
319         }
320         if (blink)
321                 scp->status |= VR_CURSOR_BLINK;
322         else
323                 scp->status &= ~VR_CURSOR_BLINK;
324 }
325
326 static void
327 kms_blink(scr_stat *scp, int at, int flip)
328 {
329         if (!(scp->status & VR_CURSOR_BLINK))
330                 return;
331         if (!(++pxlblinkrate & 4))
332                 return;
333         pxlblinkrate = 0;
334         scp->status ^= VR_CURSOR_ON;
335         draw_kmscursor(scp, at, scp->status & VR_CURSOR_ON, flip);
336 }
337
338 #ifndef SC_NO_CUTPASTE
339
340 static void
341 draw_kmsmouse(scr_stat *scp, int x, int y)
342 {
343         sc_softc_t *sc = scp->sc;
344         int line_width, pixel_size;
345         int blk_width, blk_height;
346         vm_offset_t draw_pos;
347
348         line_width = sc->fbi->stride;
349         pixel_size = 4;
350
351         if (x + scp->font_width < scp->font_width * scp->xsize)
352                 blk_width = scp->blk_width;
353         else
354                 blk_width = scp->font_width * scp->xsize - x;
355
356         if (y + scp->font_height < scp->font_height * scp->ysize)
357                 blk_height = scp->blk_height;
358         else
359                 blk_height = scp->font_height * scp->ysize - y;
360
361         draw_pos = sc->fbi->vaddr + y * scp->blk_height / scp->font_height *
362                    line_width +
363                    x * scp->blk_width / scp->font_width * pixel_size;
364         blit_blk(scp, (unsigned char *)mouse_and_mask, 16, 16,
365                  draw_pos, pixel_size, blk_width, blk_height,
366                  line_width, colormap[0], 0, BLIT_MASK);
367         blit_blk(scp, (unsigned char *)mouse_or_mask, 16, 16,
368                  draw_pos, pixel_size, blk_width, blk_height,
369                  line_width, colormap[15], 0, BLIT_MASK);
370 }
371
372 static void
373 remove_kmsmouse(scr_stat *scp, int x, int y)
374 {
375         int col, row;
376         int pos;
377         int i;
378
379         /* erase the mouse cursor image */
380         col = x / scp->font_width - scp->xoff;
381         row = y / scp->font_height - scp->yoff;
382         pos = row * scp->xsize + col;
383         i = (col < scp->xsize - 1) ? 2 : 1;
384         (*scp->rndr->draw)(scp, pos, i, FALSE);
385         if (row < scp->ysize - 1)
386                 (*scp->rndr->draw)(scp, pos + scp->xsize, i, FALSE);
387 }
388
389 static void
390 kms_mouse(scr_stat *scp, int x, int y, int on)
391 {
392         if (on)
393                 draw_kmsmouse(scp, x, y);
394         else
395                 remove_kmsmouse(scp, x, y);
396 }
397
398 #endif /* SC_NO_CUTPASTE */