native_fbdev.c revision 1de377bd307f3c4aa63f1a14a693f81af54d56ee
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
60struct 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
73struct 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
85static INLINE struct fbdev_display *
86fbdev_display(const struct native_display *ndpy)
87{
88   return (struct fbdev_display *) ndpy;
89}
90
91static INLINE struct fbdev_surface *
92fbdev_surface(const struct native_surface *nsurf)
93{
94   return (struct fbdev_surface *) nsurf;
95}
96
97static boolean
98fbdev_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
119static enum pipe_format
120vinfo_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
148static boolean
149fbdev_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
185static boolean
186fbdev_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
231static void
232fbdev_surface_wait(struct native_surface *nsurf)
233{
234   /* no-op */
235}
236
237static void
238fbdev_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
246static struct native_surface *
247fbdev_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
304static struct native_surface *
305fbdev_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
313static boolean
314fbdev_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
322static const struct native_mode **
323fbdev_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
348static const struct native_connector **
349fbdev_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! */
366static 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
373static const struct native_config **
374fbdev_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
389static int
390fbdev_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
409static void
410fbdev_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
419static boolean
420fbdev_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
447static boolean
448fbdev_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
467static struct native_display *
468fbdev_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
504fail:
505   FREE(fbdpy);
506   return NULL;
507}
508
509static const struct native_event_handler *fbdev_event_handler;
510
511static struct native_display *
512native_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
543static const struct native_platform fbdev_platform = {
544   "FBDEV", /* name */
545   native_create_display
546};
547
548const struct native_platform *
549native_get_fbdev_platform(const struct native_event_handler *event_handler)
550{
551   fbdev_event_handler = event_handler;
552   return &fbdev_platform;
553}
554