2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2012
4 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
5 * See the COPYING file in the top-level directory.
13 #include <sys/types.h>
16 #include <sys/ioctl.h>
22 #include "linux/fbdev.h"
23 #include "linux/xenv.h"
25 #define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
28 struct omapfb_plane_info pi;
29 struct omapfb_mem_info mi;
30 struct omapfb_plane_info pi_old;
31 struct omapfb_mem_info mi_old;
35 static int read_sysfs(const char *fname, char *buff, size_t size);
37 static int switch_tv_layer(struct omapfb_state *ostate, int layer)
44 tmp = getenv("SDL_OMAP_NO_PANDORA_TV");
45 if (tmp != NULL && !!strtol(tmp, NULL, 0))
48 if (stat("/proc/pandora", &st) != 0)
49 /* not pandora, don't mess with stuff */
52 ret = read_sysfs("/sys/devices/platform/omapdss/display1/enabled",
55 err("couldn't check display1 state");
59 if (strtol(buf, NULL, 0) == 0)
60 /* TV-out not enabled */
63 snprintf(buf, sizeof(buf),
64 "sudo -n /usr/pandora/scripts/op_tvout.sh -l %d", layer);
67 ostate->tv_layer = layer;
74 static const char *get_fb_device(void)
76 const char *fbname = getenv("SDL_FBDEV");
83 static int osdl_setup_omapfb(struct omapfb_state *ostate, int fd, int enabled,
84 int x, int y, int w, int h, int mem, int *buffer_count)
86 struct omapfb_plane_info pi;
87 struct omapfb_mem_info mi;
88 int mem_blocks = *buffer_count;
89 unsigned int size_cur;
92 memset(&pi, 0, sizeof(pi));
93 memset(&mi, 0, sizeof(mi));
95 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
97 err_perror("QUERY_PLANE");
101 ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
103 err_perror("QUERY_MEM");
108 /* must disable when changing stuff */
111 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
113 err_perror("SETUP_PLANE");
116 /* allocate more mem, if needed */
117 for (; size_cur < mem * mem_blocks && mem_blocks > 0; mem_blocks--) {
118 mi.size = mem * mem_blocks;
119 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
124 if (mem_blocks <= 0) {
125 err("failed to allocate at least %d bytes of vram:\n", mem);
126 err_perror("SETUP_MEM");
130 *buffer_count = mem_blocks;
136 pi.enabled = enabled;
138 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
140 err_perror("SETUP_PLANE");
141 err("(%d %d %d %d)\n", x, y, w, h);
150 static int osdl_setup_omapfb_enable(struct omapfb_state *ostate,
155 switch_tv_layer(ostate, 1);
157 ostate->pi.enabled = enabled;
158 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &ostate->pi);
160 err_perror("SETUP_PLANE");
165 static int read_sysfs(const char *fname, char *buff, size_t size)
170 f = fopen(fname, "r");
172 err_perror("open %s: ", fname);
176 ret = fread(buff, 1, size - 1, f);
179 err_perror("read %s: ", fname);
184 for (ret--; ret >= 0 && isspace(buff[ret]); ret--)
190 int read_vscreeninfo(const char *fbname, int *w, int *h)
192 struct fb_var_screeninfo fbvar;
195 fd = open(fbname, O_RDWR);
197 err_perror("open %s", fbname);
201 ret = ioctl(fd, FBIOGET_VSCREENINFO, &fbvar);
205 err_perror("ioctl %s", fbname);
209 if (fbvar.xres == 0 || fbvar.yres == 0)
217 int osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata)
219 int fb_id, overlay_id = -1, screen_id = -1;
220 char buff[64], screen_name[64];
227 pdata->phys_w = pdata->phys_h = 0;
229 fbname = get_fb_device();
231 /* Figure out screen resolution, we need to know default resolution
232 * to report to SDL and for centering stuff.
233 * The only way to achieve this seems to be walking some sysfs files.. */
234 ret = stat(fbname, &status);
236 err_perror("can't stat %s", fbname);
239 fb_id = minor(status.st_rdev);
241 snprintf(buff, sizeof(buff), "/sys/class/graphics/fb%d/overlays", fb_id);
242 f = fopen(buff, "r");
244 err("can't open %s, skip screen detection", buff);
248 ret = fscanf(f, "%d", &overlay_id);
251 err("can't parse %s, skip screen detection", buff);
255 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/overlay%d/manager", overlay_id);
256 ret = read_sysfs(buff, screen_name, sizeof(screen_name));
258 err("skip screen detection");
263 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/name", i);
264 ret = read_sysfs(buff, buff, sizeof(buff));
268 if (strcmp(screen_name, buff) == 0) {
275 err("could not find screen");
279 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/timings", screen_id);
280 f = fopen(buff, "r");
282 err("can't open %s, skip screen detection", buff);
286 ret = fscanf(f, "%*d,%d/%*d/%*d/%*d,%d/%*d/%*d/%*d", &w, &h);
289 err("can't parse %s (%d), skip screen detection", buff, ret);
293 log("detected %dx%d '%s' (%d) screen attached to fb %d and overlay %d",
294 w, h, screen_name, screen_id, fb_id, overlay_id);
301 /* attempt to extract this from FB then */
302 ret = read_vscreeninfo(fbname, &pdata->phys_w, &pdata->phys_h);
303 if (ret != 0 && strcmp(fbname, "/dev/fb0") != 0) {
305 ret = read_vscreeninfo("/dev/fb0", &pdata->phys_w, &pdata->phys_h);
309 err("VSCREENINFO has nothing meaningful");
316 static int osdl_setup_omap_layer(struct SDL_PrivateVideoData *pdata,
317 const char *fbname, int width, int height, int bpp, int *buffer_count)
319 int x = 0, y = 0, w = width, h = height; /* layer size and pos */
320 int screen_w = w, screen_h = h;
326 pdata->layer_x = pdata->layer_y = pdata->layer_w = pdata->layer_h = 0;
328 if (pdata->phys_w != 0)
329 screen_w = pdata->phys_w;
330 if (pdata->phys_h != 0)
331 screen_h = pdata->phys_h;
333 fd = open(fbname, O_RDWR);
335 err_perror("open %s", fbname);
339 /* FIXME: assuming layer doesn't change here */
340 if (pdata->layer_state == NULL) {
341 struct omapfb_state *slayer;
342 slayer = calloc(1, sizeof(*slayer));
346 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &slayer->pi_old);
348 err_perror("QUERY_PLANE");
352 ret = ioctl(fd, OMAPFB_QUERY_MEM, &slayer->mi_old);
354 err_perror("QUERY_MEM");
358 pdata->layer_state = slayer;
361 tmp = getenv("SDL_OMAP_LAYER_SIZE");
363 if (strcasecmp(tmp, "fullscreen") == 0) {
364 w = screen_w, h = screen_h;
366 else if (strcasecmp(tmp, "scaled") == 0) {
367 float factor = MIN(((float)screen_w) / width, ((float)screen_h) / height);
368 w = (int)(factor*width), h = (int)(factor*height);
370 else if (strcasecmp(tmp, "pixelperfect") == 0) {
371 float factor = MIN(((float)screen_w) / width, ((float)screen_h) / height);
372 w = ((int)factor) * width, h = ((int)factor) * height;
373 /* factor < 1.f => 0x0 layer, so fall back to 'scaled' */
375 w = (int)(factor * width), h = (int)(factor * height);
378 else if (sscanf(tmp, "%dx%d", &tmp_w, &tmp_h) == 2) {
379 w = tmp_w, h = tmp_h;
382 err("layer size specified incorrectly, "
383 "should be like 800x480");
387 /* the layer can't be set larger than screen */
388 tmp_w = w, tmp_h = h;
393 if (w != tmp_w || h != tmp_h)
394 log("layer resized %dx%d -> %dx%d to fit screen", tmp_w, tmp_h, w, h);
396 x = screen_w / 2 - w / 2;
397 y = screen_h / 2 - h / 2;
398 ret = osdl_setup_omapfb(pdata->layer_state, fd, 0, x, y, w, h,
399 width * height * ((bpp + 7) / 8), buffer_count);
413 void *osdl_video_set_mode(struct SDL_PrivateVideoData *pdata,
414 int border_l, int border_r, int border_t, int border_b,
415 int width, int height, int bpp, int *doublebuf,
416 const char *wm_title)
418 int buffers_try, buffers_set;
423 fbname = get_fb_device();
425 if (pdata->fbdev != NULL) {
426 vout_fbdev_finish(pdata->fbdev);
430 buffers_try = buffers_set = 1;
432 /* actually try tripple buffering for reduced chance of tearing */
433 buffers_try = buffers_set = 3;
435 ret = osdl_setup_omap_layer(pdata, fbname, width, height, bpp, &buffers_set);
439 pdata->fbdev = vout_fbdev_init(fbname, &width, &height, bpp, buffers_set);
440 if (pdata->fbdev == NULL)
443 if (border_l | border_r | border_t | border_b) {
444 width -= border_l + border_r;
445 height -= border_t + border_b;
446 result = vout_fbdev_resize(pdata->fbdev, width, height, bpp,
447 border_l, border_r, border_t, border_b, buffers_set);
450 result = osdl_video_flip(pdata);
455 if (!pdata->xenv_up) {
456 int xenv_flags = XENV_CAP_KEYS | XENV_CAP_MOUSE;
457 ret = xenv_init(&xenv_flags, wm_title);
460 pdata->xenv_mouse = (xenv_flags & XENV_CAP_MOUSE) ? 1 : 0;
464 ret = osdl_setup_omapfb_enable(pdata->layer_state,
465 vout_fbdev_get_fd(pdata->fbdev), 1);
467 err("layer enable failed");
471 if (buffers_try != buffers_set) {
472 log("only %d/%d buffers available, expect tearing\n",
473 buffers_set, buffers_try);
474 *doublebuf = buffers_set > 1;
480 osdl_video_finish(pdata);
484 void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
488 if (pdata->fbdev == NULL)
491 ret = vout_fbdev_flip(pdata->fbdev);
493 if (pdata->cfg_force_vsync)
494 vout_fbdev_wait_vsync(pdata->fbdev);
499 void *osdl_video_get_active_buffer(struct SDL_PrivateVideoData *pdata)
501 if (pdata->fbdev == NULL)
504 return vout_fbdev_get_active_mem(pdata->fbdev);
507 int osdl_video_pause(struct SDL_PrivateVideoData *pdata, int is_pause)
509 struct omapfb_state *state = pdata->layer_state;
510 struct omapfb_plane_info pi;
511 struct omapfb_mem_info mi;
516 if (pdata->fbdev != NULL)
517 fd = vout_fbdev_get_fd(pdata->fbdev);
519 err("bad fd %d", fd);
523 err("missing layer state\n");
528 ret = vout_fbdev_save(pdata->fbdev);
533 enabled = pi.enabled;
540 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
542 err_perror("SETUP_PLANE");
546 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
548 err_perror("SETUP_MEM");
551 ret = vout_fbdev_restore(pdata->fbdev);
553 err("fbdev_restore failed\n");
560 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
562 err_perror("SETUP_PLANE");
570 int osdl_video_get_window(void **display, int *screen, void **window)
572 return xenv_get_window(display, screen, window);
575 void osdl_video_finish(struct SDL_PrivateVideoData *pdata)
577 static const char *fbname;
579 fbname = get_fb_device();
580 if (pdata->fbdev != NULL) {
581 vout_fbdev_finish(pdata->fbdev);
585 /* restore the OMAP layer */
586 if (pdata->layer_state != NULL) {
587 struct omapfb_state *slayer = pdata->layer_state;
590 if (slayer->tv_layer)
591 switch_tv_layer(slayer, 0);
593 fd = open(fbname, O_RDWR);
595 int enabled = slayer->pi_old.enabled;
597 /* be sure to disable while setting up */
598 slayer->pi_old.enabled = 0;
599 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
600 ioctl(fd, OMAPFB_SETUP_MEM, &slayer->mi_old);
602 slayer->pi_old.enabled = enabled;
603 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
608 pdata->layer_state = NULL;
611 if (pdata->xenv_up) {