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