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 <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 *
fbdev_display(const struct native_display * ndpy)86 fbdev_display(const struct native_display *ndpy)
87 {
88    return (struct fbdev_display *) ndpy;
89 }
90 
91 static INLINE struct fbdev_surface *
fbdev_surface(const struct native_surface * nsurf)92 fbdev_surface(const struct native_surface *nsurf)
93 {
94    return (struct fbdev_surface *) nsurf;
95 }
96 
97 static boolean
fbdev_surface_validate(struct native_surface * nsurf,uint attachment_mask,unsigned int * seq_num,struct pipe_resource ** textures,int * width,int * height)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
vinfo_to_format(const struct fb_var_screeninfo * vinfo)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
fbdev_surface_update_drawable(struct native_surface * nsurf,const struct fb_var_screeninfo * vinfo)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
fbdev_surface_present(struct native_surface * nsurf,const struct native_present_control * ctrl)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
fbdev_surface_wait(struct native_surface * nsurf)232 fbdev_surface_wait(struct native_surface *nsurf)
233 {
234    /* no-op */
235 }
236 
237 static void
fbdev_surface_destroy(struct native_surface * nsurf)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 *
fbdev_display_create_window_surface(struct native_display * ndpy,EGLNativeWindowType win,const struct native_config * nconf)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 *
fbdev_display_create_scanout_surface(struct native_display * ndpy,const struct native_config * nconf,uint width,uint height)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
fbdev_display_program(struct native_display * ndpy,int crtc_idx,struct native_surface * nsurf,uint x,uint y,const struct native_connector ** nconns,int num_nconns,const struct native_mode * nmode)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 **
fbdev_display_get_modes(struct native_display * ndpy,const struct native_connector * nconn,int * num_modes)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 **
fbdev_display_get_connectors(struct native_display * ndpy,int * num_connectors,int * num_crtc)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 **
fbdev_display_get_configs(struct native_display * ndpy,int * num_configs)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
fbdev_display_get_param(struct native_display * ndpy,enum native_param_type param)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
fbdev_display_destroy(struct native_display * ndpy)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
fbdev_display_init_screen(struct native_display * ndpy)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
fbdev_display_init_config(struct native_display * ndpy)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 *
fbdev_display_create(int fd,const struct native_event_handler * event_handler)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 *
native_create_display(void * dpy,boolean use_sw)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 *
native_get_fbdev_platform(const struct native_event_handler * event_handler)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