/* * Copyright (c) 2008 Martin Decky * Copyright (c) 2006 Ondrej Palkovsky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup genarch * @{ */ /** @file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SPINLOCK_INITIALIZE(fb_lock); /**< Physical memory area for fb. */ static parea_t fb_parea; static uint8_t *fb_addr; static uint8_t *backbuf; static uint8_t *glyphs; static void *bgpixel; static unsigned int xres; static unsigned int yres; static unsigned int scanline; static unsigned int glyphscanline; static unsigned int pixelbytes; static unsigned int glyphbytes; static unsigned int cols; static unsigned int rows; static unsigned int position = 0; #define BG_COLOR 0x000080 #define FG_COLOR 0xffff00 #define CURSOR 219 #define RED(x, bits) ((x >> (8 + 8 + 8 - bits)) & ((1 << bits) - 1)) #define GREEN(x, bits) ((x >> (8 + 8 - bits)) & ((1 << bits) - 1)) #define BLUE(x, bits) ((x >> (8 - bits)) & ((1 << bits) - 1)) #define COL2X(col) ((col) * FONT_WIDTH) #define ROW2Y(row) ((row) * FONT_SCANLINES) #define X2COL(x) ((x) / FONT_WIDTH) #define Y2ROW(y) ((y) / FONT_SCANLINES) #define FB_POS(x, y) ((y) * scanline + (x) * pixelbytes) #define BB_POS(col, row) ((row) * cols + (col)) #define GLYPH_POS(glyph, y) ((glyph) * glyphbytes + (y) * glyphscanline) static void (*rgb_conv)(void *, uint32_t); /** ARGB 8:8:8:8 conversion * */ static void rgb_0888(void *dst, uint32_t rgb) { *((uint32_t *) dst) = rgb & 0xffffff; } /** ABGR 8:8:8:8 conversion * */ static void bgr_0888(void *dst, uint32_t rgb) { *((uint32_t *) dst) = (BLUE(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | RED(rgb, 8); } /** BGR 8:8:8 conversion * */ static void rgb_888(void *dst, uint32_t rgb) { #if defined(FB_INVERT_ENDIAN) *((uint32_t *) dst) = (BLUE(rgb, 8) << 16) | (GREEN(rgb, 8) << 8) | RED(rgb, 8) | (*((uint32_t *) dst) & 0xff0000); #else *((uint32_t *) dst) = (rgb & 0xffffff) | (*((uint32_t *) dst) & 0xff0000); #endif } /** RGB 5:5:5 conversion * */ static void rgb_555(void *dst, uint32_t rgb) { *((uint16_t *) dst) = (RED(rgb, 5) << 10) | (GREEN(rgb, 5) << 5) | BLUE(rgb, 5); } /** RGB 5:6:5 conversion * */ static void rgb_565(void *dst, uint32_t rgb) { *((uint16_t *) dst) = (RED(rgb, 5) << 11) | (GREEN(rgb, 6) << 5) | BLUE(rgb, 5); } /** RGB 3:2:3 * * Even though we try 3:2:3 color scheme here, an 8-bit framebuffer * will most likely use a color palette. The color appearance * will be pretty random and depend on the default installed * palette. This could be fixed by supporting custom palette * and setting it to simulate the 8-bit truecolor. * * Currently we set the palette on the ia32, amd64 and sparc64 port. * * Note that the byte is being inverted by this function. The reason is * that we would like to use a color palette where the white color code * is 0 and the black color code is 255, as some machines (Sun Blade 1500) * use these codes for black and white and prevent to set codes * 0 and 255 to other colors. * */ static void rgb_323(void *dst, uint32_t rgb) { *((uint8_t *) dst) = ~((RED(rgb, 3) << 5) | (GREEN(rgb, 2) << 3) | BLUE(rgb, 3)); } /** Draw character at given position * */ static void draw_glyph(uint8_t glyph, unsigned int col, unsigned int row) { unsigned int x = COL2X(col); unsigned int y = ROW2Y(row); unsigned int yd; backbuf[BB_POS(col, row)] = glyph; for (yd = 0; yd < FONT_SCANLINES; yd++) memcpy(&fb_addr[FB_POS(x, y + yd)], &glyphs[GLYPH_POS(glyph, yd)], glyphscanline); } /** Scroll screen down by one row * * */ static void scroll_screen(void) { unsigned int row; for (row = 0; row < rows; row++) { unsigned int y = ROW2Y(row); unsigned int yd; for (yd = 0; yd < FONT_SCANLINES; yd++) { unsigned int x; unsigned int col; for (col = 0, x = 0; col < cols; col++, x += FONT_WIDTH) { uint8_t glyph; if (row < rows - 1) { if (backbuf[BB_POS(col, row)] == backbuf[BB_POS(col, row + 1)]) continue; glyph = backbuf[BB_POS(col, row + 1)]; } else glyph = 0; memcpy(&fb_addr[FB_POS(x, y + yd)], &glyphs[GLYPH_POS(glyph, yd)], glyphscanline); } } } memcpy(backbuf, backbuf + cols, cols * (rows - 1)); memsetb(&backbuf[BB_POS(0, rows - 1)], cols, 0); } static void cursor_put(void) { draw_glyph(CURSOR, position % cols, position / cols); } static void cursor_remove(void) { draw_glyph(0, position % cols, position / cols); } /** Print character to screen * * Emulate basic terminal commands. * */ static void fb_putchar(chardev_t *dev, char ch) { spinlock_lock(&fb_lock); switch (ch) { case '\n': cursor_remove(); position += cols; position -= position % cols; break; case '\r': cursor_remove(); position -= position % cols; break; case '\b': cursor_remove(); if (position % cols) position--; break; case '\t': cursor_remove(); do { draw_glyph((uint8_t) ' ', position % cols, position / cols); position++; } while ((position % 8) && (position < cols * rows)); break; default: draw_glyph((uint8_t) ch, position % cols, position / cols); position++; } if (position >= cols * rows) { position -= cols; scroll_screen(); } cursor_put(); spinlock_unlock(&fb_lock); } static chardev_t framebuffer; static chardev_operations_t fb_ops = { .write = fb_putchar, }; /** Render glyphs * * Convert glyphs from device independent font * description to current visual representation. * */ static void render_glyphs(void) { unsigned int glyph; for (glyph = 0; glyph < FONT_GLYPHS; glyph++) { unsigned int y; for (y = 0; y < FONT_SCANLINES; y++) { unsigned int x; for (x = 0; x < FONT_WIDTH; x++) rgb_conv(&glyphs[GLYPH_POS(glyph, y) + x * pixelbytes], (fb_font[glyph * FONT_SCANLINES + y] & (1 << (7 - x))) ? FG_COLOR : BG_COLOR); } } rgb_conv(bgpixel, BG_COLOR); } /** Refresh the screen * */ void fb_redraw(void) { unsigned int row; for (row = 0; row < rows; row++) { unsigned int y = ROW2Y(row); unsigned int yd; for (yd = 0; yd < FONT_SCANLINES; yd++) { unsigned int x; unsigned int col; for (col = 0, x = 0; col < cols; col++, x += FONT_WIDTH) memcpy(&fb_addr[FB_POS(x, y + yd)], &glyphs[GLYPH_POS(backbuf[BB_POS(col, row)], yd)], glyphscanline); } } if (COL2X(cols) < xres) { unsigned int y; for (y = 0; y < yres; y++) { unsigned int x; for (x = COL2X(cols); x < xres; x++) memcpy(&fb_addr[FB_POS(x, y)], bgpixel, pixelbytes); } } if (ROW2Y(rows) < yres) { unsigned int y; for (y = ROW2Y(rows); y < yres; y++) { unsigned int x; for (x = 0; x < xres; x++) memcpy(&fb_addr[FB_POS(x, y)], bgpixel, pixelbytes); } } } /** Initialize framebuffer as a chardev output device * * @param addr Physical address of the framebuffer * @param x Screen width in pixels * @param y Screen height in pixels * @param scan Bytes per one scanline * @param visual Color model * */ void fb_init(fb_properties_t *props) { switch (props->visual) { case VISUAL_INDIRECT_8: rgb_conv = rgb_323; pixelbytes = 1; break; case VISUAL_RGB_5_5_5: rgb_conv = rgb_555; pixelbytes = 2; break; case VISUAL_RGB_5_6_5: rgb_conv = rgb_565; pixelbytes = 2; break; case VISUAL_RGB_8_8_8: rgb_conv = rgb_888; pixelbytes = 3; break; case VISUAL_RGB_8_8_8_0: rgb_conv = rgb_888; pixelbytes = 4; break; case VISUAL_RGB_0_8_8_8: rgb_conv = rgb_0888; pixelbytes = 4; break; case VISUAL_BGR_0_8_8_8: rgb_conv = bgr_0888; pixelbytes = 4; break; default: panic("Unsupported visual.\n"); } xres = props->x; yres = props->y; scanline = props->scan; cols = xres / FONT_WIDTH; rows = yres / FONT_SCANLINES; glyphscanline = FONT_WIDTH * pixelbytes; glyphbytes = glyphscanline * FONT_SCANLINES; unsigned int fbsize = scanline * yres; unsigned int bbsize = cols * rows; unsigned int glyphsize = FONT_GLYPHS * glyphbytes; backbuf = (uint8_t *) malloc(bbsize, 0); if (!backbuf) panic("Unable to allocate backbuffer.\n"); glyphs = (uint8_t *) malloc(glyphsize, 0); if (!glyphs) panic("Unable to allocate glyphs.\n"); bgpixel = malloc(pixelbytes, 0); if (!bgpixel) panic("Unable to allocate background pixel.\n"); memsetb(backbuf, bbsize, 0); memsetb(glyphs, glyphsize, 0); memsetb(bgpixel, pixelbytes, 0); render_glyphs(); fb_addr = (uint8_t *) hw_map((uintptr_t) props->addr, fbsize); fb_parea.pbase = (uintptr_t) props->addr + props->offset; fb_parea.vbase = (uintptr_t) fb_addr; fb_parea.frames = SIZE2FRAMES(fbsize); fb_parea.cacheable = false; ddi_parea_register(&fb_parea); sysinfo_set_item_val("fb", NULL, true); sysinfo_set_item_val("fb.kind", NULL, 1); sysinfo_set_item_val("fb.width", NULL, xres); sysinfo_set_item_val("fb.height", NULL, yres); sysinfo_set_item_val("fb.scanline", NULL, scanline); sysinfo_set_item_val("fb.visual", NULL, props->visual); sysinfo_set_item_val("fb.address.physical", NULL, props->addr); fb_redraw(); chardev_initialize("fb", &framebuffer, &fb_ops); stdout = &framebuffer; } /** @} */