Home | History | Annotate | Download | only in fbdev
      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