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 <dev/vt/vt.h> 41 #include <dev/vt/hw/fb/vt_fb.h> 42 #include <dev/vt/colors/vt_termcolors.h> 43 44 #include <vm/vm.h> 45 #include <vm/pmap.h> 46 47 static struct vt_driver vt_fb_driver = { 48 .vd_name = "fb", 49 .vd_init = vt_fb_init, 50 .vd_fini = vt_fb_fini, 51 .vd_blank = vt_fb_blank, 52 .vd_bitblt_text = vt_fb_bitblt_text, 53 .vd_bitblt_bmp = vt_fb_bitblt_bitmap, 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 default: 123 error = ENOIOCTL; 124 break; 125 } 126 127 return (error); 128 } 129 130 int 131 vt_fb_mmap(struct vt_device *vd, vm_ooffset_t offset, vm_paddr_t *paddr, 132 int prot, vm_memattr_t *memattr) 133 { 134 struct fb_info *info; 135 136 info = vd->vd_softc; 137 138 if (info->fb_flags & FB_FLAG_NOMMAP) 139 return (ENODEV); 140 141 if (offset >= 0 && offset < info->fb_size) { 142 if (info->fb_pbase == 0) { 143 *paddr = vtophys((uint8_t *)info->fb_vbase + offset); 144 } else { 145 *paddr = info->fb_pbase + offset; 146 if (info->fb_flags & FB_FLAG_MEMATTR) 147 *memattr = info->fb_memattr; 148 #ifdef VM_MEMATTR_WRITE_COMBINING 149 else 150 *memattr = VM_MEMATTR_WRITE_COMBINING; 151 #endif 152 } 153 return (0); 154 } 155 156 return (EINVAL); 157 } 158 159 void 160 vt_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 161 { 162 struct fb_info *info; 163 uint32_t c; 164 u_int o; 165 166 info = vd->vd_softc; 167 c = info->fb_cmap[color]; 168 o = info->fb_stride * y + x * FBTYPE_GET_BYTESPP(info); 169 170 if (info->fb_flags & FB_FLAG_NOWRITE) 171 return; 172 173 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 174 175 switch (FBTYPE_GET_BYTESPP(info)) { 176 case 1: 177 vt_fb_mem_wr1(info, o, c); 178 break; 179 case 2: 180 vt_fb_mem_wr2(info, o, c); 181 break; 182 case 3: 183 vt_fb_mem_wr1(info, o, (c >> 16) & 0xff); 184 vt_fb_mem_wr1(info, o + 1, (c >> 8) & 0xff); 185 vt_fb_mem_wr1(info, o + 2, c & 0xff); 186 break; 187 case 4: 188 vt_fb_mem_wr4(info, o, c); 189 break; 190 default: 191 /* panic? */ 192 return; 193 } 194 } 195 196 void 197 vt_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, int fill, 198 term_color_t color) 199 { 200 int x, y; 201 202 for (y = y1; y <= y2; y++) { 203 if (fill || (y == y1) || (y == y2)) { 204 for (x = x1; x <= x2; x++) 205 vt_fb_setpixel(vd, x, y, color); 206 } else { 207 vt_fb_setpixel(vd, x1, y, color); 208 vt_fb_setpixel(vd, x2, y, color); 209 } 210 } 211 } 212 213 void 214 vt_fb_blank(struct vt_device *vd, term_color_t color) 215 { 216 struct fb_info *info; 217 uint32_t c; 218 u_int o, h; 219 220 info = vd->vd_softc; 221 c = info->fb_cmap[color]; 222 223 if (info->fb_flags & FB_FLAG_NOWRITE) 224 return; 225 226 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 227 228 switch (FBTYPE_GET_BYTESPP(info)) { 229 case 1: 230 for (h = 0; h < info->fb_height; h++) 231 for (o = 0; o < info->fb_stride; o++) 232 vt_fb_mem_wr1(info, h*info->fb_stride + o, c); 233 break; 234 case 2: 235 for (h = 0; h < info->fb_height; h++) 236 for (o = 0; o < info->fb_stride; o += 2) 237 vt_fb_mem_wr2(info, h*info->fb_stride + o, c); 238 break; 239 case 3: 240 for (h = 0; h < info->fb_height; h++) 241 for (o = 0; o < info->fb_stride; o += 3) { 242 vt_fb_mem_wr1(info, h*info->fb_stride + o, 243 (c >> 16) & 0xff); 244 vt_fb_mem_wr1(info, h*info->fb_stride + o + 1, 245 (c >> 8) & 0xff); 246 vt_fb_mem_wr1(info, h*info->fb_stride + o + 2, 247 c & 0xff); 248 } 249 break; 250 case 4: 251 for (h = 0; h < info->fb_height; h++) 252 for (o = 0; o < info->fb_stride; o += 4) 253 vt_fb_mem_wr4(info, h*info->fb_stride + o, c); 254 break; 255 default: 256 /* panic? */ 257 return; 258 } 259 } 260 261 void 262 vt_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 263 const uint8_t *pattern, const uint8_t *mask, 264 unsigned int width, unsigned int height, 265 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 266 { 267 struct fb_info *info; 268 uint32_t fgc, bgc, cc, o; 269 int bpp, bpl, xi, yi; 270 int bit, byte; 271 272 info = vd->vd_softc; 273 bpp = FBTYPE_GET_BYTESPP(info); 274 fgc = info->fb_cmap[fg]; 275 bgc = info->fb_cmap[bg]; 276 bpl = (width + 7) / 8; /* Bytes per source line. */ 277 278 if (info->fb_flags & FB_FLAG_NOWRITE) 279 return; 280 281 KASSERT((info->fb_vbase != 0), ("Unmapped framebuffer")); 282 283 /* Bound by right and bottom edges. */ 284 if (y + height > vw->vw_draw_area.tr_end.tp_row) { 285 if (y >= vw->vw_draw_area.tr_end.tp_row) 286 return; 287 height = vw->vw_draw_area.tr_end.tp_row - y; 288 } 289 if (x + width > vw->vw_draw_area.tr_end.tp_col) { 290 if (x >= vw->vw_draw_area.tr_end.tp_col) 291 return; 292 width = vw->vw_draw_area.tr_end.tp_col - x; 293 } 294 for (yi = 0; yi < height; yi++) { 295 for (xi = 0; xi < width; xi++) { 296 byte = yi * bpl + xi / 8; 297 bit = 0x80 >> (xi % 8); 298 /* Skip pixel write, if mask bit not set. */ 299 if (mask != NULL && (mask[byte] & bit) == 0) 300 continue; 301 o = (y + yi) * info->fb_stride + (x + xi) * bpp; 302 o += vd->vd_transpose; 303 cc = pattern[byte] & bit ? fgc : bgc; 304 305 switch(bpp) { 306 case 1: 307 vt_fb_mem_wr1(info, o, cc); 308 break; 309 case 2: 310 vt_fb_mem_wr2(info, o, cc); 311 break; 312 case 3: 313 /* Packed mode, so unaligned. Byte access. */ 314 vt_fb_mem_wr1(info, o, (cc >> 16) & 0xff); 315 vt_fb_mem_wr1(info, o + 1, (cc >> 8) & 0xff); 316 vt_fb_mem_wr1(info, o + 2, cc & 0xff); 317 break; 318 case 4: 319 vt_fb_mem_wr4(info, o, cc); 320 break; 321 default: 322 /* panic? */ 323 break; 324 } 325 } 326 } 327 } 328 329 void 330 vt_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 331 const term_rect_t *area) 332 { 333 unsigned int col, row, x, y; 334 struct vt_font *vf; 335 term_char_t c; 336 term_color_t fg, bg; 337 const uint8_t *pattern; 338 339 vf = vw->vw_font; 340 341 for (row = area->tr_begin.tp_row; row < area->tr_end.tp_row; ++row) { 342 for (col = area->tr_begin.tp_col; col < area->tr_end.tp_col; 343 ++col) { 344 x = col * vf->vf_width + 345 vw->vw_draw_area.tr_begin.tp_col; 346 y = row * vf->vf_height + 347 vw->vw_draw_area.tr_begin.tp_row; 348 349 c = VTBUF_GET_FIELD(&vw->vw_buf, row, col); 350 pattern = vtfont_lookup(vf, c); 351 vt_determine_colors(c, 352 VTBUF_ISCURSOR(&vw->vw_buf, row, col), &fg, &bg); 353 354 vt_fb_bitblt_bitmap(vd, vw, 355 pattern, NULL, vf->vf_width, vf->vf_height, 356 x, y, fg, bg); 357 } 358 } 359 360 #ifndef SC_NO_CUTPASTE 361 if (!vd->vd_mshown) 362 return; 363 364 term_rect_t drawn_area; 365 366 drawn_area.tr_begin.tp_col = area->tr_begin.tp_col * vf->vf_width; 367 drawn_area.tr_begin.tp_row = area->tr_begin.tp_row * vf->vf_height; 368 drawn_area.tr_end.tp_col = area->tr_end.tp_col * vf->vf_width; 369 drawn_area.tr_end.tp_row = area->tr_end.tp_row * vf->vf_height; 370 371 if (vt_is_cursor_in_area(vd, &drawn_area)) { 372 vt_fb_bitblt_bitmap(vd, vw, 373 vd->vd_mcursor->map, vd->vd_mcursor->mask, 374 vd->vd_mcursor->width, vd->vd_mcursor->height, 375 vd->vd_mx_drawn + vw->vw_draw_area.tr_begin.tp_col, 376 vd->vd_my_drawn + vw->vw_draw_area.tr_begin.tp_row, 377 vd->vd_mcursor_fg, vd->vd_mcursor_bg); 378 } 379 #endif 380 } 381 382 void 383 vt_fb_postswitch(struct vt_device *vd) 384 { 385 struct fb_info *info; 386 387 info = vd->vd_softc; 388 389 if (info->enter != NULL) 390 info->enter(info->fb_priv); 391 } 392 393 static int 394 vt_fb_init_cmap(uint32_t *cmap, int depth) 395 { 396 397 switch (depth) { 398 case 8: 399 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 400 0x7, 5, 0x7, 2, 0x3, 0)); 401 case 15: 402 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 403 0x1f, 10, 0x1f, 5, 0x1f, 0)); 404 case 16: 405 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 406 0x1f, 11, 0x3f, 5, 0x1f, 0)); 407 case 24: 408 case 32: /* Ignore alpha. */ 409 return (vt_generate_cons_palette(cmap, COLOR_FORMAT_RGB, 410 0xff, 16, 0xff, 8, 0xff, 0)); 411 default: 412 return (1); 413 } 414 } 415 416 int 417 vt_fb_init(struct vt_device *vd) 418 { 419 struct fb_info *info; 420 u_int margin; 421 int err; 422 423 info = vd->vd_softc; 424 vd->vd_height = MIN(VT_FB_MAX_HEIGHT, info->fb_height); 425 margin = (info->fb_height - vd->vd_height) >> 1; 426 vd->vd_transpose = margin * info->fb_stride; 427 vd->vd_width = MIN(VT_FB_MAX_WIDTH, info->fb_width); 428 margin = (info->fb_width - vd->vd_width) >> 1; 429 vd->vd_transpose += margin * (info->fb_bpp / NBBY); 430 vd->vd_video_dev = info->fb_video_dev; 431 432 if (info->fb_size == 0) 433 return (CN_DEAD); 434 435 if (info->fb_pbase == 0 && info->fb_vbase == 0) 436 info->fb_flags |= FB_FLAG_NOMMAP; 437 438 if (info->fb_cmsize <= 0) { 439 err = vt_fb_init_cmap(info->fb_cmap, FBTYPE_GET_BPP(info)); 440 if (err) 441 return (CN_DEAD); 442 info->fb_cmsize = 16; 443 } 444 445 /* Clear the screen. */ 446 vd->vd_driver->vd_blank(vd, TC_BLACK); 447 448 /* Wakeup screen. KMS need this. */ 449 vt_fb_postswitch(vd); 450 451 return (CN_INTERNAL); 452 } 453 454 void 455 vt_fb_fini(struct vt_device *vd, void *softc) 456 { 457 458 vd->vd_video_dev = NULL; 459 } 460 461 int 462 vt_fb_attach(struct fb_info *info) 463 { 464 465 vt_allocate(&vt_fb_driver, info); 466 467 return (0); 468 } 469 470 int 471 vt_fb_detach(struct fb_info *info) 472 { 473 474 vt_deallocate(&vt_fb_driver, info); 475 476 return (0); 477 } 478 479 void 480 vt_fb_suspend(struct vt_device *vd) 481 { 482 483 vt_suspend(vd); 484 } 485 486 void 487 vt_fb_resume(struct vt_device *vd) 488 { 489 490 vt_resume(vd); 491 } 492