native_fbdev.c revision 08e1076fd2d3f6fb879dd2529e7d035d6a399da2
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@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 <sys/ioctl.h> 45#include <sys/types.h> 46#include <sys/stat.h> 47#include <fcntl.h> 48#include <linux/fb.h> 49 50#include "pipe/p_screen.h" 51#include "util/u_memory.h" 52#include "util/u_inlines.h" 53#include "util/u_pointer.h" 54 55#include "common/native.h" 56#include "common/native_helper.h" 57#include "fbdev/fbdev_sw_winsys.h" 58 59struct fbdev_display { 60 struct native_display base; 61 62 int fd; 63 const struct native_event_handler *event_handler; 64 65 struct fb_fix_screeninfo finfo; 66 struct fb_var_screeninfo config_vinfo; 67 struct native_config config; 68 69 boolean assume_fixed_vinfo; 70}; 71 72struct fbdev_surface { 73 struct native_surface base; 74 75 struct fbdev_display *fbdpy; 76 struct resource_surface *rsurf; 77 int width, height; 78 79 unsigned int sequence_number; 80 81 struct fbdev_sw_drawable drawable; 82}; 83 84static INLINE struct fbdev_display * 85fbdev_display(const struct native_display *ndpy) 86{ 87 return (struct fbdev_display *) ndpy; 88} 89 90static INLINE struct fbdev_surface * 91fbdev_surface(const struct native_surface *nsurf) 92{ 93 return (struct fbdev_surface *) nsurf; 94} 95 96static boolean 97fbdev_surface_validate(struct native_surface *nsurf, uint attachment_mask, 98 unsigned int *seq_num, struct pipe_resource **textures, 99 int *width, int *height) 100{ 101 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 102 103 if (!resource_surface_add_resources(fbsurf->rsurf, attachment_mask)) 104 return FALSE; 105 if (textures) 106 resource_surface_get_resources(fbsurf->rsurf, textures, attachment_mask); 107 108 if (seq_num) 109 *seq_num = fbsurf->sequence_number; 110 if (width) 111 *width = fbsurf->width; 112 if (height) 113 *height = fbsurf->height; 114 115 return TRUE; 116} 117 118static enum pipe_format 119vinfo_to_format(const struct fb_var_screeninfo *vinfo) 120{ 121 enum pipe_format format = PIPE_FORMAT_NONE; 122 123 /* should also check channel offsets... */ 124 switch (vinfo->bits_per_pixel) { 125 case 32: 126 if (vinfo->red.length == 8 && 127 vinfo->green.length == 8 && 128 vinfo->blue.length == 8) { 129 format = (vinfo->transp.length == 8) ? 130 PIPE_FORMAT_B8G8R8A8_UNORM : PIPE_FORMAT_B8G8R8X8_UNORM; 131 } 132 break; 133 case 16: 134 if (vinfo->red.length == 5 && 135 vinfo->green.length == 6 && 136 vinfo->blue.length == 5 && 137 vinfo->transp.length == 0) 138 format = PIPE_FORMAT_B5G6R5_UNORM; 139 break; 140 default: 141 break; 142 } 143 144 return format; 145} 146 147static boolean 148fbdev_surface_update_drawable(struct native_surface *nsurf, 149 const struct fb_var_screeninfo *vinfo) 150{ 151 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 152 unsigned x, y, width, height; 153 154 x = vinfo->xoffset; 155 y = vinfo->yoffset; 156 width = MIN2(vinfo->xres, fbsurf->width); 157 height = MIN2(vinfo->yres, fbsurf->height); 158 159 /* sanitize the values */ 160 if (x + width > vinfo->xres_virtual) { 161 if (x > vinfo->xres_virtual) 162 width = 0; 163 else 164 width = vinfo->xres_virtual - x; 165 } 166 if (y + height > vinfo->yres_virtual) { 167 if (y > vinfo->yres_virtual) 168 height = 0; 169 else 170 height = vinfo->yres_virtual - y; 171 } 172 173 fbsurf->drawable.format = vinfo_to_format(vinfo); 174 fbsurf->drawable.x = vinfo->xoffset; 175 fbsurf->drawable.y = vinfo->yoffset; 176 fbsurf->drawable.width = vinfo->xres; 177 fbsurf->drawable.height = vinfo->yres; 178 179 return (fbsurf->drawable.format != PIPE_FORMAT_NONE && 180 fbsurf->drawable.width && 181 fbsurf->drawable.height); 182} 183 184static boolean 185fbdev_surface_present(struct native_surface *nsurf, 186 const struct native_present_control *ctrl) 187{ 188 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 189 struct fbdev_display *fbdpy = fbsurf->fbdpy; 190 boolean ret = FALSE; 191 192 if (ctrl->swap_interval) 193 return FALSE; 194 if (ctrl->natt != NATIVE_ATTACHMENT_BACK_LEFT) 195 return FALSE; 196 197 if (!fbdpy->assume_fixed_vinfo) { 198 struct fb_var_screeninfo vinfo; 199 200 memset(&vinfo, 0, sizeof(vinfo)); 201 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) 202 return FALSE; 203 204 /* present the surface */ 205 if (fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) { 206 ret = resource_surface_present(fbsurf->rsurf, 207 ctrl->natt, (void *) &fbsurf->drawable); 208 } 209 210 fbsurf->width = vinfo.xres; 211 fbsurf->height = vinfo.yres; 212 213 if (resource_surface_set_size(fbsurf->rsurf, 214 fbsurf->width, fbsurf->height)) { 215 /* surface resized */ 216 fbsurf->sequence_number++; 217 fbdpy->event_handler->invalid_surface(&fbdpy->base, 218 &fbsurf->base, fbsurf->sequence_number); 219 } 220 } 221 else { 222 /* the drawable never changes */ 223 ret = resource_surface_present(fbsurf->rsurf, 224 ctrl->natt, (void *) &fbsurf->drawable); 225 } 226 227 return ret; 228} 229 230static void 231fbdev_surface_wait(struct native_surface *nsurf) 232{ 233 /* no-op */ 234} 235 236static void 237fbdev_surface_destroy(struct native_surface *nsurf) 238{ 239 struct fbdev_surface *fbsurf = fbdev_surface(nsurf); 240 241 resource_surface_destroy(fbsurf->rsurf); 242 FREE(fbsurf); 243} 244 245static struct native_surface * 246fbdev_display_create_window_surface(struct native_display *ndpy, 247 EGLNativeWindowType win, 248 const struct native_config *nconf) 249{ 250 struct fbdev_display *fbdpy = fbdev_display(ndpy); 251 struct fbdev_surface *fbsurf; 252 struct fb_var_screeninfo vinfo; 253 254 /* there is only one native window: NULL */ 255 if (win) 256 return NULL; 257 258 fbsurf = CALLOC_STRUCT(fbdev_surface); 259 if (!fbsurf) 260 return NULL; 261 262 fbsurf->fbdpy = fbdpy; 263 264 /* get current vinfo */ 265 if (fbdpy->assume_fixed_vinfo) { 266 vinfo = fbdpy->config_vinfo; 267 } 268 else { 269 memset(&vinfo, 0, sizeof(vinfo)); 270 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) { 271 FREE(fbsurf); 272 return NULL; 273 } 274 } 275 276 fbsurf->width = vinfo.xres; 277 fbsurf->height = vinfo.yres; 278 279 if (!fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) { 280 FREE(fbsurf); 281 return NULL; 282 } 283 284 fbsurf->rsurf = resource_surface_create(fbdpy->base.screen, 285 nconf->color_format, 286 PIPE_BIND_RENDER_TARGET | 287 PIPE_BIND_DISPLAY_TARGET); 288 if (!fbsurf->rsurf) { 289 FREE(fbsurf); 290 return NULL; 291 } 292 293 resource_surface_set_size(fbsurf->rsurf, fbsurf->width, fbsurf->height); 294 295 fbsurf->base.destroy = fbdev_surface_destroy; 296 fbsurf->base.present = fbdev_surface_present; 297 fbsurf->base.validate = fbdev_surface_validate; 298 fbsurf->base.wait = fbdev_surface_wait; 299 300 return &fbsurf->base; 301} 302 303static struct native_surface * 304fbdev_display_create_scanout_surface(struct native_display *ndpy, 305 const struct native_config *nconf, 306 uint width, uint height) 307{ 308 return fbdev_display_create_window_surface(ndpy, 309 (EGLNativeWindowType) NULL, nconf); 310} 311 312static boolean 313fbdev_display_program(struct native_display *ndpy, int crtc_idx, 314 struct native_surface *nsurf, uint x, uint y, 315 const struct native_connector **nconns, int num_nconns, 316 const struct native_mode *nmode) 317{ 318 return TRUE; 319} 320 321static const struct native_mode ** 322fbdev_display_get_modes(struct native_display *ndpy, 323 const struct native_connector *nconn, 324 int *num_modes) 325{ 326 static struct native_mode mode; 327 const struct native_mode **modes; 328 329 if (!mode.desc) { 330 struct fbdev_display *fbdpy = fbdev_display(ndpy); 331 mode.desc = "Current Mode"; 332 mode.width = fbdpy->config_vinfo.xres; 333 mode.height = fbdpy->config_vinfo.yres; 334 mode.refresh_rate = 60 * 1000; /* dummy */ 335 } 336 337 modes = MALLOC(sizeof(*modes)); 338 if (modes) { 339 modes[0] = &mode; 340 if (num_modes) 341 *num_modes = 1; 342 } 343 344 return modes; 345} 346 347static const struct native_connector ** 348fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors, 349 int *num_crtc) 350{ 351 static struct native_connector connector; 352 const struct native_connector **connectors; 353 354 connectors = MALLOC(sizeof(*connectors)); 355 if (connectors) { 356 connectors[0] = &connector; 357 if (num_connectors) 358 *num_connectors = 1; 359 } 360 361 return connectors; 362} 363 364/* remove modeset support one day! */ 365static const struct native_display_modeset fbdev_display_modeset = { 366 .get_connectors = fbdev_display_get_connectors, 367 .get_modes = fbdev_display_get_modes, 368 .create_scanout_surface = fbdev_display_create_scanout_surface, 369 .program = fbdev_display_program 370}; 371 372static const struct native_config ** 373fbdev_display_get_configs(struct native_display *ndpy, int *num_configs) 374{ 375 struct fbdev_display *fbdpy = fbdev_display(ndpy); 376 const struct native_config **configs; 377 378 configs = MALLOC(sizeof(*configs)); 379 if (configs) { 380 configs[0] = &fbdpy->config; 381 if (num_configs) 382 *num_configs = 1; 383 } 384 385 return configs; 386} 387 388static int 389fbdev_display_get_param(struct native_display *ndpy, 390 enum native_param_type param) 391{ 392 int val; 393 394 switch (param) { 395 case NATIVE_PARAM_PRESERVE_BUFFER: 396 val = 1; 397 break; 398 case NATIVE_PARAM_USE_NATIVE_BUFFER: 399 case NATIVE_PARAM_MAX_SWAP_INTERVAL: 400 default: 401 val = 0; 402 break; 403 } 404 405 return val; 406} 407 408static void 409fbdev_display_destroy(struct native_display *ndpy) 410{ 411 struct fbdev_display *fbdpy = fbdev_display(ndpy); 412 413 ndpy_uninit(&fbdpy->base); 414 close(fbdpy->fd); 415 FREE(fbdpy); 416} 417 418static boolean 419fbdev_display_init_screen(struct native_display *ndpy) 420{ 421 struct fbdev_display *fbdpy = fbdev_display(ndpy); 422 struct sw_winsys *ws; 423 424 ws = fbdev_create_sw_winsys(fbdpy->fd); 425 if (!ws) 426 return FALSE; 427 428 fbdpy->base.screen = fbdpy->event_handler->new_sw_screen(&fbdpy->base, ws); 429 if (!fbdpy->base.screen) { 430 if (ws->destroy) 431 ws->destroy(ws); 432 return FALSE; 433 } 434 435 if (!fbdpy->base.screen->is_format_supported(fbdpy->base.screen, 436 fbdpy->config.color_format, PIPE_TEXTURE_2D, 0, 437 PIPE_BIND_RENDER_TARGET)) { 438 fbdpy->base.screen->destroy(fbdpy->base.screen); 439 fbdpy->base.screen = NULL; 440 return FALSE; 441 } 442 443 return TRUE; 444} 445 446static boolean 447fbdev_display_init_config(struct native_display *ndpy) 448{ 449 struct fbdev_display *fbdpy = fbdev_display(ndpy); 450 struct native_config *nconf = &fbdpy->config; 451 452 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->config_vinfo)) 453 return FALSE; 454 455 nconf->color_format = vinfo_to_format(&fbdpy->config_vinfo); 456 if (nconf->color_format == PIPE_FORMAT_NONE) 457 return FALSE; 458 459 nconf->buffer_mask = (1 << NATIVE_ATTACHMENT_BACK_LEFT); 460 461 nconf->window_bit = TRUE; 462 463 return TRUE; 464} 465 466static struct native_display * 467fbdev_display_create(int fd, const struct native_event_handler *event_handler) 468{ 469 struct fbdev_display *fbdpy; 470 471 fbdpy = CALLOC_STRUCT(fbdev_display); 472 if (!fbdpy) 473 return NULL; 474 475 fbdpy->fd = fd; 476 fbdpy->event_handler = event_handler; 477 478 if (ioctl(fbdpy->fd, FBIOGET_FSCREENINFO, &fbdpy->finfo)) 479 goto fail; 480 481 if (fbdpy->finfo.visual != FB_VISUAL_TRUECOLOR || 482 fbdpy->finfo.type != FB_TYPE_PACKED_PIXELS) 483 goto fail; 484 485 if (!fbdev_display_init_config(&fbdpy->base)) 486 goto fail; 487 488 fbdpy->assume_fixed_vinfo = TRUE; 489 490 fbdpy->base.init_screen = fbdev_display_init_screen; 491 fbdpy->base.destroy = fbdev_display_destroy; 492 fbdpy->base.get_param = fbdev_display_get_param; 493 fbdpy->base.get_configs = fbdev_display_get_configs; 494 495 fbdpy->base.create_window_surface = fbdev_display_create_window_surface; 496 497 /* we'd like to remove modeset support one day */ 498 fbdpy->config.scanout_bit = TRUE; 499 fbdpy->base.modeset = &fbdev_display_modeset; 500 501 return &fbdpy->base; 502 503fail: 504 FREE(fbdpy); 505 return NULL; 506} 507 508static const struct native_event_handler *fbdev_event_handler; 509 510static struct native_display * 511native_create_display(void *dpy, boolean use_sw) 512{ 513 struct native_display *ndpy; 514 int fd; 515 516 /* well, this makes fd 0 being ignored */ 517 if (!dpy) { 518 fd = open("/dev/fb0", O_RDWR); 519 } 520 else { 521 fd = dup((int) pointer_to_intptr(dpy)); 522 } 523 if (fd < 0) 524 return NULL; 525 526 ndpy = fbdev_display_create(fd, fbdev_event_handler); 527 if (!ndpy) 528 close(fd); 529 530 return ndpy; 531} 532 533static const struct native_platform fbdev_platform = { 534 "FBDEV", /* name */ 535 native_create_display 536}; 537 538const struct native_platform * 539native_get_fbdev_platform(const struct native_event_handler *event_handler) 540{ 541 fbdev_event_handler = event_handler; 542 return &fbdev_platform; 543} 544