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