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