source: mainline/kernel/genarch/src/fb/fb.c@ 3b065c9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3b065c9 was 7ddc2c7, checked in by Martin Decky <martin@…>, 12 years ago

add support for framebuffer history paging (using Page Up and Page Down keys), inspiration taken from the code by Sebastian Köln
add support for input device out-of-band signalling

  • Property mode set to 100644
File size: 17.6 KB
Line 
1/*
2 * Copyright (c) 2008 Martin Decky
3 * Copyright (c) 2006 Ondrej Palkovsky
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup genarch
31 * @{
32 */
33/** @file
34 */
35
36#include <genarch/fb/font-8x16.h>
37#include <genarch/fb/fb.h>
38#include <console/chardev.h>
39#include <console/console.h>
40#include <sysinfo/sysinfo.h>
41#include <mm/km.h>
42#include <mm/slab.h>
43#include <align.h>
44#include <panic.h>
45#include <memstr.h>
46#include <config.h>
47#include <bitops.h>
48#include <print.h>
49#include <str.h>
50#include <ddi/ddi.h>
51#include <typedefs.h>
52#include <byteorder.h>
53
54#define BG_COLOR 0x001620
55#define FG_COLOR 0xf3cf65
56#define INV_COLOR 0xaaaaaa
57
58#define FB_PAGES 8
59
60#define RED(x, bits) (((x) >> (8 + 8 + 8 - (bits))) & ((1 << (bits)) - 1))
61#define GREEN(x, bits) (((x) >> (8 + 8 - (bits))) & ((1 << (bits)) - 1))
62#define BLUE(x, bits) (((x) >> (8 - (bits))) & ((1 << (bits)) - 1))
63
64#define COL2X(col) ((col) * FONT_WIDTH)
65#define ROW2Y(row) ((row) * FONT_SCANLINES)
66
67#define X2COL(x) ((x) / FONT_WIDTH)
68#define Y2ROW(y) ((y) / FONT_SCANLINES)
69
70#define FB_POS(instance, x, y) \
71 ((y) * (instance)->scanline + (x) * (instance)->pixelbytes)
72
73#define BB_POS(instance, col, row) \
74 ((((instance)->start_row + (row)) % (instance)->rows) * \
75 (instance)->cols + (col))
76
77#define GLYPH_POS(instance, glyph, y) \
78 ((glyph) * (instance)->glyphbytes + (y) * (instance)->glyphscanline)
79
80typedef void (* rgb_conv_t)(void *, uint32_t);
81
82typedef struct {
83 SPINLOCK_DECLARE(lock);
84
85 parea_t parea;
86
87 uint8_t *addr;
88 uint16_t *backbuf;
89 uint8_t *glyphs;
90 uint8_t *bgscan;
91
92 rgb_conv_t rgb_conv;
93
94 unsigned int xres;
95 unsigned int yres;
96
97 /** Number of rows that fit on framebuffer */
98 unsigned int rowtrim;
99
100 unsigned int scanline;
101 unsigned int glyphscanline;
102
103 unsigned int pixelbytes;
104 unsigned int glyphbytes;
105 unsigned int bgscanbytes;
106
107 /** Number of columns in the backbuffer */
108 unsigned int cols;
109 /** Number of rows in the backbuffer */
110 unsigned int rows;
111
112 /** Starting row in the cyclic backbuffer */
113 unsigned int start_row;
114
115 /** Top-most visible row (relative to start_row) */
116 unsigned int offset_row;
117
118 /** Current backbuffer position */
119 unsigned int position;
120} fb_instance_t;
121
122static void fb_putchar(outdev_t *, wchar_t);
123static void fb_redraw(outdev_t *);
124static void fb_scroll_up(outdev_t *);
125static void fb_scroll_down(outdev_t *);
126
127static outdev_operations_t fbdev_ops = {
128 .write = fb_putchar,
129 .redraw = fb_redraw,
130 .scroll_up = fb_scroll_up,
131 .scroll_down = fb_scroll_down
132};
133
134/*
135 * RGB conversion functions.
136 *
137 * These functions write an RGB value to some memory in some predefined format.
138 * The naming convention corresponds to the format created by these functions.
139 * The functions use the so called network order (i.e. big endian) with respect
140 * to their names.
141 */
142
143static void rgb_0888(void *dst, uint32_t rgb)
144{
145 *((uint32_t *) dst) = host2uint32_t_be((0 << 24) |
146 (RED(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | (BLUE(rgb, 8)));
147}
148
149static void bgr_0888(void *dst, uint32_t rgb)
150{
151 *((uint32_t *) dst) = host2uint32_t_be((0 << 24) |
152 (BLUE(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | (RED(rgb, 8)));
153}
154
155static void rgb_8880(void *dst, uint32_t rgb)
156{
157 *((uint32_t *) dst) = host2uint32_t_be((RED(rgb, 8) << 24) |
158 (GREEN(rgb, 8) << 16) | (BLUE(rgb, 8) << 8) | 0);
159}
160
161static void bgr_8880(void *dst, uint32_t rgb)
162{
163 *((uint32_t *) dst) = host2uint32_t_be((BLUE(rgb, 8) << 24) |
164 (GREEN(rgb, 8) << 16) | (RED(rgb, 8) << 8) | 0);
165}
166
167static void rgb_888(void *dst, uint32_t rgb)
168{
169 ((uint8_t *) dst)[0] = RED(rgb, 8);
170 ((uint8_t *) dst)[1] = GREEN(rgb, 8);
171 ((uint8_t *) dst)[2] = BLUE(rgb, 8);
172}
173
174static void bgr_888(void *dst, uint32_t rgb)
175{
176 ((uint8_t *) dst)[0] = BLUE(rgb, 8);
177 ((uint8_t *) dst)[1] = GREEN(rgb, 8);
178 ((uint8_t *) dst)[2] = RED(rgb, 8);
179}
180
181static void rgb_555_be(void *dst, uint32_t rgb)
182{
183 *((uint16_t *) dst) = host2uint16_t_be(RED(rgb, 5) << 10 |
184 GREEN(rgb, 5) << 5 | BLUE(rgb, 5));
185}
186
187static void rgb_555_le(void *dst, uint32_t rgb)
188{
189 *((uint16_t *) dst) = host2uint16_t_le(RED(rgb, 5) << 10 |
190 GREEN(rgb, 5) << 5 | BLUE(rgb, 5));
191}
192
193static void rgb_565_be(void *dst, uint32_t rgb)
194{
195 *((uint16_t *) dst) = host2uint16_t_be(RED(rgb, 5) << 11 |
196 GREEN(rgb, 6) << 5 | BLUE(rgb, 5));
197}
198
199static void rgb_565_le(void *dst, uint32_t rgb)
200{
201 *((uint16_t *) dst) = host2uint16_t_le(RED(rgb, 5) << 11 |
202 GREEN(rgb, 6) << 5 | BLUE(rgb, 5));
203}
204
205/** BGR 3:2:3
206 *
207 * Even though we try 3:2:3 color scheme here, an 8-bit framebuffer
208 * will most likely use a color palette. The color appearance
209 * will be pretty random and depend on the default installed
210 * palette. This could be fixed by supporting custom palette
211 * and setting it to simulate the 8-bit truecolor.
212 *
213 * Currently we set the palette on the ia32, amd64, ppc32 and sparc64 port.
214 *
215 * Note that the byte is being inverted by this function. The reason is
216 * that we would like to use a color palette where the white color code
217 * is 0 and the black color code is 255, as some machines (Sun Blade 1500)
218 * use these codes for black and white and prevent to set codes
219 * 0 and 255 to other colors.
220 *
221 */
222static void bgr_323(void *dst, uint32_t rgb)
223{
224 *((uint8_t *) dst)
225 = ~((RED(rgb, 3) << 5) | (GREEN(rgb, 2) << 3) | BLUE(rgb, 3));
226}
227
228/** Draw character at given position
229 *
230 */
231static void glyph_draw(fb_instance_t *instance, uint16_t glyph,
232 unsigned int col, unsigned int row, bool overlay)
233{
234 if (!overlay)
235 instance->backbuf[BB_POS(instance, col, row)] = glyph;
236
237 /* Do not output if the framebuffer is used by user space */
238 if ((instance->parea.mapped) && (!console_override))
239 return;
240
241 /* Check whether the glyph should be visible */
242 if (row < instance->offset_row)
243 return;
244
245 unsigned int rel_row = row - instance->offset_row;
246 if (rel_row >= instance->rowtrim)
247 return;
248
249 unsigned int x = COL2X(col);
250 unsigned int y = ROW2Y(rel_row);
251
252 for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++)
253 memcpy(&instance->addr[FB_POS(instance, x, y + yd)],
254 &instance->glyphs[GLYPH_POS(instance, glyph, yd)],
255 instance->glyphscanline);
256}
257
258/** Scroll screen down by one row
259 *
260 */
261static void screen_scroll(fb_instance_t *instance)
262{
263 if ((!instance->parea.mapped) || (console_override)) {
264 for (unsigned int rel_row = 0; rel_row < instance->rowtrim; rel_row++) {
265 unsigned int y = ROW2Y(rel_row);
266 unsigned int row = rel_row + instance->offset_row;
267
268 for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++) {
269 unsigned int x;
270 unsigned int col;
271
272 for (col = 0, x = 0; col < instance->cols;
273 col++, x += FONT_WIDTH) {
274 uint16_t glyph;
275
276 if (row < instance->rows - 1) {
277 if (instance->backbuf[BB_POS(instance, col, row)] ==
278 instance->backbuf[BB_POS(instance, col, row + 1)])
279 continue;
280
281 glyph = instance->backbuf[BB_POS(instance, col, row + 1)];
282 } else
283 glyph = 0;
284
285 memcpy(&instance->addr[FB_POS(instance, x, y + yd)],
286 &instance->glyphs[GLYPH_POS(instance, glyph, yd)],
287 instance->glyphscanline);
288 }
289 }
290 }
291 }
292
293 /*
294 * Implement backbuffer scrolling by wrapping around
295 * the cyclic buffer.
296 */
297
298 instance->start_row++;
299 if (instance->start_row == instance->rows)
300 instance->start_row = 0;
301
302 memsetw(&instance->backbuf[BB_POS(instance, 0, instance->rows - 1)],
303 instance->cols, 0);
304}
305
306static void cursor_put(fb_instance_t *instance)
307{
308 unsigned int col = instance->position % instance->cols;
309 unsigned int row = instance->position / instance->cols;
310
311 glyph_draw(instance, fb_font_glyph(U_CURSOR), col, row, true);
312}
313
314static void cursor_remove(fb_instance_t *instance)
315{
316 unsigned int col = instance->position % instance->cols;
317 unsigned int row = instance->position / instance->cols;
318
319 glyph_draw(instance, instance->backbuf[BB_POS(instance, col, row)],
320 col, row, true);
321}
322
323/** Render glyphs
324 *
325 * Convert glyphs from device independent font
326 * description to current visual representation.
327 *
328 */
329static void glyphs_render(fb_instance_t *instance)
330{
331 /* Prerender glyphs */
332 uint16_t glyph;
333
334 for (glyph = 0; glyph < FONT_GLYPHS; glyph++) {
335 uint32_t fg_color;
336
337 if (glyph == FONT_GLYPHS - 1)
338 fg_color = INV_COLOR;
339 else
340 fg_color = FG_COLOR;
341
342 unsigned int y;
343
344 for (y = 0; y < FONT_SCANLINES; y++) {
345 unsigned int x;
346
347 for (x = 0; x < FONT_WIDTH; x++) {
348 void *dst =
349 &instance->glyphs[GLYPH_POS(instance, glyph, y) +
350 x * instance->pixelbytes];
351 uint32_t rgb = (fb_font[glyph][y] &
352 (1 << (7 - x))) ? fg_color : BG_COLOR;
353 instance->rgb_conv(dst, rgb);
354 }
355 }
356 }
357
358 /* Prerender background scanline */
359 unsigned int x;
360
361 for (x = 0; x < instance->xres; x++)
362 instance->rgb_conv(&instance->bgscan[x * instance->pixelbytes], BG_COLOR);
363}
364
365static void fb_redraw_internal(fb_instance_t *instance)
366{
367 for (unsigned int rel_row = 0; rel_row < instance->rowtrim; rel_row++) {
368 unsigned int y = ROW2Y(rel_row);
369 unsigned int row = rel_row + instance->offset_row;
370
371 for (unsigned int yd = 0; yd < FONT_SCANLINES; yd++) {
372 unsigned int x;
373 unsigned int col;
374
375 for (col = 0, x = 0; col < instance->cols;
376 col++, x += FONT_WIDTH) {
377 uint16_t glyph =
378 instance->backbuf[BB_POS(instance, col, row)];
379 void *dst = &instance->addr[FB_POS(instance, x, y + yd)];
380 void *src = &instance->glyphs[GLYPH_POS(instance, glyph, yd)];
381 memcpy(dst, src, instance->glyphscanline);
382 }
383 }
384 }
385
386 if (COL2X(instance->cols) < instance->xres) {
387 unsigned int y;
388 unsigned int size =
389 (instance->xres - COL2X(instance->cols)) * instance->pixelbytes;
390
391 for (y = 0; y < instance->yres; y++)
392 memcpy(&instance->addr[FB_POS(instance, COL2X(instance->cols), y)],
393 instance->bgscan, size);
394 }
395
396 if (ROW2Y(instance->rowtrim) < instance->yres) {
397 unsigned int y;
398
399 for (y = ROW2Y(instance->rowtrim); y < instance->yres; y++)
400 memcpy(&instance->addr[FB_POS(instance, 0, y)],
401 instance->bgscan, instance->bgscanbytes);
402 }
403}
404
405/** Print character to screen
406 *
407 * Emulate basic terminal commands.
408 *
409 */
410static void fb_putchar(outdev_t *dev, wchar_t ch)
411{
412 fb_instance_t *instance = (fb_instance_t *) dev->data;
413 spinlock_lock(&instance->lock);
414
415 switch (ch) {
416 case '\n':
417 cursor_remove(instance);
418 instance->position += instance->cols;
419 instance->position -= instance->position % instance->cols;
420 break;
421 case '\r':
422 cursor_remove(instance);
423 instance->position -= instance->position % instance->cols;
424 break;
425 case '\b':
426 cursor_remove(instance);
427 if (instance->position % instance->cols)
428 instance->position--;
429 break;
430 case '\t':
431 cursor_remove(instance);
432 do {
433 glyph_draw(instance, fb_font_glyph(' '),
434 instance->position % instance->cols,
435 instance->position / instance->cols, false);
436 instance->position++;
437 } while (((instance->position % instance->cols) % 8 != 0) &&
438 (instance->position < instance->cols * instance->rows));
439 break;
440 default:
441 glyph_draw(instance, fb_font_glyph(ch),
442 instance->position % instance->cols,
443 instance->position / instance->cols, false);
444 instance->position++;
445 }
446
447 if (instance->position >= instance->cols * instance->rows) {
448 instance->position -= instance->cols;
449 screen_scroll(instance);
450 }
451
452 cursor_put(instance);
453
454 spinlock_unlock(&instance->lock);
455}
456
457/** Scroll the framebuffer up
458 *
459 */
460static void fb_scroll_up(outdev_t *dev)
461{
462 fb_instance_t *instance = (fb_instance_t *) dev->data;
463 spinlock_lock(&instance->lock);
464
465 if (instance->offset_row >= instance->rowtrim / 2)
466 instance->offset_row -= instance->rowtrim / 2;
467 else
468 instance->offset_row = 0;
469
470 fb_redraw_internal(instance);
471 cursor_put(instance);
472
473 spinlock_unlock(&instance->lock);
474}
475
476/** Scroll the framebuffer down
477 *
478 */
479static void fb_scroll_down(outdev_t *dev)
480{
481 fb_instance_t *instance = (fb_instance_t *) dev->data;
482 spinlock_lock(&instance->lock);
483
484 if (instance->offset_row + instance->rowtrim / 2 <=
485 instance->rows - instance->rowtrim)
486 instance->offset_row += instance->rowtrim / 2;
487 else
488 instance->offset_row = instance->rows - instance->rowtrim;
489
490 fb_redraw_internal(instance);
491 cursor_put(instance);
492
493 spinlock_unlock(&instance->lock);
494}
495
496/** Refresh the screen
497 *
498 */
499static void fb_redraw(outdev_t *dev)
500{
501 fb_instance_t *instance = (fb_instance_t *) dev->data;
502
503 spinlock_lock(&instance->lock);
504 fb_redraw_internal(instance);
505 spinlock_unlock(&instance->lock);
506}
507
508/** Initialize framebuffer as a output character device
509 *
510 */
511outdev_t *fb_init(fb_properties_t *props)
512{
513 ASSERT(props);
514 ASSERT(props->x > 0);
515 ASSERT(props->y > 0);
516 ASSERT(props->scan > 0);
517
518 rgb_conv_t rgb_conv;
519 unsigned int pixelbytes;
520
521 switch (props->visual) {
522 case VISUAL_INDIRECT_8:
523 rgb_conv = bgr_323;
524 pixelbytes = 1;
525 break;
526 case VISUAL_RGB_5_5_5_LE:
527 rgb_conv = rgb_555_le;
528 pixelbytes = 2;
529 break;
530 case VISUAL_RGB_5_5_5_BE:
531 rgb_conv = rgb_555_be;
532 pixelbytes = 2;
533 break;
534 case VISUAL_RGB_5_6_5_LE:
535 rgb_conv = rgb_565_le;
536 pixelbytes = 2;
537 break;
538 case VISUAL_RGB_5_6_5_BE:
539 rgb_conv = rgb_565_be;
540 pixelbytes = 2;
541 break;
542 case VISUAL_RGB_8_8_8:
543 rgb_conv = rgb_888;
544 pixelbytes = 3;
545 break;
546 case VISUAL_BGR_8_8_8:
547 rgb_conv = bgr_888;
548 pixelbytes = 3;
549 break;
550 case VISUAL_RGB_8_8_8_0:
551 rgb_conv = rgb_8880;
552 pixelbytes = 4;
553 break;
554 case VISUAL_RGB_0_8_8_8:
555 rgb_conv = rgb_0888;
556 pixelbytes = 4;
557 break;
558 case VISUAL_BGR_0_8_8_8:
559 rgb_conv = bgr_0888;
560 pixelbytes = 4;
561 break;
562 case VISUAL_BGR_8_8_8_0:
563 rgb_conv = bgr_8880;
564 pixelbytes = 4;
565 break;
566 default:
567 LOG("Unsupported visual.");
568 return NULL;
569 }
570
571 outdev_t *fbdev = malloc(sizeof(outdev_t), FRAME_ATOMIC);
572 if (!fbdev)
573 return NULL;
574
575 fb_instance_t *instance = malloc(sizeof(fb_instance_t), FRAME_ATOMIC);
576 if (!instance) {
577 free(fbdev);
578 return NULL;
579 }
580
581 outdev_initialize("fbdev", fbdev, &fbdev_ops);
582 fbdev->data = instance;
583
584 spinlock_initialize(&instance->lock, "*fb.instance.lock");
585
586 instance->rgb_conv = rgb_conv;
587 instance->pixelbytes = pixelbytes;
588 instance->xres = props->x;
589 instance->yres = props->y;
590 instance->scanline = props->scan;
591
592 instance->rowtrim = Y2ROW(instance->yres);
593
594 instance->cols = X2COL(instance->xres);
595 instance->rows = FB_PAGES * instance->rowtrim;
596
597 instance->start_row = instance->rows - instance->rowtrim;
598 instance->offset_row = instance->start_row;
599 instance->position = instance->start_row * instance->cols;
600
601 instance->glyphscanline = FONT_WIDTH * instance->pixelbytes;
602 instance->glyphbytes = ROW2Y(instance->glyphscanline);
603 instance->bgscanbytes = instance->xres * instance->pixelbytes;
604
605 size_t fbsize = instance->scanline * instance->yres;
606 size_t bbsize = instance->cols * instance->rows * sizeof(uint16_t);
607 size_t glyphsize = FONT_GLYPHS * instance->glyphbytes;
608
609 instance->addr = (uint8_t *) km_map((uintptr_t) props->addr, fbsize,
610 PAGE_WRITE | PAGE_NOT_CACHEABLE);
611 if (!instance->addr) {
612 LOG("Unable to map framebuffer.");
613 free(instance);
614 free(fbdev);
615 return NULL;
616 }
617
618 instance->backbuf = (uint16_t *) malloc(bbsize, 0);
619 if (!instance->backbuf) {
620 LOG("Unable to allocate backbuffer.");
621 free(instance);
622 free(fbdev);
623 return NULL;
624 }
625
626 instance->glyphs = (uint8_t *) malloc(glyphsize, 0);
627 if (!instance->glyphs) {
628 LOG("Unable to allocate glyphs.");
629 free(instance->backbuf);
630 free(instance);
631 free(fbdev);
632 return NULL;
633 }
634
635 instance->bgscan = malloc(instance->bgscanbytes, 0);
636 if (!instance->bgscan) {
637 LOG("Unable to allocate background pixel.");
638 free(instance->glyphs);
639 free(instance->backbuf);
640 free(instance);
641 free(fbdev);
642 return NULL;
643 }
644
645 memsetw(instance->backbuf, instance->cols * instance->rows, 0);
646 glyphs_render(instance);
647
648 link_initialize(&instance->parea.link);
649 instance->parea.pbase = props->addr;
650 instance->parea.frames = SIZE2FRAMES(fbsize);
651 instance->parea.unpriv = false;
652 instance->parea.mapped = false;
653 ddi_parea_register(&instance->parea);
654
655 if (!fb_exported) {
656 /*
657 * We export the kernel framebuffer for uspace usage.
658 * This is used in the case the uspace framebuffer
659 * driver is not self-sufficient.
660 */
661 sysinfo_set_item_val("fb", NULL, true);
662 sysinfo_set_item_val("fb.kind", NULL, 1);
663 sysinfo_set_item_val("fb.width", NULL, instance->xres);
664 sysinfo_set_item_val("fb.height", NULL, instance->yres);
665 sysinfo_set_item_val("fb.scanline", NULL, instance->scanline);
666 sysinfo_set_item_val("fb.visual", NULL, props->visual);
667 sysinfo_set_item_val("fb.address.physical", NULL, props->addr);
668
669 fb_exported = true;
670 }
671
672 fb_redraw(fbdev);
673 return fbdev;
674}
675
676/** @}
677 */
Note: See TracBrowser for help on using the repository browser.