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