vt_fb.c revision 271684
147133Sobrien/*- 247133Sobrien * Copyright (c) 2013 The FreeBSD Foundation 347133Sobrien * All rights reserved. 447133Sobrien * 547133Sobrien * This software was developed by Aleksandr Rybalko under sponsorship from the 647133Sobrien * FreeBSD Foundation. 747133Sobrien * 847133Sobrien * Redistribution and use in source and binary forms, with or without 947133Sobrien * modification, are permitted provided that the following conditions 1047133Sobrien * are met: 1147133Sobrien * 1. Redistributions of source code must retain the above copyright 1247133Sobrien * notice, this list of conditions and the following disclaimer. 1347133Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1447133Sobrien * notice, this list of conditions and the following disclaimer in the 1547133Sobrien * documentation and/or other materials provided with the distribution. 1647133Sobrien * 1747133Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1847133Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1947133Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2047133Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2147133Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2247133Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2347133Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2447133Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2547133Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2651594Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2750477Speter * SUCH DAMAGE. 2847133Sobrien * 2955723Simp * $FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 271684 2014-09-16 18:02:24Z dumbbell $ 3055723Simp */ 3147133Sobrien 3247133Sobrien#include <sys/cdefs.h> 3348114Sobrien__FBSDID("$FreeBSD: head/sys/dev/vt/hw/fb/vt_fb.c 271684 2014-09-16 18:02:24Z dumbbell $"); 3448114Sobrien 3548114Sobrien#include <sys/param.h> 3648114Sobrien#include <sys/systm.h> 3748114Sobrien#include <sys/malloc.h> 3848114Sobrien#include <sys/queue.h> 3948114Sobrien#include <sys/fbio.h> 4048114Sobrien#include <dev/vt/vt.h> 4148114Sobrien#include <dev/vt/hw/fb/vt_fb.h> 4248114Sobrien#include <dev/vt/colors/vt_termcolors.h> 4348114Sobrien 4448114Sobrienstatic struct vt_driver vt_fb_driver = { 4548114Sobrien .vd_name = "fb", 4648114Sobrien .vd_init = vt_fb_init, 4748114Sobrien .vd_blank = vt_fb_blank, 4848114Sobrien .vd_bitblt_text = vt_fb_bitblt_text, 4948114Sobrien .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 5048114Sobrien .vd_drawrect = vt_fb_drawrect, 5148114Sobrien .vd_setpixel = vt_fb_setpixel, 5248114Sobrien .vd_postswitch = vt_fb_postswitch, 5348114Sobrien .vd_priority = VD_PRIORITY_GENERIC+10, 5448114Sobrien .vd_fb_ioctl = vt_fb_ioctl, 5548114Sobrien .vd_fb_mmap = vt_fb_mmap, 5648114Sobrien}; 5748114Sobrien 5848114SobrienVT_DRIVER_DECLARE(vt_fb, vt_fb_driver); 5948114Sobrien 6048114Sobrienstatic void 6148114Sobrienvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 6248114Sobrien{ 6348114Sobrien 6448114Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 6548114Sobrien *(uint8_t *)(sc->fb_vbase + o) = v; 6648114Sobrien} 6748114Sobrien 6848114Sobrienstatic void 6947133Sobrienvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 7047133Sobrien{ 7148114Sobrien 7248114Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 7348114Sobrien *(uint16_t *)(sc->fb_vbase + o) = v; 7448114Sobrien} 7547133Sobrien 7648114Sobrienstatic void 7748114Sobrienvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 7848114Sobrien{ 7947133Sobrien 8048114Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 8148114Sobrien *(uint32_t *)(sc->fb_vbase + o) = v; 8248114Sobrien} 8348114Sobrien 8447133Sobrienint 8548114Sobrienvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td) 8647133Sobrien{ 8748114Sobrien struct fb_info *info; 8847133Sobrien int error = 0; 8948114Sobrien 9048114Sobrien info = vd->vd_softc; 9148114Sobrien 9248114Sobrien switch (cmd) { 9347133Sobrien case FBIOGTYPE: 9448114Sobrien bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 9548114Sobrien break; 9648114Sobrien 9748114Sobrien case FBIO_GETWINORG: /* get frame buffer window origin */ 9848114Sobrien *(u_int *)data = 0; 9948114Sobrien break; 10048114Sobrien 10148114Sobrien case FBIO_GETDISPSTART: /* get display start address */ 10248114Sobrien ((video_display_start_t *)data)->x = 0; 10348114Sobrien ((video_display_start_t *)data)->y = 0; 10448114Sobrien break; 10548114Sobrien 10648114Sobrien case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 10748114Sobrien *(u_int *)data = info->fb_stride; 10848114Sobrien break; 10947133Sobrien 11048114Sobrien case FBIO_BLANK: /* blank display */ 11148114Sobrien if (vd->vd_driver->vd_blank == NULL) 11247133Sobrien return (ENODEV); 11347133Sobrien vd->vd_driver->vd_blank(vd, TC_BLACK); 11448114Sobrien break; 11547133Sobrien 11648114Sobrien default: 11747133Sobrien error = ENOIOCTL; 11848114Sobrien break; 11948114Sobrien } 12048114Sobrien 12148114Sobrien return (error); 12248114Sobrien} 12347133Sobrien 12448114Sobrienint 12548114Sobrienvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 12648114Sobrien int prot, vm_memattr_t *memattr) 12748114Sobrien{ 12848114Sobrien struct fb_info *info; 12948114Sobrien 13048114Sobrien info = vd->vd_softc; 13148114Sobrien 13247133Sobrien if (info->fb_flags & FB_FLAG_NOMMAP) 13348114Sobrien return (ENODEV); 13448114Sobrien 13548114Sobrien if (offset >= 0 && offset < info->fb_size) { 13648114Sobrien *paddr = info->fb_pbase + offset; 13748114Sobrien #ifdef VM_MEMATTR_WRITE_COMBINING 13848114Sobrien *memattr = VM_MEMATTR_WRITE_COMBINING; 13948114Sobrien #endif 14048114Sobrien return (0); 14148114Sobrien } 14248114Sobrien 14348114Sobrien return (EINVAL); 14448114Sobrien} 14548114Sobrien 14648114Sobrienvoid 14748114Sobrienvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 14847133Sobrien{ 14948114Sobrien struct fb_info *info; 15048114Sobrien uint32_t c; 15148114Sobrien u_int o; 15247133Sobrien 15347133Sobrien info = vd->vd_softc; 15448114Sobrien c = info->fb_cmap[color]; 15547133Sobrien o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 15648114Sobrien 15747133Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 15848114Sobrien 15948114Sobrien switch (FBTYPE_GET_BYTESPP(info)) { 16048114Sobrien case 1: 16148114Sobrien vt_fb_mem_wr1(info, o, c); 16248114Sobrien break; 16347133Sobrien case 2: 16448114Sobrien vt_fb_mem_wr2(info, o, c); 16548114Sobrien break; 16648114Sobrien case 3: 16748114Sobrien vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 16848114Sobrien vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 16948114Sobrien vt_fb_mem_wr1(info, o + 2, c & 0xff); 17048114Sobrien break; 17148114Sobrien case 4: 17248114Sobrien vt_fb_mem_wr4(info, o, c); 17347133Sobrien break; 17448114Sobrien default: 17548114Sobrien /* panic? */ 17648114Sobrien return; 17748114Sobrien } 17848114Sobrien 17947133Sobrien} 18048114Sobrien 18148114Sobrienvoid 18247133Sobrienvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 18347133Sobrien term_color_t color) 18447133Sobrien{ 18548114Sobrien int x, y; 18648114Sobrien 18747133Sobrien for (y = y1; y <= y2; y++) { 18847133Sobrien if (fill || (y == y1) || (y == y2)) { 18947133Sobrien for (x = x1; x <= x2; x++) 19048114Sobrien vt_fb_setpixel(vd, x, y, color); 19148114Sobrien } else { 19248114Sobrien vt_fb_setpixel(vd, x1, y, color); 19348114Sobrien vt_fb_setpixel(vd, x2, y, color); 19448114Sobrien } 19547133Sobrien } 19648114Sobrien} 19748114Sobrien 19848114Sobrienvoid 19948114Sobrienvt_fb_blank(struct vt_device *vd, term_color_t color) 20048114Sobrien{ 20148114Sobrien struct fb_info *info; 20248114Sobrien uint32_t c; 20348114Sobrien u_int o, h; 20448114Sobrien 20548114Sobrien info = vd->vd_softc; 20648114Sobrien c = info->fb_cmap[color]; 20748114Sobrien 20848114Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 20948114Sobrien 21048114Sobrien switch (FBTYPE_GET_BYTESPP(info)) { 21148114Sobrien case 1: 21248114Sobrien for (h = 0; h < info->fb_height; h++) 21348114Sobrien for (o = 0; o < info->fb_stride; o++) 21448114Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 21548114Sobrien break; 21648114Sobrien case 2: 21748114Sobrien for (h = 0; h < info->fb_height; h++) 21848114Sobrien for (o = 0; o < info->fb_stride; o += 2) 21948114Sobrien vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 22048114Sobrien break; 22148114Sobrien case 3: 22248114Sobrien for (h = 0; h < info->fb_height; h++) 22348114Sobrien for (o = 0; o < info->fb_stride; o += 3) { 22448114Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o, 22548114Sobrien (c >> 16) & 0xff); 22648114Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 22748114Sobrien (c >> 8) & 0xff); 22848114Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 22948114Sobrien c & 0xff); 23048114Sobrien } 23148114Sobrien break; 23248114Sobrien case 4: 23348114Sobrien for (h = 0; h < info->fb_height; h++) 23448114Sobrien for (o = 0; o < info->fb_stride; o += 4) 23548114Sobrien vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 23648114Sobrien break; 23748114Sobrien default: 23848114Sobrien /* panic? */ 23948114Sobrien return; 24048114Sobrien } 24148114Sobrien} 24248114Sobrien 24348114Sobrienvoid 24448114Sobrienvt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 24548114Sobrien const uint8_t *pattern, const uint8_t *mask, 24648114Sobrien unsigned int width, unsigned int height, 24748114Sobrien unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 24848114Sobrien{ 24948114Sobrien struct fb_info *info; 25048114Sobrien uint32_t fgc, bgc, cc, o; 25148114Sobrien int c, l, bpp, bpl; 25248114Sobrien u_long line; 25348114Sobrien uint8_t b, m; 25448114Sobrien const uint8_t *ch; 25548114Sobrien 25648114Sobrien info = vd->vd_softc; 25748114Sobrien bpp = FBTYPE_GET_BYTESPP(info); 25848114Sobrien fgc = info->fb_cmap[fg]; 25948114Sobrien bgc = info->fb_cmap[bg]; 26048114Sobrien b = m = 0; 26148114Sobrien bpl = (width + 7) >> 3; /* Bytes per source line. */ 26248114Sobrien 26348114Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 26448114Sobrien 26548114Sobrien line = (info->fb_stride * y) + (x * bpp); 26648114Sobrien for (l = 0; 26748114Sobrien l < height && y + l < vw->vw_draw_area.tr_end.tp_row; 26848114Sobrien l++) { 26948114Sobrien ch = pattern; 27048114Sobrien for (c = 0; 27148114Sobrien c < width && x + c < vw->vw_draw_area.tr_end.tp_col; 27248114Sobrien c++) { 27348114Sobrien if (c % 8 == 0) 27448114Sobrien b = *ch++; 27548114Sobrien else 27648114Sobrien b <<= 1; 27748114Sobrien if (mask != NULL) { 27848114Sobrien if (c % 8 == 0) 27948114Sobrien m = *mask++; 28048114Sobrien else 28148114Sobrien m <<= 1; 28248114Sobrien /* Skip pixel write, if mask has no bit set. */ 28348114Sobrien if ((m & 0x80) == 0) 28448114Sobrien continue; 28548114Sobrien } 286121099Srsm o = line + (c * bpp); 28748114Sobrien cc = b & 0x80 ? fgc : bgc; 28848114Sobrien 28948114Sobrien switch(bpp) { 29048114Sobrien case 1: 29148114Sobrien vt_fb_mem_wr1(info, o, cc); 292121099Srsm break; 293121099Srsm case 2: 294121099Srsm vt_fb_mem_wr2(info, o, cc); 29548114Sobrien break; 29648114Sobrien case 3: 29748114Sobrien /* Packed mode, so unaligned. Byte access. */ 29848114Sobrien vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 29948114Sobrien vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 30048114Sobrien vt_fb_mem_wr1(info, o + 2, cc & 0xff); 30148114Sobrien break; 30248114Sobrien case 4: 30348114Sobrien vt_fb_mem_wr4(info, o, cc); 30448114Sobrien break; 30548114Sobrien default: 30648114Sobrien /* panic? */ 30747133Sobrien break; 30847133Sobrien } 30948114Sobrien } 31048114Sobrien line += info->fb_stride; 31148114Sobrien pattern += bpl; 31248114Sobrien } 31348114Sobrien} 31447133Sobrien 31548114Sobrienvoid 31648114Sobrienvt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 31748114Sobrien const term_rect_t *area) 31848114Sobrien{ 31948114Sobrien unsigned int col, row, x, y; 32048114Sobrien struct vt_font *vf; 32148114Sobrien term_char_t c; 32248114Sobrien term_color_t fg, bg; 32348114Sobrien const uint8_t *pattern; 32448114Sobrien 32548114Sobrien vf = vw->vw_font; 32647133Sobrien 32748114Sobrien for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 32848114Sobrien for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 32948114Sobrien ++col) { 33048114Sobrien x = col * vf->vf_width + 33148114Sobrien vw->vw_draw_area.tr_begin.tp_col; 33248114Sobrien y = row * vf->vf_height + 33348114Sobrien vw->vw_draw_area.tr_begin.tp_row; 33448114Sobrien 33548114Sobrien c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 33648114Sobrien pattern = vtfont_lookup(vf, c); 33748114Sobrien vt_determine_colors(c, 33848114Sobrien VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 33948114Sobrien 34048114Sobrien vt_fb_bitblt_bitmap(vd, vw, 34148114Sobrien pattern, NULL, vf->vf_width, vf->vf_height, 34248114Sobrien x, y, fg, bg); 34348114Sobrien } 34448114Sobrien } 34548114Sobrien 34648114Sobrien#ifndef SC_NO_CUTPASTE 34748114Sobrien if (!vd->vd_mshown) 34848114Sobrien return; 34948114Sobrien 35048114Sobrien term_rect_t drawn_area; 35148114Sobrien 35248114Sobrien drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 35348114Sobrien drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 35448114Sobrien drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 35547133Sobrien drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 35648114Sobrien 35748114Sobrien if (vt_is_cursor_in_area(vd, &drawn_area)) { 35848114Sobrien vt_fb_bitblt_bitmap(vd, vw, 35948114Sobrien vd->vd_mcursor->map, vd->vd_mcursor->mask, 36048114Sobrien vd->vd_mcursor->width, vd->vd_mcursor->height, 36148114Sobrien vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 36247133Sobrien vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 36347133Sobrien vd->vd_mcursor_fg, vd->vd_mcursor_bg); 36448114Sobrien } 36548114Sobrien#endif 36648114Sobrien} 36748114Sobrien 36848114Sobrienvoid 36948114Sobrienvt_fb_postswitch(struct vt_device *vd) 37047133Sobrien{ 37148114Sobrien struct fb_info *info; 37248114Sobrien 373121099Srsm info = vd->vd_softc; 37448114Sobrien 37548114Sobrien if (info->enter != NULL) 37648114Sobrien info->enter(info->fb_priv); 37748114Sobrien} 37847133Sobrien 37948114Sobrienstatic int 380121099Srsmvt_fb_init_cmap(uint32_t *cmap, int depth) 381121099Srsm{ 38248114Sobrien 38348114Sobrien switch (depth) { 38448114Sobrien case 8: 38548114Sobrien return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 38648114Sobrien 0x7, 5, 0x7, 2, 0x3, 0)); 38748114Sobrien case 15: 38848114Sobrien return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 38948114Sobrien 0x1f, 10, 0x1f, 5, 0x1f, 0)); 39048114Sobrien case 16: 39148114Sobrien return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 39248114Sobrien 0x1f, 11, 0x3f, 5, 0x1f, 0)); 39348114Sobrien case 24: 39448114Sobrien case 32: /* Ignore alpha. */ 39548114Sobrien return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 39648114Sobrien 0xff, 16, 0xff, 8, 0xff, 0)); 39747133Sobrien default: 39848114Sobrien return (1); 39947133Sobrien } 40048114Sobrien} 40148114Sobrien 40248114Sobrienint 40348114Sobrienvt_fb_init(struct vt_device *vd) 40447133Sobrien{ 40547133Sobrien struct fb_info *info; 40647133Sobrien int err; 40748114Sobrien 40847133Sobrien info = vd->vd_softc; 40948114Sobrien vd->vd_height = info->fb_height; 41048114Sobrien vd->vd_width = info->fb_width; 41148114Sobrien 41248114Sobrien if (info->fb_size == 0) 41347133Sobrien return (CN_DEAD); 41448114Sobrien 41547133Sobrien if (info->fb_pbase == 0) 41648114Sobrien info->fb_flags |= FB_FLAG_NOMMAP; 41747133Sobrien 41848114Sobrien if (info->fb_cmsize <= 0) { 41948114Sobrien err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info)); 42048114Sobrien if (err) 42148114Sobrien return (CN_DEAD); 42247133Sobrien info->fb_cmsize = 16; 42347133Sobrien } 42447133Sobrien 42548114Sobrien /* Clear the screen. */ 42647133Sobrien vd->vd_driver->vd_blank(vd, TC_BLACK); 42748114Sobrien 42848114Sobrien /* Wakeup screen. KMS need this. */ 42948114Sobrien vt_fb_postswitch(vd); 43047133Sobrien 43148114Sobrien return (CN_INTERNAL); 43248114Sobrien} 43348114Sobrien 43448114Sobrienint 43547133Sobrienvt_fb_attach(struct fb_info *info) 43648114Sobrien{ 43747133Sobrien 43848114Sobrien vt_allocate(&vt_fb_driver, info); 43947133Sobrien 44048114Sobrien return (0); 44148114Sobrien} 44248114Sobrien 44348114Sobrienvoid 44448114Sobrienvt_fb_resume(void) 44548114Sobrien{ 44648114Sobrien 44747133Sobrien vt_resume(); 44848114Sobrien} 44948114Sobrien 45048114Sobrienvoid 45148114Sobrienvt_fb_suspend(void) 45248114Sobrien{ 45348114Sobrien 45448114Sobrien vt_suspend(); 45548114Sobrien} 45647133Sobrien