vt_fb.c revision 271128
189857Sobrien/*- 289857Sobrien * Copyright (c) 2013 The FreeBSD Foundation 389857Sobrien * All rights reserved. 489857Sobrien * 589857Sobrien * This software was developed by Aleksandr Rybalko under sponsorship from the 689857Sobrien * FreeBSD Foundation. 789857Sobrien * 889857Sobrien * Redistribution and use in source and binary forms, with or without 989857Sobrien * modification, are permitted provided that the following conditions 1089857Sobrien * are met: 1189857Sobrien * 1. Redistributions of source code must retain the above copyright 1289857Sobrien * notice, this list of conditions and the following disclaimer. 1389857Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1489857Sobrien * notice, this list of conditions and the following disclaimer in the 1589857Sobrien * documentation and/or other materials provided with the distribution. 1689857Sobrien * 1789857Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1889857Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1989857Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20218822Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2289857Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2389857Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2489857Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2589857Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2689857Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2789857Sobrien * SUCH DAMAGE. 2889857Sobrien * 2989857Sobrien * $FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271128 2014-09-04 20:18:08Z emaste $ 3089857Sobrien */ 3189857Sobrien 3289857Sobrien#include <sys/cdefs.h> 3389857Sobrien__FBSDID("$FreeBSD: stable/10/sys/dev/vt/hw/fb/vt_fb.c 271128 2014-09-04 20:18:08Z emaste $"); 3489857Sobrien 3589857Sobrien#include <sys/param.h> 3689857Sobrien#include <sys/systm.h> 3789857Sobrien#include <sys/malloc.h> 3889857Sobrien#include <sys/queue.h> 3989857Sobrien#include <sys/fbio.h> 4089857Sobrien#include <dev/vt/vt.h> 4189857Sobrien#include <dev/vt/hw/fb/vt_fb.h> 4289857Sobrien#include <dev/vt/colors/vt_termcolors.h> 4389857Sobrien 4489857Sobrienstatic vd_drawrect_t vt_fb_drawrect; 4589857Sobrienstatic vd_setpixel_t vt_fb_setpixel; 4689857Sobrien 4789857Sobrienstatic struct vt_driver vt_fb_driver = { 4889857Sobrien .vd_name = "fb", 4989857Sobrien .vd_init = vt_fb_init, 5089857Sobrien .vd_blank = vt_fb_blank, 5189857Sobrien .vd_bitblt_text = vt_fb_bitblt_text, 5289857Sobrien .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 5389857Sobrien .vd_drawrect = vt_fb_drawrect, 5489857Sobrien .vd_setpixel = vt_fb_setpixel, 5589857Sobrien .vd_postswitch = vt_fb_postswitch, 5689857Sobrien .vd_priority = VD_PRIORITY_GENERIC+10, 5789857Sobrien .vd_fb_ioctl = vt_fb_ioctl, 5889857Sobrien .vd_fb_mmap = vt_fb_mmap, 5989857Sobrien}; 6089857Sobrien 6189857SobrienVT_DRIVER_DECLARE(vt_fb, vt_fb_driver); 6289857Sobrien 6389857Sobrienstatic void 6489857Sobrienvt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 6589857Sobrien{ 6689857Sobrien 6789857Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 6889857Sobrien *(uint8_t *)(sc->fb_vbase + o) = v; 6989857Sobrien} 7089857Sobrien 7189857Sobrienstatic void 7289857Sobrienvt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 7389857Sobrien{ 7489857Sobrien 7589857Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 7689857Sobrien *(uint16_t *)(sc->fb_vbase + o) = v; 7789857Sobrien} 7889857Sobrien 7989857Sobrienstatic void 8089857Sobrienvt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 8189857Sobrien{ 8289857Sobrien 8389857Sobrien KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 8489857Sobrien *(uint32_t *)(sc->fb_vbase + o) = v; 8589857Sobrien} 8689857Sobrien 8789857Sobrienint 8889857Sobrienvt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td) 8989857Sobrien{ 9089857Sobrien struct fb_info *info; 9189857Sobrien int error = 0; 9289857Sobrien 9389857Sobrien info = vd->vd_softc; 9489857Sobrien 9589857Sobrien switch (cmd) { 9689857Sobrien case FBIOGTYPE: 9789857Sobrien bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 9889857Sobrien break; 9989857Sobrien 10089857Sobrien case FBIO_GETWINORG: /* get frame buffer window origin */ 10189857Sobrien *(u_int *)data = 0; 10289857Sobrien break; 10389857Sobrien 10489857Sobrien case FBIO_GETDISPSTART: /* get display start address */ 10589857Sobrien ((video_display_start_t *)data)->x = 0; 10689857Sobrien ((video_display_start_t *)data)->y = 0; 10789857Sobrien break; 10889857Sobrien 10989857Sobrien case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 11089857Sobrien *(u_int *)data = info->fb_stride; 11189857Sobrien break; 11289857Sobrien 11389857Sobrien case FBIO_BLANK: /* blank display */ 11489857Sobrien if (vd->vd_driver->vd_blank == NULL) 11589857Sobrien return (ENODEV); 11689857Sobrien vd->vd_driver->vd_blank(vd, TC_BLACK); 11789857Sobrien break; 11889857Sobrien 11989857Sobrien default: 12089857Sobrien error = ENOIOCTL; 12189857Sobrien break; 12289857Sobrien } 12389857Sobrien 12489857Sobrien return (error); 12589857Sobrien} 12689857Sobrien 12789857Sobrienint 12889857Sobrienvt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 12989857Sobrien int prot, vm_memattr_t *memattr) 13089857Sobrien{ 13189857Sobrien struct fb_info *info; 13289857Sobrien 13389857Sobrien info = vd->vd_softc; 13489857Sobrien 13589857Sobrien if (info->fb_flags & FB_FLAG_NOMMAP) 13689857Sobrien return (ENODEV); 13789857Sobrien 13889857Sobrien if (offset >= 0 && offset < info->fb_size) { 13989857Sobrien *paddr = info->fb_pbase + offset; 14089857Sobrien #ifdef VM_MEMATTR_WRITE_COMBINING 14189857Sobrien *memattr = VM_MEMATTR_WRITE_COMBINING; 14289857Sobrien #endif 14389857Sobrien return (0); 14489857Sobrien } 14589857Sobrien 14689857Sobrien return (EINVAL); 14789857Sobrien} 14889857Sobrien 14989857Sobrienstatic void 15089857Sobrienvt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 15189857Sobrien{ 15289857Sobrien struct fb_info *info; 15389857Sobrien uint32_t c; 15489857Sobrien u_int o; 15589857Sobrien 15689857Sobrien info = vd->vd_softc; 15789857Sobrien c = info->fb_cmap[color]; 15889857Sobrien o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 15989857Sobrien 16089857Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 16189857Sobrien 16289857Sobrien switch (FBTYPE_GET_BYTESPP(info)) { 16389857Sobrien case 1: 16489857Sobrien vt_fb_mem_wr1(info, o, c); 16589857Sobrien break; 16689857Sobrien case 2: 16789857Sobrien vt_fb_mem_wr2(info, o, c); 16889857Sobrien break; 16989857Sobrien case 3: 17089857Sobrien vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 17189857Sobrien vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 17289857Sobrien vt_fb_mem_wr1(info, o + 2, c & 0xff); 17389857Sobrien break; 17489857Sobrien case 4: 17589857Sobrien vt_fb_mem_wr4(info, o, c); 17689857Sobrien break; 17789857Sobrien default: 17889857Sobrien /* panic? */ 17989857Sobrien return; 18089857Sobrien } 18189857Sobrien 18289857Sobrien} 18389857Sobrien 18489857Sobrienstatic void 18589857Sobrienvt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 18689857Sobrien term_color_t color) 18789857Sobrien{ 18889857Sobrien int x, y; 18989857Sobrien 19089857Sobrien for (y = y1; y <= y2; y++) { 19189857Sobrien if (fill || (y == y1) || (y == y2)) { 19289857Sobrien for (x = x1; x <= x2; x++) 19389857Sobrien vt_fb_setpixel(vd, x, y, color); 19489857Sobrien } else { 19589857Sobrien vt_fb_setpixel(vd, x1, y, color); 19689857Sobrien vt_fb_setpixel(vd, x2, y, color); 19789857Sobrien } 19889857Sobrien } 19989857Sobrien} 20089857Sobrien 20189857Sobrienvoid 20289857Sobrienvt_fb_blank(struct vt_device *vd, term_color_t color) 20389857Sobrien{ 20489857Sobrien struct fb_info *info; 20589857Sobrien uint32_t c; 20689857Sobrien u_int o, h; 20789857Sobrien 20889857Sobrien info = vd->vd_softc; 20989857Sobrien c = info->fb_cmap[color]; 21089857Sobrien 21189857Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 21289857Sobrien 21389857Sobrien switch (FBTYPE_GET_BYTESPP(info)) { 21489857Sobrien case 1: 215130561Sobrien for (h = 0; h < info->fb_height; h++) 216130561Sobrien for (o = 0; o < info->fb_stride; o++) 217130561Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 218130561Sobrien break; 219130561Sobrien case 2: 220130561Sobrien for (h = 0; h < info->fb_height; h++) 22189857Sobrien for (o = 0; o < info->fb_stride; o += 2) 22289857Sobrien vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 22389857Sobrien break; 22489857Sobrien case 3: 22589857Sobrien for (h = 0; h < info->fb_height; h++) 22689857Sobrien for (o = 0; o < info->fb_stride; o += 3) { 22789857Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o, 22889857Sobrien (c >> 16) & 0xff); 22989857Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 23089857Sobrien (c >> 8) & 0xff); 231130561Sobrien vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 232130561Sobrien c & 0xff); 233130561Sobrien } 234130561Sobrien break; 235130561Sobrien case 4: 236130561Sobrien for (h = 0; h < info->fb_height; h++) 237130561Sobrien for (o = 0; o < info->fb_stride; o += 4) 238130561Sobrien vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 23989857Sobrien break; 24089857Sobrien default: 24189857Sobrien /* panic? */ 24289857Sobrien return; 24389857Sobrien } 24489857Sobrien} 24589857Sobrien 24689857Sobrienvoid 24789857Sobrienvt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 24889857Sobrien const uint8_t *pattern, const uint8_t *mask, 24989857Sobrien unsigned int width, unsigned int height, 25089857Sobrien unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 25189857Sobrien{ 25289857Sobrien struct fb_info *info; 25389857Sobrien uint32_t fgc, bgc, cc, o; 25489857Sobrien int c, l, bpp, bpl; 25589857Sobrien u_long line; 25689857Sobrien uint8_t b, m; 25789857Sobrien const uint8_t *ch; 25889857Sobrien 25989857Sobrien info = vd->vd_softc; 26089857Sobrien bpp = FBTYPE_GET_BYTESPP(info); 26189857Sobrien fgc = info->fb_cmap[fg]; 26289857Sobrien bgc = info->fb_cmap[bg]; 26389857Sobrien b = m = 0; 26489857Sobrien bpl = (width + 7) >> 3; /* Bytes per source line. */ 26589857Sobrien 26689857Sobrien KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 26789857Sobrien 268130561Sobrien line = (info->fb_stride * y) + (x * bpp); 26989857Sobrien for (l = 0; 270130561Sobrien l < height && y + l < vw->vw_draw_area.tr_end.tp_row; 271130561Sobrien l++) { 27289857Sobrien ch = pattern; 27389857Sobrien for (c = 0; 27489857Sobrien c < width && x + c < vw->vw_draw_area.tr_end.tp_col; 27589857Sobrien c++) { 27689857Sobrien if (c % 8 == 0) 27789857Sobrien b = *ch++; 27889857Sobrien else 27989857Sobrien b <<= 1; 28089857Sobrien if (mask != NULL) { 28189857Sobrien if (c % 8 == 0) 28289857Sobrien m = *mask++; 283130561Sobrien else 284130561Sobrien m <<= 1; 28589857Sobrien /* Skip pixel write, if mask has no bit set. */ 286130561Sobrien if ((m & 0x80) == 0) 287130561Sobrien continue; 288130561Sobrien } 28989857Sobrien o = line + (c * bpp); 29089857Sobrien cc = b & 0x80 ? fgc : bgc; 29189857Sobrien 29289857Sobrien switch(bpp) { 29389857Sobrien case 1: 29489857Sobrien vt_fb_mem_wr1(info, o, cc); 29589857Sobrien break; 29689857Sobrien case 2: 297 vt_fb_mem_wr2(info, o, cc); 298 break; 299 case 3: 300 /* Packed mode, so unaligned. Byte access. */ 301 vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 302 vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 303 vt_fb_mem_wr1(info, o + 2, cc & 0xff); 304 break; 305 case 4: 306 vt_fb_mem_wr4(info, o, cc); 307 break; 308 default: 309 /* panic? */ 310 break; 311 } 312 } 313 line += info->fb_stride; 314 pattern += bpl; 315 } 316} 317 318void 319vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 320 const term_rect_t *area) 321{ 322 unsigned int col, row, x, y; 323 struct vt_font *vf; 324 term_char_t c; 325 term_color_t fg, bg; 326 const uint8_t *pattern; 327 328 vf = vw->vw_font; 329 330 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 331 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 332 ++col) { 333 x = col * vf->vf_width + 334 vw->vw_draw_area.tr_begin.tp_col; 335 y = row * vf->vf_height + 336 vw->vw_draw_area.tr_begin.tp_row; 337 338 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 339 pattern = vtfont_lookup(vf, c); 340 vt_determine_colors(c, 341 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 342 343 vt_fb_bitblt_bitmap(vd, vw, 344 pattern, NULL, vf->vf_width, vf->vf_height, 345 x, y, fg, bg); 346 } 347 } 348 349#ifndef SC_NO_CUTPASTE 350 if (!vd->vd_mshown) 351 return; 352 353 term_rect_t drawn_area; 354 355 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 356 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 357 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 358 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 359 360 if (vt_is_cursor_in_area(vd, &drawn_area)) { 361 vt_fb_bitblt_bitmap(vd, vw, 362 vd->vd_mcursor->map, vd->vd_mcursor->mask, 363 vd->vd_mcursor->width, vd->vd_mcursor->height, 364 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 365 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 366 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 367 } 368#endif 369} 370 371void 372vt_fb_postswitch(struct vt_device *vd) 373{ 374 struct fb_info *info; 375 376 info = vd->vd_softc; 377 378 if (info->enter != NULL) 379 info->enter(info->fb_priv); 380} 381 382static int 383vt_fb_init_cmap(uint32_t *cmap, int depth) 384{ 385 386 switch (depth) { 387 case 8: 388 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 389 0x7, 5, 0x7, 2, 0x3, 0)); 390 case 15: 391 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 392 0x1f, 10, 0x1f, 5, 0x1f, 0)); 393 case 16: 394 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 395 0x1f, 11, 0x3f, 5, 0x1f, 0)); 396 case 24: 397 case 32: /* Ignore alpha. */ 398 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 399 0xff, 16, 0xff, 8, 0xff, 0)); 400 default: 401 return (1); 402 } 403} 404 405int 406vt_fb_init(struct vt_device *vd) 407{ 408 struct fb_info *info; 409 int err; 410 411 info = vd->vd_softc; 412 vd->vd_height = info->fb_height; 413 vd->vd_width = info->fb_width; 414 415 if (info->fb_size == 0) 416 return (CN_DEAD); 417 418 if (info->fb_pbase == 0) 419 info->fb_flags |= FB_FLAG_NOMMAP; 420 421 if (info->fb_cmsize <= 0) { 422 err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info)); 423 if (err) 424 return (CN_DEAD); 425 info->fb_cmsize = 16; 426 } 427 428 /* Clear the screen. */ 429 vd->vd_driver->vd_blank(vd, TC_BLACK); 430 431 /* Wakeup screen. KMS need this. */ 432 vt_fb_postswitch(vd); 433 434 return (CN_INTERNAL); 435} 436 437int 438vt_fb_attach(struct fb_info *info) 439{ 440 441 vt_allocate(&vt_fb_driver, info); 442 443 return (0); 444} 445 446void 447vt_fb_resume(void) 448{ 449 450 vt_resume(); 451} 452 453void 454vt_fb_suspend(void) 455{ 456 457 vt_suspend(); 458} 459