Tizen 2.0 Release
[profile/ivi/osmesa.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 }