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