1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2013 The FreeBSD Foundation 5 * 6 * This software was developed by Aleksandr Rybalko under sponsorship from the 7 * FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/malloc.h> 34 #include <sys/queue.h> 35 #include <sys/fbio.h> 36 #include <sys/kernel.h> 37 #include <sys/endian.h> 38 #include <dev/vt/vt.h> 39 #include <dev/vt/hw/fb/vt_fb.h> 40 #include <dev/vt/colors/vt_termcolors.h> 41 42 #include <vm/vm.h> 43 #include <vm/pmap.h> 44 45 static struct vt_driver vt_fb_driver = { 46 .vd_name = "fb", 47 .vd_init = vt_fb_init, 48 .vd_fini = vt_fb_fini, 49 .vd_blank = vt_fb_blank, 50 .vd_bitblt_text = vt_fb_bitblt_text, 51 .vd_invalidate_text = vt_fb_invalidate_text, 52 .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 53 .vd_bitblt_argb = vt_fb_bitblt_argb, 54 .vd_drawrect = vt_fb_drawrect, 55 .vd_setpixel = vt_fb_setpixel, 56 .vd_postswitch = vt_fb_postswitch, 57 .vd_priority = VD_PRIORITY_GENERIC+10, 58 .vd_fb_ioctl = vt_fb_ioctl, 59 .vd_fb_mmap = vt_fb_mmap, 60 .vd_suspend = vt_fb_suspend, 61 .vd_resume = vt_fb_resume, 62 }; 63 64 VT_DRIVER_DECLARE(vt_fb, vt_fb_driver); 65 66 static void 67 vt_fb_mem_wr1(struct fb_info *sc, uint32_t o, uint8_t v) 68 { 69 70 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 71 *(uint8_t *)(sc->fb_vbase + o) = v; 72 } 73 74 static void 75 vt_fb_mem_wr2(struct fb_info *sc, uint32_t o, uint16_t v) 76 { 77 78 KASSERT((o + 1 < sc->fb_size), ("Offset %#08x out of fb size", o + 1)); 79 *(uint16_t *)(sc->fb_vbase + o) = v; 80 } 81 82 static void 83 vt_fb_mem_wr3(struct fb_info *sc, uint32_t o, uint32_t v) 84 { 85 uint8_t *b = (uint8_t *)sc->fb_vbase + o; 86 87 KASSERT((o + 2 < sc->fb_size), ("Offset %#08x out of fb size", o + 2)); 88 /* 89 * We want to write three bytes, independent 90 * of endianness. Multiply _QUAD_LOWWORD and 91 * _QUAD_HIGHWORD by 2 to skip the middle byte. 92 */ 93 b[_QUAD_LOWWORD * 2] = v & 0xff; 94 b[1] = (v >> 8) & 0xff; 95 b[_QUAD_HIGHWORD * 2] = (v >> 16) & 0xff; 96 } 97 98 static void 99 vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 100 { 101 102 KASSERT((o + 3 < sc->fb_size), ("Offset %#08x out of fb size", o + 3)); 103 *(uint32_t *)(sc->fb_vbase + o) = v; 104 } 105 106 int 107 vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td) 108 { 109 struct fb_info *info; 110 int error = 0; 111 112 info = vd->vd_softc; 113 114 switch (cmd) { 115 case FBIOGTYPE: 116 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 117 break; 118 119 case FBIO_GETWINORG: /* get frame buffer window origin */ 120 *(u_int *)data = 0; 121 break; 122 123 case FBIO_GETDISPSTART: /* get display start address */ 124 ((video_display_start_t *)data)->x = 0; 125 ((video_display_start_t *)data)->y = 0; 126 break; 127 128 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 129 *(u_int *)data = info->fb_stride; 130 break; 131 132 case FBIO_BLANK: /* blank display */ 133 if (vd->vd_driver->vd_blank == NULL) 134 return (ENODEV); 135 vd->vd_driver->vd_blank(vd, TC_BLACK); 136 break; 137 138 case FBIO_GETRGBOFFS: /* get RGB offsets */ 139 if (info->fb_rgboffs.red == 0 && info->fb_rgboffs.green == 0 && 140 info->fb_rgboffs.blue == 0) 141 return (ENOTTY); 142 memcpy((struct fb_rgboffs *)data, &info->fb_rgboffs, 143 sizeof(struct fb_rgboffs)); 144 break; 145 146 default: 147 error = ENOIOCTL; 148 break; 149 } 150 151 return (error); 152 } 153 154 int 155 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 156 int prot, vm_memattr_t *memattr) 157 { 158 struct fb_info *info; 159 160 info = vd->vd_softc; 161 162 if (info->fb_flags & FB_FLAG_NOMMAP) 163 return (ENODEV); 164 165 if (offset < info->fb_size) { 166 if (info->fb_pbase == 0) { 167 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 168 } else { 169 *paddr = info->fb_pbase + offset; 170 if (info->fb_flags & FB_FLAG_MEMATTR) 171 *memattr = info->fb_memattr; 172 #ifdef VM_MEMATTR_WRITE_COMBINING 173 else 174 *memattr = VM_MEMATTR_WRITE_COMBINING; 175 #endif 176 } 177 return (0); 178 } 179 180 return (EINVAL); 181 } 182 183 void 184 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 185 { 186 struct fb_info *info; 187 uint32_t c; 188 u_int o; 189 190 info = vd->vd_softc; 191 c = info->fb_cmap[color]; 192 o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 193 194 if (info->fb_flags & FB_FLAG_NOWRITE) 195 return; 196 197 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 198 199 switch (FBTYPE_GET_BYTESPP(info)) { 200 case 1: 201 vt_fb_mem_wr1(info, o, c); 202 break; 203 case 2: 204 vt_fb_mem_wr2(info, o, c); 205 break; 206 case 3: 207 vt_fb_mem_wr3(info, o, c); 208 break; 209 case 4: 210 vt_fb_mem_wr4(info, o, c); 211 break; 212 default: 213 /* panic? */ 214 return; 215 } 216 } 217 218 void 219 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 220 term_color_t color) 221 { 222 int x, y; 223 224 for (y = y1; y <= y2; y++) { 225 if (fill || (y == y1) || (y == y2)) { 226 for (x = x1; x <= x2; x++) 227 vt_fb_setpixel(vd, x, y, color); 228 } else { 229 vt_fb_setpixel(vd, x1, y, color); 230 vt_fb_setpixel(vd, x2, y, color); 231 } 232 } 233 } 234 235 void 236 vt_fb_blank(struct vt_device *vd, term_color_t color) 237 { 238 struct fb_info *info; 239 uint32_t c; 240 u_int o, h; 241 242 info = vd->vd_softc; 243 c = info->fb_cmap[color]; 244 245 if (info->fb_flags & FB_FLAG_NOWRITE) 246 return; 247 248 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 249 250 switch (FBTYPE_GET_BYTESPP(info)) { 251 case 1: 252 for (h = 0; h < info->fb_height; h++) 253 for (o = 0; o < info->fb_stride; o++) 254 vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 255 break; 256 case 2: 257 for (h = 0; h < info->fb_height; h++) 258 for (o = 0; o < info->fb_stride - 1; o += 2) 259 vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 260 break; 261 case 3: 262 for (h = 0; h < info->fb_height; h++) 263 for (o = 0; o < info->fb_stride - 2; o += 3) { 264 vt_fb_mem_wr3(info, h*info->fb_stride + o, c); 265 } 266 break; 267 case 4: 268 for (h = 0; h < info->fb_height; h++) 269 for (o = 0; o < info->fb_stride - 3; o += 4) 270 vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 271 break; 272 default: 273 /* panic? */ 274 return; 275 } 276 } 277 278 void 279 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 280 const uint8_t *pattern, const uint8_t *mask, 281 unsigned int width, unsigned int height, 282 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 283 { 284 struct fb_info *info; 285 uint32_t fgc, bgc, cc, o; 286 int bpp, bpl, xi, yi; 287 int bit, byte; 288 289 info = vd->vd_softc; 290 bpp = FBTYPE_GET_BYTESPP(info); 291 fgc = info->fb_cmap[fg]; 292 bgc = info->fb_cmap[bg]; 293 bpl = (width + 7) / 8; /* Bytes per source line. */ 294 295 if (info->fb_flags & FB_FLAG_NOWRITE) 296 return; 297 298 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 299 300 /* Bound by right and bottom edges. */ 301 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 302 if (y >= vw->vw_draw_area.tr_end.tp_row) 303 return; 304 height = vw->vw_draw_area.tr_end.tp_row - y; 305 } 306 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 307 if (x >= vw->vw_draw_area.tr_end.tp_col) 308 return; 309 width = vw->vw_draw_area.tr_end.tp_col - x; 310 } 311 for (yi = 0; yi < height; yi++) { 312 for (xi = 0; xi < width; xi++) { 313 byte = yi * bpl + xi / 8; 314 bit = 0x80 >> (xi % 8); 315 /* Skip pixel write, if mask bit not set. */ 316 if (mask != NULL && (mask[byte] & bit) == 0) 317 continue; 318 o = (y + yi) * info->fb_stride + (x + xi) * bpp; 319 o += vd->vd_transpose; 320 cc = pattern[byte] & bit ? fgc : bgc; 321 322 switch(bpp) { 323 case 1: 324 vt_fb_mem_wr1(info, o, cc); 325 break; 326 case 2: 327 vt_fb_mem_wr2(info, o, cc); 328 break; 329 case 3: 330 vt_fb_mem_wr3(info, o, cc); 331 break; 332 case 4: 333 vt_fb_mem_wr4(info, o, cc); 334 break; 335 default: 336 /* panic? */ 337 break; 338 } 339 } 340 } 341 } 342 343 int 344 vt_fb_bitblt_argb(struct vt_device *vd, const struct vt_window *vw, 345 const uint8_t *argb, 346 unsigned int width, unsigned int height, 347 unsigned int x, unsigned int y) 348 { 349 struct fb_info *info; 350 uint32_t o, cc; 351 int bpp, xi, yi; 352 353 info = vd->vd_softc; 354 bpp = FBTYPE_GET_BYTESPP(info); 355 if (bpp != 4) 356 return (EOPNOTSUPP); 357 358 if (info->fb_flags & FB_FLAG_NOWRITE) 359 return (0); 360 361 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 362 363 /* Bound by right and bottom edges. */ 364 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 365 if (y >= vw->vw_draw_area.tr_end.tp_row) 366 return (EINVAL); 367 height = vw->vw_draw_area.tr_end.tp_row - y; 368 } 369 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 370 if (x >= vw->vw_draw_area.tr_end.tp_col) 371 return (EINVAL); 372 width = vw->vw_draw_area.tr_end.tp_col - x; 373 } 374 for (yi = 0; yi < height; yi++) { 375 for (xi = 0; xi < (width * 4); xi += 4) { 376 o = (y + yi) * info->fb_stride + (x + (xi / 4)) * bpp; 377 o += vd->vd_transpose; 378 cc = (argb[yi * width * 4 + xi] << 16) | 379 (argb[yi * width * 4 + xi + 1] << 8) | 380 (argb[yi * width * 4 + xi + 2]) | 381 (argb[yi * width * 4 + xi + 3] << 24); 382 vt_fb_mem_wr4(info, o, cc); 383 } 384 } 385 386 return (0); 387 } 388 389 void 390 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 391 const term_rect_t *area) 392 { 393 unsigned int col, row, x, y; 394 struct vt_font *vf; 395 term_char_t c; 396 term_color_t fg, bg; 397 const uint8_t *pattern; 398 size_t z; 399 400 vf = vw->vw_font; 401 402 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 403 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 404 ++col) { 405 x = col * vf->vf_width + 406 vw->vw_draw_area.tr_begin.tp_col; 407 y = row * vf->vf_height + 408 vw->vw_draw_area.tr_begin.tp_row; 409 410 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 411 pattern = vtfont_lookup(vf, c); 412 vt_determine_colors(c, 413 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 414 415 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 416 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 417 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 418 continue; 419 if (vd->vd_drawn && (vd->vd_drawn[z] == c) && 420 vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && 421 vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) 422 continue; 423 424 vt_fb_bitblt_bitmap(vd, vw, 425 pattern, NULL, vf->vf_width, vf->vf_height, 426 x, y, fg, bg); 427 428 if (vd->vd_drawn) 429 vd->vd_drawn[z] = c; 430 if (vd->vd_drawnfg) 431 vd->vd_drawnfg[z] = fg; 432 if (vd->vd_drawnbg) 433 vd->vd_drawnbg[z] = bg; 434 } 435 } 436 437 #ifndef SC_NO_CUTPASTE 438 if (!vd->vd_mshown) 439 return; 440 441 term_rect_t drawn_area; 442 443 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 444 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 445 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 446 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 447 448 if (vt_is_cursor_in_area(vd, &drawn_area)) { 449 vt_fb_bitblt_bitmap(vd, vw, 450 vd->vd_mcursor->map, vd->vd_mcursor->mask, 451 vd->vd_mcursor->width, vd->vd_mcursor->height, 452 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 453 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 454 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 455 } 456 #endif 457 } 458 459 void 460 vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area) 461 { 462 unsigned int col, row; 463 size_t z; 464 465 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 466 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 467 ++col) { 468 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 469 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 470 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 471 continue; 472 if (vd->vd_drawn) 473 vd->vd_drawn[z] = 0; 474 if (vd->vd_drawnfg) 475 vd->vd_drawnfg[z] = 0; 476 if (vd->vd_drawnbg) 477 vd->vd_drawnbg[z] = 0; 478 } 479 } 480 } 481 482 void 483 vt_fb_postswitch(struct vt_device *vd) 484 { 485 struct fb_info *info; 486 487 info = vd->vd_softc; 488 489 if (info->enter != NULL) 490 info->enter(info->fb_priv); 491 } 492 493 static int 494 vt_fb_init_colors(struct fb_info *info) 495 { 496 497 switch (FBTYPE_GET_BPP(info)) { 498 case 8: 499 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 500 0x7, 5, 0x7, 2, 0x3, 0)); 501 case 15: 502 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 503 0x1f, 10, 0x1f, 5, 0x1f, 0)); 504 case 16: 505 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 506 0x1f, 11, 0x3f, 5, 0x1f, 0)); 507 case 24: 508 case 32: /* Ignore alpha. */ 509 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 510 0xff, 16, 0xff, 8, 0xff, 0)); 511 default: 512 return (1); 513 } 514 } 515 516 int 517 vt_fb_init(struct vt_device *vd) 518 { 519 struct fb_info *info; 520 u_int margin; 521 int bg, err; 522 term_color_t c; 523 524 info = vd->vd_softc; 525 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 526 margin = (info->fb_height - vd->vd_height) >> 1; 527 vd->vd_transpose = margin * info->fb_stride; 528 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 529 margin = (info->fb_width - vd->vd_width) >> 1; 530 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 531 vd->vd_video_dev = info->fb_video_dev; 532 533 if (info->fb_size == 0) 534 return (CN_DEAD); 535 536 if (info->fb_pbase == 0 && info->fb_vbase == 0) 537 info->fb_flags |= FB_FLAG_NOMMAP; 538 539 if (info->fb_cmsize <= 0) { 540 err = vt_fb_init_colors(info); 541 if (err) 542 return (CN_DEAD); 543 info->fb_cmsize = 16; 544 } 545 546 c = TC_BLACK; 547 if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) { 548 if (bg == TC_WHITE) 549 bg |= TC_LIGHT; 550 c = bg; 551 } 552 /* Clear the screen. */ 553 vd->vd_driver->vd_blank(vd, c); 554 555 /* Wakeup screen. KMS need this. */ 556 vt_fb_postswitch(vd); 557 558 return (CN_INTERNAL); 559 } 560 561 void 562 vt_fb_fini(struct vt_device *vd, void *softc) 563 { 564 565 vd->vd_video_dev = NULL; 566 } 567 568 int 569 vt_fb_attach(struct fb_info *info) 570 { 571 int ret; 572 573 ret = vt_allocate(&vt_fb_driver, info); 574 575 return (ret); 576 } 577 578 int 579 vt_fb_detach(struct fb_info *info) 580 { 581 int ret; 582 583 ret = vt_deallocate(&vt_fb_driver, info); 584 585 return (ret); 586 } 587 588 void 589 vt_fb_suspend(struct vt_device *vd) 590 { 591 592 vt_suspend(vd); 593 } 594 595 void 596 vt_fb_resume(struct vt_device *vd) 597 { 598 599 vt_resume(vd); 600 } 601