Merge branch 'gallium-polygon-stipple'
[mesa.git] / src / gallium / state_trackers / egl / fbdev / native_fbdev.c
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
59 struct 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
72 struct 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
84 static INLINE struct fbdev_display *
85 fbdev_display(const struct native_display *ndpy)
86 {
87 return (struct fbdev_display *) ndpy;
88 }
89
90 static INLINE struct fbdev_surface *
91 fbdev_surface(const struct native_surface *nsurf)
92 {
93 return (struct fbdev_surface *) nsurf;
94 }
95
96 static boolean
97 fbdev_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
118 static enum pipe_format
119 vinfo_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
147 static boolean
148 fbdev_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
184 static boolean
185 fbdev_surface_present(struct native_surface *nsurf,
186 enum native_attachment natt,
187 boolean preserve,
188 uint swap_interval)
189 {
190 struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
191 struct fbdev_display *fbdpy = fbsurf->fbdpy;
192 boolean ret = FALSE;
193
194 if (swap_interval)
195 return FALSE;
196 if (natt != NATIVE_ATTACHMENT_BACK_LEFT)
197 return FALSE;
198
199 if (!fbdpy->assume_fixed_vinfo) {
200 struct fb_var_screeninfo vinfo;
201
202 memset(&vinfo, 0, sizeof(vinfo));
203 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo))
204 return FALSE;
205
206 /* present the surface */
207 if (fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
208 ret = resource_surface_present(fbsurf->rsurf,
209 natt, (void *) &fbsurf->drawable);
210 }
211
212 fbsurf->width = vinfo.xres;
213 fbsurf->height = vinfo.yres;
214
215 if (resource_surface_set_size(fbsurf->rsurf,
216 fbsurf->width, fbsurf->height)) {
217 /* surface resized */
218 fbsurf->sequence_number++;
219 fbdpy->event_handler->invalid_surface(&fbdpy->base,
220 &fbsurf->base, fbsurf->sequence_number);
221 }
222 }
223 else {
224 /* the drawable never changes */
225 ret = resource_surface_present(fbsurf->rsurf,
226 natt, (void *) &fbsurf->drawable);
227 }
228
229 return ret;
230 }
231
232 static void
233 fbdev_surface_wait(struct native_surface *nsurf)
234 {
235 /* no-op */
236 }
237
238 static void
239 fbdev_surface_destroy(struct native_surface *nsurf)
240 {
241 struct fbdev_surface *fbsurf = fbdev_surface(nsurf);
242
243 resource_surface_destroy(fbsurf->rsurf);
244 FREE(fbsurf);
245 }
246
247 static struct native_surface *
248 fbdev_display_create_window_surface(struct native_display *ndpy,
249 EGLNativeWindowType win,
250 const struct native_config *nconf)
251 {
252 struct fbdev_display *fbdpy = fbdev_display(ndpy);
253 struct fbdev_surface *fbsurf;
254 struct fb_var_screeninfo vinfo;
255
256 /* there is only one native window: NULL */
257 if (win)
258 return NULL;
259
260 fbsurf = CALLOC_STRUCT(fbdev_surface);
261 if (!fbsurf)
262 return NULL;
263
264 fbsurf->fbdpy = fbdpy;
265
266 /* get current vinfo */
267 if (fbdpy->assume_fixed_vinfo) {
268 vinfo = fbdpy->config_vinfo;
269 }
270 else {
271 memset(&vinfo, 0, sizeof(vinfo));
272 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &vinfo)) {
273 FREE(fbsurf);
274 return NULL;
275 }
276 }
277
278 fbsurf->width = vinfo.xres;
279 fbsurf->height = vinfo.yres;
280
281 if (!fbdev_surface_update_drawable(&fbsurf->base, &vinfo)) {
282 FREE(fbsurf);
283 return NULL;
284 }
285
286 fbsurf->rsurf = resource_surface_create(fbdpy->base.screen,
287 nconf->color_format,
288 PIPE_BIND_RENDER_TARGET |
289 PIPE_BIND_DISPLAY_TARGET);
290 if (!fbsurf->rsurf) {
291 FREE(fbsurf);
292 return NULL;
293 }
294
295 resource_surface_set_size(fbsurf->rsurf, fbsurf->width, fbsurf->height);
296
297 fbsurf->base.destroy = fbdev_surface_destroy;
298 fbsurf->base.present = fbdev_surface_present;
299 fbsurf->base.validate = fbdev_surface_validate;
300 fbsurf->base.wait = fbdev_surface_wait;
301
302 return &fbsurf->base;
303 }
304
305 static struct native_surface *
306 fbdev_display_create_scanout_surface(struct native_display *ndpy,
307 const struct native_config *nconf,
308 uint width, uint height)
309 {
310 return fbdev_display_create_window_surface(ndpy,
311 (EGLNativeWindowType) NULL, nconf);
312 }
313
314 static boolean
315 fbdev_display_program(struct native_display *ndpy, int crtc_idx,
316 struct native_surface *nsurf, uint x, uint y,
317 const struct native_connector **nconns, int num_nconns,
318 const struct native_mode *nmode)
319 {
320 return TRUE;
321 }
322
323 static const struct native_mode **
324 fbdev_display_get_modes(struct native_display *ndpy,
325 const struct native_connector *nconn,
326 int *num_modes)
327 {
328 static struct native_mode mode;
329 const struct native_mode **modes;
330
331 if (!mode.desc) {
332 struct fbdev_display *fbdpy = fbdev_display(ndpy);
333 mode.desc = "Current Mode";
334 mode.width = fbdpy->config_vinfo.xres;
335 mode.height = fbdpy->config_vinfo.yres;
336 mode.refresh_rate = 60 * 1000; /* dummy */
337 }
338
339 modes = MALLOC(sizeof(*modes));
340 if (modes) {
341 modes[0] = &mode;
342 if (num_modes)
343 *num_modes = 1;
344 }
345
346 return modes;
347 }
348
349 static const struct native_connector **
350 fbdev_display_get_connectors(struct native_display *ndpy, int *num_connectors,
351 int *num_crtc)
352 {
353 static struct native_connector connector;
354 const struct native_connector **connectors;
355
356 connectors = MALLOC(sizeof(*connectors));
357 if (connectors) {
358 connectors[0] = &connector;
359 if (num_connectors)
360 *num_connectors = 1;
361 }
362
363 return connectors;
364 }
365
366 /* remove modeset support one day! */
367 static const struct native_display_modeset fbdev_display_modeset = {
368 .get_connectors = fbdev_display_get_connectors,
369 .get_modes = fbdev_display_get_modes,
370 .create_scanout_surface = fbdev_display_create_scanout_surface,
371 .program = fbdev_display_program
372 };
373
374 static const struct native_config **
375 fbdev_display_get_configs(struct native_display *ndpy, int *num_configs)
376 {
377 struct fbdev_display *fbdpy = fbdev_display(ndpy);
378 const struct native_config **configs;
379
380 configs = MALLOC(sizeof(*configs));
381 if (configs) {
382 configs[0] = &fbdpy->config;
383 if (num_configs)
384 *num_configs = 1;
385 }
386
387 return configs;
388 }
389
390 static int
391 fbdev_display_get_param(struct native_display *ndpy,
392 enum native_param_type param)
393 {
394 int val;
395
396 switch (param) {
397 case NATIVE_PARAM_PRESERVE_BUFFER:
398 val = 1;
399 break;
400 case NATIVE_PARAM_USE_NATIVE_BUFFER:
401 case NATIVE_PARAM_MAX_SWAP_INTERVAL:
402 default:
403 val = 0;
404 break;
405 }
406
407 return val;
408 }
409
410 static void
411 fbdev_display_destroy(struct native_display *ndpy)
412 {
413 struct fbdev_display *fbdpy = fbdev_display(ndpy);
414
415 ndpy_uninit(&fbdpy->base);
416 close(fbdpy->fd);
417 FREE(fbdpy);
418 }
419
420 static boolean
421 fbdev_display_init_screen(struct native_display *ndpy)
422 {
423 struct fbdev_display *fbdpy = fbdev_display(ndpy);
424 struct sw_winsys *ws;
425
426 ws = fbdev_create_sw_winsys(fbdpy->fd);
427 if (!ws)
428 return FALSE;
429
430 fbdpy->base.screen = fbdpy->event_handler->new_sw_screen(&fbdpy->base, ws);
431 if (!fbdpy->base.screen) {
432 if (ws->destroy)
433 ws->destroy(ws);
434 return FALSE;
435 }
436
437 if (!fbdpy->base.screen->is_format_supported(fbdpy->base.screen,
438 fbdpy->config.color_format, PIPE_TEXTURE_2D, 0,
439 PIPE_BIND_RENDER_TARGET)) {
440 fbdpy->base.screen->destroy(fbdpy->base.screen);
441 fbdpy->base.screen = NULL;
442 return FALSE;
443 }
444
445 return TRUE;
446 }
447
448 static boolean
449 fbdev_display_init_config(struct native_display *ndpy)
450 {
451 struct fbdev_display *fbdpy = fbdev_display(ndpy);
452 struct native_config *nconf = &fbdpy->config;
453
454 if (ioctl(fbdpy->fd, FBIOGET_VSCREENINFO, &fbdpy->config_vinfo))
455 return FALSE;
456
457 nconf->color_format = vinfo_to_format(&fbdpy->config_vinfo);
458 if (nconf->color_format == PIPE_FORMAT_NONE)
459 return FALSE;
460
461 nconf->buffer_mask = (1 << NATIVE_ATTACHMENT_BACK_LEFT);
462
463 nconf->window_bit = TRUE;
464
465 return TRUE;
466 }
467
468 static struct native_display *
469 fbdev_display_create(int fd, const struct native_event_handler *event_handler)
470 {
471 struct fbdev_display *fbdpy;
472
473 fbdpy = CALLOC_STRUCT(fbdev_display);
474 if (!fbdpy)
475 return NULL;
476
477 fbdpy->fd = fd;
478 fbdpy->event_handler = event_handler;
479
480 if (ioctl(fbdpy->fd, FBIOGET_FSCREENINFO, &fbdpy->finfo))
481 goto fail;
482
483 if (fbdpy->finfo.visual != FB_VISUAL_TRUECOLOR ||
484 fbdpy->finfo.type != FB_TYPE_PACKED_PIXELS)
485 goto fail;
486
487 if (!fbdev_display_init_config(&fbdpy->base))
488 goto fail;
489
490 fbdpy->assume_fixed_vinfo = TRUE;
491
492 fbdpy->base.init_screen = fbdev_display_init_screen;
493 fbdpy->base.destroy = fbdev_display_destroy;
494 fbdpy->base.get_param = fbdev_display_get_param;
495 fbdpy->base.get_configs = fbdev_display_get_configs;
496
497 fbdpy->base.create_window_surface = fbdev_display_create_window_surface;
498
499 /* we'd like to remove modeset support one day */
500 fbdpy->config.scanout_bit = TRUE;
501 fbdpy->base.modeset = &fbdev_display_modeset;
502
503 return &fbdpy->base;
504
505 fail:
506 FREE(fbdpy);
507 return NULL;
508 }
509
510 static const struct native_event_handler *fbdev_event_handler;
511
512 static struct native_display *
513 native_create_display(void *dpy, boolean use_sw)
514 {
515 struct native_display *ndpy;
516 int fd;
517
518 /* well, this makes fd 0 being ignored */
519 if (!dpy) {
520 fd = open("/dev/fb0", O_RDWR);
521 }
522 else {
523 fd = dup((int) pointer_to_intptr(dpy));
524 }
525 if (fd < 0)
526 return NULL;
527
528 ndpy = fbdev_display_create(fd, fbdev_event_handler);
529 if (!ndpy)
530 close(fd);
531
532 return ndpy;
533 }
534
535 static const struct native_platform fbdev_platform = {
536 "FBDEV", /* name */
537 native_create_display
538 };
539
540 const struct native_platform *
541 native_get_fbdev_platform(const struct native_event_handler *event_handler)
542 {
543 fbdev_event_handler = event_handler;
544 return &fbdev_platform;
545 }