1 /* 2 * Mesa 3-D graphics library 3 * Version: 7.9 4 * 5 * Copyright (C) 2010 LunarG Inc. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included 15 * in all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 * DEALINGS IN THE SOFTWARE. 24 * 25 * Authors: 26 * Chia-I Wu <olv (at) lunarg.com> 27 */ 28 29 /** 30 * Considering fbdev as an in-kernel window system, 31 * 32 * - opening a device opens a connection 33 * - there is only one window: the framebuffer 34 * - fb_var_screeninfo decides window position, size, and even color format 35 * - there is no pixmap 36 * 37 * Now EGL is built on top of this window system. So we should have 38 * 39 * - the fd as the handle of the native display 40 * - reject all but one native window: NULL 41 * - no pixmap support 42 */ 43 44 #include <errno.h> 45 #include <sys/ioctl.h> 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 #include <fcntl.h> 49 #include <linux/fb.h> 50 51 #include "pipe/p_screen.h" 52 #include "util/u_memory.h" 53 #include "util/u_inlines.h" 54 #include "util/u_pointer.h" 55 56 #include "common/native.h" 57 #include "common/native_helper.h" 58 #include "fbdev/fbdev_sw_winsys.h" 59 60 struct fbdev_display { 61 struct native_display base; 62 63 int fd; 64 const struct native_event_handler *event_handler; 65 66 struct fb_fix_screeninfo finfo; 67 struct fb_var_screeninfo config_vinfo; 68 struct native_config config; 69 70 boolean assume_fixed_vinfo; 71 }; 72 73 struct fbdev_surface { 74 struct native_surface base; 75 76 struct fbdev_display *fbdpy; 77 struct resource_surface *rsurf; 78 int width, height; 79 80 unsigned int sequence_number; 81 82 struct fbdev_sw_drawable drawable; 83 }; 84 85 static INLINE struct fbdev_display * 86 fbdev_display(const struct native_display *ndpy) 87 { 88 return (struct fbdev_display *) ndpy; 89 } 90 91 static INLINE struct fbdev_surface * 92 fbdev_surface(const struct native_surface *nsurf) 93 { 94 return (struct fbdev_surface *) nsurf; 95 } 96 97 static boolean 98 fbdev_surface_validate(struct native_surface *nsurf, uint attachment_mask, 99 unsigned int *seq_num, struct pipe_resource **textures, 100 int *width, int *height) 101 { 102 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 103 104 if (!resource_surface_add_resources(fbsurf->rsurf, attachment_mask)) 105 return FALSE; 106 if (textures) 107 resource_surface_get_resources(fbsurf->rsurf, textures, attachment_mask); 108 109 if (seq_num) 110 *seq_num = fbsurf->sequence_number; 111 if (width) 112 *width = fbsurf->width; 113 if (height) 114 *height = fbsurf->height; 115 116 return TRUE; 117 } 118 119 static enum pipe_format 120 vinfo_to_format(const struct fb_var_screeninfo *vinfo) 121 { 122 enum pipe_format format = PIPE_FORMAT_NONE; 123 124 /* should also check channel offsets... */ 125 switch (vinfo->bits_per_pixel) { 126 case 32: 127 if (vinfo->red.length == 8 && 128 vinfo->green.length == 8 && 129 vinfo->blue.length == 8) { 130 format = (vinfo->transp.length == 8) ? 131 PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM; 132 } 133 break; 134 case 16: 135 if (vinfo->red.length == 5 && 136 vinfo->green.length == 6 && 137 vinfo->blue.length == 5 && 138 vinfo->transp.length == 0) 139 format = PIPE_FORMAT_B5G6R5_UNORM; 140 break; 141 default: 142 break; 143 } 144 145 return format; 146 } 147 148 static boolean 149 fbdev_surface_update_drawable(struct native_surface *nsurf, 150 const struct fb_var_screeninfo *vinfo) 151 { 152 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 153 unsigned x, y, width, height; 154 155 x = vinfo->xoffset; 156 y = vinfo->yoffset; 157 width = MIN2(vinfo->xres, fbsurf->width); 158 height = MIN2(vinfo->yres, fbsurf->height); 159 160 /* sanitize the values */ 161 if (x + width > vinfo->xres_virtual) { 162 if (x > vinfo->xres_virtual) 163 width = 0; 164 else 165 width = vinfo->xres_virtual - x; 166 } 167 if (y + height > vinfo->yres_virtual) { 168 if (y > vinfo->yres_virtual) 169 height = 0; 170 else 171 height = vinfo->yres_virtual - y; 172 } 173 174 fbsurf->drawable.format = vinfo_to_format(vinfo); 175 fbsurf->drawable.x = vinfo->xoffset; 176 fbsurf->drawable.y = vinfo->yoffset; 177 fbsurf->drawable.width = vinfo->xres; 178 fbsurf->drawable.height = vinfo->yres; 179 180 return (fbsurf->drawable.format != PIPE_FORMAT_NONE && 181 fbsurf->drawable.width && 182 fbsurf->drawable.height); 183 } 184 185 static boolean 186 fbdev_surface_present(struct native_surface *nsurf, 187 const struct native_present_control *ctrl) 188 { 189 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 190 struct fbdev_display *fbdpy = fbsurf->fbdpy; 191 boolean ret = FALSE; 192 193 if (ctrl->swap_interval) 194 return FALSE; 195 if (ctrl->natt != NATIVE_ATTACHMENT_BACK_LEFT) 196 return FALSE; 197 198 if (!fbdpy->assume_fixed_vinfo) { 199 struct fb_var_screeninfo vinfo; 200 201 memset(&vinfo, 0, sizeof(vinfo)); 202 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) 203 return FALSE; 204 205 /* present the surface */ 206 if (fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) { 207 ret = resource_surface_present(fbsurf->rsurf, 208 ctrl->natt, (void *) &fbsurf->drawable); 209 } 210 211 fbsurf->width = vinfo.xres; 212 fbsurf->height = vinfo.yres; 213 214 if (resource_surface_set_size(fbsurf->rsurf, 215 fbsurf->width, fbsurf->height)) { 216 /* surface resized */ 217 fbsurf->sequence_number++; 218 fbdpy->event_handler->invalid_surface(&fbdpy->base, 219 &fbsurf->base, fbsurf->sequence_number); 220 } 221 } 222 else { 223 /* the drawable never changes */ 224 ret = resource_surface_present(fbsurf->rsurf, 225 ctrl->natt, (void *) &fbsurf->drawable); 226 } 227 228 return ret; 229 } 230 231 static void 232 fbdev_surface_wait(struct native_surface *nsurf) 233 { 234 /* no-op */ 235 } 236 237 static void 238 fbdev_surface_destroy(struct native_surface *nsurf) 239 { 240 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 241 242 resource_surface_destroy(fbsurf->rsurf); 243 FREE(fbsurf); 244 } 245 246 static struct native_surface * 247 fbdev_display_create_window_surface(struct native_display *ndpy, 248 EGLNativeWindowType win, 249 const struct native_config *nconf) 250 { 251 struct fbdev_display *fbdpy = fbdev_display(ndpy); 252 struct fbdev_surface *fbsurf; 253 struct fb_var_screeninfo vinfo; 254 255 /* there is only one native window: NULL */ 256 if (win) 257 return NULL; 258 259 fbsurf = CALLOC_STRUCT(fbdev_surface); 260 if (!fbsurf) 261 return NULL; 262 263 fbsurf->fbdpy = fbdpy; 264 265 /* get current vinfo */ 266 if (fbdpy->assume_fixed_vinfo) { 267 vinfo = fbdpy->config_vinfo; 268 } 269 else { 270 memset(&vinfo, 0, sizeof(vinfo)); 271 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) { 272 FREE(fbsurf); 273 return NULL; 274 } 275 } 276 277 fbsurf->width = vinfo.xres; 278 fbsurf->height = vinfo.yres; 279 280 if (!fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) { 281 FREE(fbsurf); 282 return NULL; 283 } 284 285 fbsurf->rsurf = resource_surface_create(fbdpy->base.screen, 286 nconf->color_format, 287 PIPE_BIND_RENDER_TARGET | 288 PIPE_BIND_DISPLAY_TARGET); 289 if (!fbsurf->rsurf) { 290 FREE(fbsurf); 291 return NULL; 292 } 293 294 resource_surface_set_size(fbsurf->rsurf, fbsurf->width, fbsurf->height); 295 296 fbsurf->base.destroy = fbdev_surface_destroy; 297 fbsurf->base.present = fbdev_surface_present; 298 fbsurf->base.validate = fbdev_surface_validate; 299 fbsurf->base.wait = fbdev_surface_wait; 300 301 return &fbsurf->base; 302 } 303 304 static struct native_surface * 305 fbdev_display_create_scanout_surface(struct native_display *ndpy, 306 const struct native_config *nconf, 307 uint width, uint height) 308 { 309 return fbdev_display_create_window_surface(ndpy, 310 (EGLNativeWindowType) NULL, nconf); 311 } 312 313 static boolean 314 fbdev_display_program(struct native_display *ndpy, int crtc_idx, 315 struct native_surface *nsurf, uint x, uint y, 316 const struct native_connector **nconns, int num_nconns, 317 const struct native_mode *nmode) 318 { 319 return TRUE; 320 } 321 322 static const struct native_mode ** 323 fbdev_display_get_modes(struct native_display *ndpy, 324 const struct native_connector *nconn, 325 int *num_modes) 326 { 327 static struct native_mode mode; 328 const struct native_mode **modes; 329 330 if (!mode.desc) { 331 struct fbdev_display *fbdpy = fbdev_display(ndpy); 332 mode.desc = "Current Mode"; 333 mode.width = fbdpy->config_vinfo.xres; 334 mode.height = fbdpy->config_vinfo.yres; 335 mode.refresh_rate = 60 * 1000; /* dummy */ 336 } 337 338 modes = MALLOC(sizeof(*modes)); 339 if (modes) { 340 modes[0] = &mode; 341 if (num_modes) 342 *num_modes = 1; 343 } 344 345 return modes; 346 } 347 348 static const struct native_connector ** 349 fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors, 350 int *num_crtc) 351 { 352 static struct native_connector connector; 353 const struct native_connector **connectors; 354 355 connectors = MALLOC(sizeof(*connectors)); 356 if (connectors) { 357 connectors[0] = &connector; 358 if (num_connectors) 359 *num_connectors = 1; 360 } 361 362 return connectors; 363 } 364 365 /* remove modeset support one day! */ 366 static const struct native_display_modeset fbdev_display_modeset = { 367 .get_connectors = fbdev_display_get_connectors, 368 .get_modes = fbdev_display_get_modes, 369 .create_scanout_surface = fbdev_display_create_scanout_surface, 370 .program = fbdev_display_program 371 }; 372 373 static const struct native_config ** 374 fbdev_display_get_configs(struct native_display *ndpy, int *num_configs) 375 { 376 struct fbdev_display *fbdpy = fbdev_display(ndpy); 377 const struct native_config **configs; 378 379 configs = MALLOC(sizeof(*configs)); 380 if (configs) { 381 configs[0] = &fbdpy->config; 382 if (num_configs) 383 *num_configs = 1; 384 } 385 386 return configs; 387 } 388 389 static int 390 fbdev_display_get_param(struct native_display *ndpy, 391 enum native_param_type param) 392 { 393 int val; 394 395 switch (param) { 396 case NATIVE_PARAM_PRESERVE_BUFFER: 397 val = 1; 398 break; 399 case NATIVE_PARAM_USE_NATIVE_BUFFER: 400 case NATIVE_PARAM_MAX_SWAP_INTERVAL: 401 default: 402 val = 0; 403 break; 404 } 405 406 return val; 407 } 408 409 static void 410 fbdev_display_destroy(struct native_display *ndpy) 411 { 412 struct fbdev_display *fbdpy = fbdev_display(ndpy); 413 414 ndpy_uninit(&fbdpy->base); 415 close(fbdpy->fd); 416 FREE(fbdpy); 417 } 418 419 static boolean 420 fbdev_display_init_screen(struct native_display *ndpy) 421 { 422 struct fbdev_display *fbdpy = fbdev_display(ndpy); 423 struct sw_winsys *ws; 424 425 ws = fbdev_create_sw_winsys(fbdpy->fd); 426 if (!ws) 427 return FALSE; 428 429 fbdpy->base.screen = fbdpy->event_handler->new_sw_screen(&fbdpy->base, ws); 430 if (!fbdpy->base.screen) { 431 if (ws->destroy) 432 ws->destroy(ws); 433 return FALSE; 434 } 435 436 if (!fbdpy->base.screen->is_format_supported(fbdpy->base.screen, 437 fbdpy->config.color_format, PIPE_TEXTURE_2D, 0, 438 PIPE_BIND_RENDER_TARGET)) { 439 fbdpy->base.screen->destroy(fbdpy->base.screen); 440 fbdpy->base.screen = NULL; 441 return FALSE; 442 } 443 444 return TRUE; 445 } 446 447 static boolean 448 fbdev_display_init_config(struct native_display *ndpy) 449 { 450 struct fbdev_display *fbdpy = fbdev_display(ndpy); 451 struct native_config *nconf = &fbdpy->config; 452 453 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->config_vinfo)) 454 return FALSE; 455 456 nconf->color_format = vinfo_to_format(&fbdpy->config_vinfo); 457 if (nconf->color_format == PIPE_FORMAT_NONE) 458 return FALSE; 459 460 nconf->buffer_mask = (1 << NATIVE_ATTACHMENT_BACK_LEFT); 461 462 nconf->window_bit = TRUE; 463 464 return TRUE; 465 } 466 467 static struct native_display * 468 fbdev_display_create(int fd, const struct native_event_handler *event_handler) 469 { 470 struct fbdev_display *fbdpy; 471 472 fbdpy = CALLOC_STRUCT(fbdev_display); 473 if (!fbdpy) 474 return NULL; 475 476 fbdpy->fd = fd; 477 fbdpy->event_handler = event_handler; 478 479 if (ioctl(fbdpy->fd, FBIOGET_FSCREENINFO, &fbdpy->finfo)) 480 goto fail; 481 482 if (fbdpy->finfo.visual != FB_VISUAL_TRUECOLOR || 483 fbdpy->finfo.type != FB_TYPE_PACKED_PIXELS) 484 goto fail; 485 486 if (!fbdev_display_init_config(&fbdpy->base)) 487 goto fail; 488 489 fbdpy->assume_fixed_vinfo = TRUE; 490 491 fbdpy->base.init_screen = fbdev_display_init_screen; 492 fbdpy->base.destroy = fbdev_display_destroy; 493 fbdpy->base.get_param = fbdev_display_get_param; 494 fbdpy->base.get_configs = fbdev_display_get_configs; 495 496 fbdpy->base.create_window_surface = fbdev_display_create_window_surface; 497 498 /* we'd like to remove modeset support one day */ 499 fbdpy->config.scanout_bit = TRUE; 500 fbdpy->base.modeset = &fbdev_display_modeset; 501 502 return &fbdpy->base; 503 504 fail: 505 FREE(fbdpy); 506 return NULL; 507 } 508 509 static const struct native_event_handler *fbdev_event_handler; 510 511 static struct native_display * 512 native_create_display(void *dpy, boolean use_sw) 513 { 514 struct native_display *ndpy; 515 int fd; 516 517 /* well, this makes fd 0 being ignored */ 518 if (!dpy) { 519 const char *device_name="/dev/fb0"; 520 #ifdef O_CLOEXEC 521 fd = open(device_name, O_RDWR | O_CLOEXEC); 522 if (fd == -1 && errno == EINVAL) 523 #endif 524 { 525 fd = open(device_name, O_RDWR); 526 if (fd != -1) 527 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 528 } 529 } 530 else { 531 fd = dup((int) pointer_to_intptr(dpy)); 532 } 533 if (fd < 0) 534 return NULL; 535 536 ndpy = fbdev_display_create(fd, fbdev_event_handler); 537 if (!ndpy) 538 close(fd); 539 540 return ndpy; 541 } 542 543 static const struct native_platform fbdev_platform = { 544 "FBDEV", /* name */ 545 native_create_display 546 }; 547 548 const struct native_platform * 549 native_get_fbdev_platform(const struct native_event_handler *event_handler) 550 { 551 fbdev_event_handler = event_handler; 552 return &fbdev_platform; 553 } 554