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/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/malloc.h> 35 #include <sys/queue.h> 36 #include <sys/fbio.h> 37 #include <sys/kernel.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 < sc->fb_size), ("Offset %#08x out of fb size", o)); 79 *(uint16_t *)(sc->fb_vbase + o) = v; 80 } 81 82 static void 83 vt_fb_mem_wr4(struct fb_info *sc, uint32_t o, uint32_t v) 84 { 85 86 KASSERT((o < sc->fb_size), ("Offset %#08x out of fb size", o)); 87 *(uint32_t *)(sc->fb_vbase + o) = v; 88 } 89 90 int 91 vt_fb_ioctl(struct vt_device *vd, u_long cmd, caddr_t data, struct thread *td) 92 { 93 struct fb_info *info; 94 int error = 0; 95 96 info = vd->vd_softc; 97 98 switch (cmd) { 99 case FBIOGTYPE: 100 bcopy(info, (struct fbtype *)data, sizeof(struct fbtype)); 101 break; 102 103 case FBIO_GETWINORG: /* get frame buffer window origin */ 104 *(u_int *)data = 0; 105 break; 106 107 case FBIO_GETDISPSTART: /* get display start address */ 108 ((video_display_start_t *)data)->x = 0; 109 ((video_display_start_t *)data)->y = 0; 110 break; 111 112 case FBIO_GETLINEWIDTH: /* get scan line width in bytes */ 113 *(u_int *)data = info->fb_stride; 114 break; 115 116 case FBIO_BLANK: /* blank display */ 117 if (vd->vd_driver->vd_blank == NULL) 118 return (ENODEV); 119 vd->vd_driver->vd_blank(vd, TC_BLACK); 120 break; 121 122 case FBIO_GETRGBOFFS: /* get RGB offsets */ 123 if (info->fb_rgboffs.red == 0 && info->fb_rgboffs.green == 0 && 124 info->fb_rgboffs.blue == 0) 125 return (ENOTTY); 126 memcpy((struct fb_rgboffs *)data, &info->fb_rgboffs, 127 sizeof(struct fb_rgboffs)); 128 break; 129 130 default: 131 error = ENOIOCTL; 132 break; 133 } 134 135 return (error); 136 } 137 138 int 139 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 140 int prot, vm_memattr_t *memattr) 141 { 142 struct fb_info *info; 143 144 info = vd->vd_softc; 145 146 if (info->fb_flags & FB_FLAG_NOMMAP) 147 return (ENODEV); 148 149 if (offset < info->fb_size) { 150 if (info->fb_pbase == 0) { 151 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 152 } else { 153 *paddr = info->fb_pbase + offset; 154 if (info->fb_flags & FB_FLAG_MEMATTR) 155 *memattr = info->fb_memattr; 156 #ifdef VM_MEMATTR_WRITE_COMBINING 157 else 158 *memattr = VM_MEMATTR_WRITE_COMBINING; 159 #endif 160 } 161 return (0); 162 } 163 164 return (EINVAL); 165 } 166 167 void 168 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 169 { 170 struct fb_info *info; 171 uint32_t c; 172 u_int o; 173 174 info = vd->vd_softc; 175 c = info->fb_cmap[color]; 176 o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 177 178 if (info->fb_flags & FB_FLAG_NOWRITE) 179 return; 180 181 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 182 183 switch (FBTYPE_GET_BYTESPP(info)) { 184 case 1: 185 vt_fb_mem_wr1(info, o, c); 186 break; 187 case 2: 188 vt_fb_mem_wr2(info, o, c); 189 break; 190 case 3: 191 vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 192 vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 193 vt_fb_mem_wr1(info, o + 2, c & 0xff); 194 break; 195 case 4: 196 vt_fb_mem_wr4(info, o, c); 197 break; 198 default: 199 /* panic? */ 200 return; 201 } 202 } 203 204 void 205 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 206 term_color_t color) 207 { 208 int x, y; 209 210 for (y = y1; y <= y2; y++) { 211 if (fill || (y == y1) || (y == y2)) { 212 for (x = x1; x <= x2; x++) 213 vt_fb_setpixel(vd, x, y, color); 214 } else { 215 vt_fb_setpixel(vd, x1, y, color); 216 vt_fb_setpixel(vd, x2, y, color); 217 } 218 } 219 } 220 221 void 222 vt_fb_blank(struct vt_device *vd, term_color_t color) 223 { 224 struct fb_info *info; 225 uint32_t c; 226 u_int o, h; 227 228 info = vd->vd_softc; 229 c = info->fb_cmap[color]; 230 231 if (info->fb_flags & FB_FLAG_NOWRITE) 232 return; 233 234 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 235 236 switch (FBTYPE_GET_BYTESPP(info)) { 237 case 1: 238 for (h = 0; h < info->fb_height; h++) 239 for (o = 0; o < info->fb_stride; o++) 240 vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 241 break; 242 case 2: 243 for (h = 0; h < info->fb_height; h++) 244 for (o = 0; o < info->fb_stride - 1; o += 2) 245 vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 246 break; 247 case 3: 248 for (h = 0; h < info->fb_height; h++) 249 for (o = 0; o < info->fb_stride - 2; o += 3) { 250 vt_fb_mem_wr1(info, h*info->fb_stride + o, 251 (c >> 16) & 0xff); 252 vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 253 (c >> 8) & 0xff); 254 vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 255 c & 0xff); 256 } 257 break; 258 case 4: 259 for (h = 0; h < info->fb_height; h++) 260 for (o = 0; o < info->fb_stride - 3; o += 4) 261 vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 262 break; 263 default: 264 /* panic? */ 265 return; 266 } 267 } 268 269 void 270 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 271 const uint8_t *pattern, const uint8_t *mask, 272 unsigned int width, unsigned int height, 273 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 274 { 275 struct fb_info *info; 276 uint32_t fgc, bgc, cc, o; 277 int bpp, bpl, xi, yi; 278 int bit, byte; 279 280 info = vd->vd_softc; 281 bpp = FBTYPE_GET_BYTESPP(info); 282 fgc = info->fb_cmap[fg]; 283 bgc = info->fb_cmap[bg]; 284 bpl = (width + 7) / 8; /* Bytes per source line. */ 285 286 if (info->fb_flags & FB_FLAG_NOWRITE) 287 return; 288 289 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 290 291 /* Bound by right and bottom edges. */ 292 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 293 if (y >= vw->vw_draw_area.tr_end.tp_row) 294 return; 295 height = vw->vw_draw_area.tr_end.tp_row - y; 296 } 297 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 298 if (x >= vw->vw_draw_area.tr_end.tp_col) 299 return; 300 width = vw->vw_draw_area.tr_end.tp_col - x; 301 } 302 for (yi = 0; yi < height; yi++) { 303 for (xi = 0; xi < width; xi++) { 304 byte = yi * bpl + xi / 8; 305 bit = 0x80 >> (xi % 8); 306 /* Skip pixel write, if mask bit not set. */ 307 if (mask != NULL && (mask[byte] & bit) == 0) 308 continue; 309 o = (y + yi) * info->fb_stride + (x + xi) * bpp; 310 o += vd->vd_transpose; 311 cc = pattern[byte] & bit ? fgc : bgc; 312 313 switch(bpp) { 314 case 1: 315 vt_fb_mem_wr1(info, o, cc); 316 break; 317 case 2: 318 vt_fb_mem_wr2(info, o, cc); 319 break; 320 case 3: 321 /* Packed mode, so unaligned. Byte access. */ 322 vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 323 vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 324 vt_fb_mem_wr1(info, o + 2, cc & 0xff); 325 break; 326 case 4: 327 vt_fb_mem_wr4(info, o, cc); 328 break; 329 default: 330 /* panic? */ 331 break; 332 } 333 } 334 } 335 } 336 337 int 338 vt_fb_bitblt_argb(struct vt_device *vd, const struct vt_window *vw, 339 const uint8_t *argb, 340 unsigned int width, unsigned int height, 341 unsigned int x, unsigned int y) 342 { 343 struct fb_info *info; 344 uint32_t o, cc; 345 int bpp, xi, yi; 346 347 info = vd->vd_softc; 348 bpp = FBTYPE_GET_BYTESPP(info); 349 if (bpp != 4) 350 return (EOPNOTSUPP); 351 352 if (info->fb_flags & FB_FLAG_NOWRITE) 353 return (0); 354 355 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 356 357 /* Bound by right and bottom edges. */ 358 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 359 if (y >= vw->vw_draw_area.tr_end.tp_row) 360 return (EINVAL); 361 height = vw->vw_draw_area.tr_end.tp_row - y; 362 } 363 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 364 if (x >= vw->vw_draw_area.tr_end.tp_col) 365 return (EINVAL); 366 width = vw->vw_draw_area.tr_end.tp_col - x; 367 } 368 for (yi = 0; yi < height; yi++) { 369 for (xi = 0; xi < (width * 4); xi += 4) { 370 o = (y + yi) * info->fb_stride + (x + (xi / 4)) * bpp; 371 o += vd->vd_transpose; 372 cc = (argb[yi * width * 4 + xi] << 16) | 373 (argb[yi * width * 4 + xi + 1] << 8) | 374 (argb[yi * width * 4 + xi + 2]) | 375 (argb[yi * width * 4 + xi + 3] << 24); 376 vt_fb_mem_wr4(info, o, cc); 377 } 378 } 379 380 return (0); 381 } 382 383 void 384 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 385 const term_rect_t *area) 386 { 387 unsigned int col, row, x, y; 388 struct vt_font *vf; 389 term_char_t c; 390 term_color_t fg, bg; 391 const uint8_t *pattern; 392 size_t z; 393 394 vf = vw->vw_font; 395 396 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 397 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 398 ++col) { 399 x = col * vf->vf_width + 400 vw->vw_draw_area.tr_begin.tp_col; 401 y = row * vf->vf_height + 402 vw->vw_draw_area.tr_begin.tp_row; 403 404 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 405 pattern = vtfont_lookup(vf, c); 406 vt_determine_colors(c, 407 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 408 409 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 410 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 411 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 412 continue; 413 if (vd->vd_drawn && (vd->vd_drawn[z] == c) && 414 vd->vd_drawnfg && (vd->vd_drawnfg[z] == fg) && 415 vd->vd_drawnbg && (vd->vd_drawnbg[z] == bg)) 416 continue; 417 418 vt_fb_bitblt_bitmap(vd, vw, 419 pattern, NULL, vf->vf_width, vf->vf_height, 420 x, y, fg, bg); 421 422 if (vd->vd_drawn) 423 vd->vd_drawn[z] = c; 424 if (vd->vd_drawnfg) 425 vd->vd_drawnfg[z] = fg; 426 if (vd->vd_drawnbg) 427 vd->vd_drawnbg[z] = bg; 428 } 429 } 430 431 #ifndef SC_NO_CUTPASTE 432 if (!vd->vd_mshown) 433 return; 434 435 term_rect_t drawn_area; 436 437 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 438 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 439 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 440 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 441 442 if (vt_is_cursor_in_area(vd, &drawn_area)) { 443 vt_fb_bitblt_bitmap(vd, vw, 444 vd->vd_mcursor->map, vd->vd_mcursor->mask, 445 vd->vd_mcursor->width, vd->vd_mcursor->height, 446 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 447 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 448 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 449 } 450 #endif 451 } 452 453 void 454 vt_fb_invalidate_text(struct vt_device *vd, const term_rect_t *area) 455 { 456 unsigned int col, row; 457 size_t z; 458 459 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 460 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 461 ++col) { 462 z = row * PIXEL_WIDTH(VT_FB_MAX_WIDTH) + col; 463 if (z >= PIXEL_HEIGHT(VT_FB_MAX_HEIGHT) * 464 PIXEL_WIDTH(VT_FB_MAX_WIDTH)) 465 continue; 466 if (vd->vd_drawn) 467 vd->vd_drawn[z] = 0; 468 if (vd->vd_drawnfg) 469 vd->vd_drawnfg[z] = 0; 470 if (vd->vd_drawnbg) 471 vd->vd_drawnbg[z] = 0; 472 } 473 } 474 } 475 476 void 477 vt_fb_postswitch(struct vt_device *vd) 478 { 479 struct fb_info *info; 480 481 info = vd->vd_softc; 482 483 if (info->enter != NULL) 484 info->enter(info->fb_priv); 485 } 486 487 static int 488 vt_fb_init_colors(struct fb_info *info) 489 { 490 491 switch (FBTYPE_GET_BPP(info)) { 492 case 8: 493 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 494 0x7, 5, 0x7, 2, 0x3, 0)); 495 case 15: 496 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 497 0x1f, 10, 0x1f, 5, 0x1f, 0)); 498 case 16: 499 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 500 0x1f, 11, 0x3f, 5, 0x1f, 0)); 501 case 24: 502 case 32: /* Ignore alpha. */ 503 return (vt_config_cons_colors(info, COLOR_FORMAT_RGB, 504 0xff, 16, 0xff, 8, 0xff, 0)); 505 default: 506 return (1); 507 } 508 } 509 510 int 511 vt_fb_init(struct vt_device *vd) 512 { 513 struct fb_info *info; 514 u_int margin; 515 int bg, err; 516 term_color_t c; 517 518 info = vd->vd_softc; 519 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 520 margin = (info->fb_height - vd->vd_height) >> 1; 521 vd->vd_transpose = margin * info->fb_stride; 522 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 523 margin = (info->fb_width - vd->vd_width) >> 1; 524 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 525 vd->vd_video_dev = info->fb_video_dev; 526 527 if (info->fb_size == 0) 528 return (CN_DEAD); 529 530 if (info->fb_pbase == 0 && info->fb_vbase == 0) 531 info->fb_flags |= FB_FLAG_NOMMAP; 532 533 if (info->fb_cmsize <= 0) { 534 err = vt_fb_init_colors(info); 535 if (err) 536 return (CN_DEAD); 537 info->fb_cmsize = 16; 538 } 539 540 c = TC_BLACK; 541 if (TUNABLE_INT_FETCH("teken.bg_color", &bg) != 0) { 542 if (bg == TC_WHITE) 543 bg |= TC_LIGHT; 544 c = bg; 545 } 546 /* Clear the screen. */ 547 vd->vd_driver->vd_blank(vd, c); 548 549 /* Wakeup screen. KMS need this. */ 550 vt_fb_postswitch(vd); 551 552 return (CN_INTERNAL); 553 } 554 555 void 556 vt_fb_fini(struct vt_device *vd, void *softc) 557 { 558 559 vd->vd_video_dev = NULL; 560 } 561 562 int 563 vt_fb_attach(struct fb_info *info) 564 { 565 int ret; 566 567 ret = vt_allocate(&vt_fb_driver, info); 568 569 return (ret); 570 } 571 572 int 573 vt_fb_detach(struct fb_info *info) 574 { 575 int ret; 576 577 ret = vt_deallocate(&vt_fb_driver, info); 578 579 return (ret); 580 } 581 582 void 583 vt_fb_suspend(struct vt_device *vd) 584 { 585 586 vt_suspend(vd); 587 } 588 589 void 590 vt_fb_resume(struct vt_device *vd) 591 { 592 593 vt_resume(vd); 594 } 595