pandora: do automatic layer switching for TV
[sdl_omap.git] / src / video / omapdss / osdl_video.c
CommitLineData
f641fccb 1/*
0c7caf2b 2 * (C) GraÅžvydas "notaz" Ignotas, 2010-2012
f641fccb 3 *
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.
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <strings.h>
12#include <ctype.h>
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <fcntl.h>
16#include <sys/ioctl.h>
17#include <unistd.h>
18#include <linux/fb.h>
19
9ab462cd 20#include "osdl.h"
f641fccb 21#include "omapfb.h"
22#include "linux/fbdev.h"
f9b3f440 23#include "linux/xenv.h"
f641fccb 24
21e209de
DA
25#define MIN(a, b) ( ((a) < (b)) ? (a) : (b) )
26
090bcb85 27struct omapfb_state {
f641fccb 28 struct omapfb_plane_info pi;
29 struct omapfb_mem_info mi;
090bcb85 30 struct omapfb_plane_info pi_old;
31 struct omapfb_mem_info mi_old;
79b5de04 32 int tv_layer;
f641fccb 33};
34
79b5de04 35static int read_sysfs(const char *fname, char *buff, size_t size);
36
37static int switch_tv_layer(struct omapfb_state *ostate, int layer)
38{
39 const char *tmp;
40 struct stat st;
41 char buf[128];
42 int ret;
43
44 tmp = getenv("SDL_OMAP_NO_PANDORA_TV");
45 if (tmp != NULL && !!strtol(tmp, NULL, 0))
46 return 0;
47
48 if (stat("/proc/pandora", &st) != 0)
49 /* not pandora, don't mess with stuff */
50 return 0;
51
52 ret = read_sysfs("/sys/devices/platform/omapdss/display1/enabled",
53 buf, sizeof(buf));
54 if (ret < 0) {
55 err("couldn't check display1 state");
56 return -1;
57 }
58
59 if (strtol(buf, NULL, 0) == 0)
60 /* TV-out not enabled */
61 return 0;
62
63 snprintf(buf, sizeof(buf),
64 "sudo -n /usr/pandora/scripts/op_tvout.sh -l %d", layer);
65 ret = system(buf);
66 if (ret >= 0) {
67 ostate->tv_layer = layer;
68 ret = 0;
69 }
70
71 return ret;
72}
73
b9a19b44 74static const char *get_fb_device(void)
75{
76 const char *fbname = getenv("SDL_FBDEV");
77 if (fbname == NULL)
78 fbname = "/dev/fb1";
79
80 return fbname;
81}
82
090bcb85 83static 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)
f641fccb 85{
86 struct omapfb_plane_info pi;
87 struct omapfb_mem_info mi;
0c7caf2b 88 int mem_blocks = *buffer_count;
3b1e72e7 89 unsigned int size_cur;
f641fccb 90 int ret;
91
5f4b1fd3 92 memset(&pi, 0, sizeof(pi));
93 memset(&mi, 0, sizeof(mi));
94
f641fccb 95 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
96 if (ret != 0) {
7b66578c 97 err_perror("QUERY_PLANE");
f641fccb 98 return -1;
99 }
100
101 ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
102 if (ret != 0) {
7b66578c 103 err_perror("QUERY_MEM");
f641fccb 104 return -1;
105 }
3b1e72e7 106 size_cur = mi.size;
f641fccb 107
108 /* must disable when changing stuff */
109 if (pi.enabled) {
110 pi.enabled = 0;
111 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
112 if (ret != 0)
7b66578c 113 err_perror("SETUP_PLANE");
f641fccb 114 }
115
3b1e72e7 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);
120 if (ret == 0)
121 break;
122 mi.size = size_cur;
f641fccb 123 }
3b1e72e7 124 if (mem_blocks <= 0) {
125 err("failed to allocate at least %d bytes of vram:\n", mem);
126 err_perror("SETUP_MEM");
127 return -1;
128 }
129
0c7caf2b 130 *buffer_count = mem_blocks;
f641fccb 131
132 pi.pos_x = x;
133 pi.pos_y = y;
134 pi.out_width = w;
135 pi.out_height = h;
136 pi.enabled = enabled;
137
138 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
139 if (ret != 0) {
7b66578c 140 err_perror("SETUP_PLANE");
6f13ad97 141 err("(%d %d %d %d)\n", x, y, w, h);
f641fccb 142 return -1;
143 }
144
090bcb85 145 ostate->pi = pi;
146 ostate->mi = mi;
f641fccb 147 return 0;
148}
149
3af567be 150static int osdl_setup_omapfb_enable(struct omapfb_state *ostate,
151 int fd, int enabled)
152{
153 int ret;
154
79b5de04 155 switch_tv_layer(ostate, 1);
156
3af567be 157 ostate->pi.enabled = enabled;
158 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &ostate->pi);
159 if (ret != 0)
160 err_perror("SETUP_PLANE");
161
162 return ret;
163}
164
f641fccb 165static int read_sysfs(const char *fname, char *buff, size_t size)
166{
167 FILE *f;
168 int ret;
169
170 f = fopen(fname, "r");
171 if (f == NULL) {
7b66578c 172 err_perror("open %s: ", fname);
f641fccb 173 return -1;
174 }
175
176 ret = fread(buff, 1, size - 1, f);
177 fclose(f);
178 if (ret <= 0) {
7b66578c 179 err_perror("read %s: ", fname);
f641fccb 180 return -1;
181 }
182
183 buff[ret] = 0;
184 for (ret--; ret >= 0 && isspace(buff[ret]); ret--)
185 buff[ret] = 0;
186
187 return 0;
188}
189
34edc246 190int read_vscreeninfo(const char *fbname, int *w, int *h)
191{
192 struct fb_var_screeninfo fbvar;
193 int ret, fd;
194
195 fd = open(fbname, O_RDWR);
196 if (fd == -1) {
197 err_perror("open %s", fbname);
198 return -1;
199 }
200
201 ret = ioctl(fd, FBIOGET_VSCREENINFO, &fbvar);
202 close(fd);
203
204 if (ret == -1) {
205 err_perror("ioctl %s", fbname);
206 return -1;
207 }
208
209 if (fbvar.xres == 0 || fbvar.yres == 0)
210 return -1;
211
212 *w = fbvar.xres;
213 *h = fbvar.yres;
214 return 0;
215}
216
b9a19b44 217int osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata)
f641fccb 218{
f641fccb 219 int fb_id, overlay_id = -1, screen_id = -1;
220 char buff[64], screen_name[64];
b9a19b44 221 const char *fbname;
f641fccb 222 struct stat status;
b9a19b44 223 int fd, i, ret;
224 int w, h;
f641fccb 225 FILE *f;
226
5f4b1fd3 227 pdata->phys_w = pdata->phys_h = 0;
228
b9a19b44 229 fbname = get_fb_device();
f641fccb 230
b9a19b44 231 /* Figure out screen resolution, we need to know default resolution
232 * to report to SDL and for centering stuff.
f641fccb 233 * The only way to achieve this seems to be walking some sysfs files.. */
234 ret = stat(fbname, &status);
235 if (ret != 0) {
7b66578c 236 err_perror("can't stat %s", fbname);
b9a19b44 237 goto skip_screen;
f641fccb 238 }
239 fb_id = minor(status.st_rdev);
240
241 snprintf(buff, sizeof(buff), "/sys/class/graphics/fb%d/overlays", fb_id);
242 f = fopen(buff, "r");
243 if (f == NULL) {
7b66578c 244 err("can't open %s, skip screen detection", buff);
f641fccb 245 goto skip_screen;
246 }
247
248 ret = fscanf(f, "%d", &overlay_id);
249 fclose(f);
250 if (ret != 1) {
7b66578c 251 err("can't parse %s, skip screen detection", buff);
f641fccb 252 goto skip_screen;
253 }
254
255 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/overlay%d/manager", overlay_id);
256 ret = read_sysfs(buff, screen_name, sizeof(screen_name));
257 if (ret < 0) {
7b66578c 258 err("skip screen detection");
f641fccb 259 goto skip_screen;
260 }
261
262 for (i = 0; ; i++) {
263 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/name", i);
264 ret = read_sysfs(buff, buff, sizeof(buff));
265 if (ret < 0)
266 break;
267
268 if (strcmp(screen_name, buff) == 0) {
269 screen_id = i;
270 break;
271 }
272 }
273
274 if (screen_id < 0) {
7b66578c 275 err("could not find screen");
f641fccb 276 goto skip_screen;
277 }
278
279 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/timings", screen_id);
280 f = fopen(buff, "r");
281 if (f == NULL) {
7b66578c 282 err("can't open %s, skip screen detection", buff);
f641fccb 283 goto skip_screen;
284 }
285
b9a19b44 286 ret = fscanf(f, "%*d,%d/%*d/%*d/%*d,%d/%*d/%*d/%*d", &w, &h);
f641fccb 287 fclose(f);
288 if (ret != 2) {
7b66578c 289 err("can't parse %s (%d), skip screen detection", buff, ret);
f641fccb 290 goto skip_screen;
291 }
292
7b66578c 293 log("detected %dx%d '%s' (%d) screen attached to fb %d and overlay %d",
b9a19b44 294 w, h, screen_name, screen_id, fb_id, overlay_id);
295
5f4b1fd3 296 pdata->phys_w = w;
297 pdata->phys_h = h;
b9a19b44 298 return 0;
f641fccb 299
300skip_screen:
b9a19b44 301 /* attempt to extract this from FB then */
34edc246 302 ret = read_vscreeninfo(fbname, &pdata->phys_w, &pdata->phys_h);
303 if (ret != 0 && strcmp(fbname, "/dev/fb0") != 0) {
304 /* last resort */
305 ret = read_vscreeninfo("/dev/fb0", &pdata->phys_w, &pdata->phys_h);
b9a19b44 306 }
307
34edc246 308 if (ret != 0) {
b9a19b44 309 err("VSCREENINFO has nothing meaningful");
310 return -1;
311 }
312
b9a19b44 313 return 0;
314}
315
316static int osdl_setup_omap_layer(struct SDL_PrivateVideoData *pdata,
0c7caf2b 317 const char *fbname, int width, int height, int bpp, int *buffer_count)
b9a19b44 318{
319 int x = 0, y = 0, w = width, h = height; /* layer size and pos */
320 int screen_w = w, screen_h = h;
f151701d 321 int tmp_w, tmp_h;
b9a19b44 322 const char *tmp;
f11c56a6 323 int retval = -1;
b9a19b44 324 int ret, fd;
325
5f4b1fd3 326 pdata->layer_x = pdata->layer_y = pdata->layer_w = pdata->layer_h = 0;
327
328 if (pdata->phys_w != 0)
329 screen_w = pdata->phys_w;
330 if (pdata->phys_h != 0)
331 screen_h = pdata->phys_h;
b9a19b44 332
333 fd = open(fbname, O_RDWR);
334 if (fd == -1) {
335 err_perror("open %s", fbname);
336 return -1;
337 }
338
339 /* FIXME: assuming layer doesn't change here */
79b5de04 340 if (pdata->layer_state == NULL) {
090bcb85 341 struct omapfb_state *slayer;
b9a19b44 342 slayer = calloc(1, sizeof(*slayer));
343 if (slayer == NULL)
f11c56a6 344 goto out;
b9a19b44 345
090bcb85 346 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &slayer->pi_old);
b9a19b44 347 if (ret != 0) {
348 err_perror("QUERY_PLANE");
f11c56a6 349 goto out;
b9a19b44 350 }
351
090bcb85 352 ret = ioctl(fd, OMAPFB_QUERY_MEM, &slayer->mi_old);
b9a19b44 353 if (ret != 0) {
354 err_perror("QUERY_MEM");
f11c56a6 355 goto out;
b9a19b44 356 }
357
79b5de04 358 pdata->layer_state = slayer;
b9a19b44 359 }
360
f641fccb 361 tmp = getenv("SDL_OMAP_LAYER_SIZE");
362 if (tmp != NULL) {
21e209de 363 if (strcasecmp(tmp, "fullscreen") == 0) {
f641fccb 364 w = screen_w, h = screen_h;
21e209de
DA
365 }
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);
369 }
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' */
374 if (!w || !h) {
375 w = (int)(factor * width), h = (int)(factor * height);
376 }
377 }
378 else if (sscanf(tmp, "%dx%d", &tmp_w, &tmp_h) == 2) {
f151701d 379 w = tmp_w, h = tmp_h;
21e209de
DA
380 }
381 else {
7b66578c 382 err("layer size specified incorrectly, "
383 "should be like 800x480");
21e209de 384 }
f641fccb 385 }
f11c56a6 386
387 /* the layer can't be set larger than screen */
388 tmp_w = w, tmp_h = h;
389 if (w > screen_w)
390 w = screen_w;
391 if (h > screen_h)
392 h = screen_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);
f641fccb 395
396 x = screen_w / 2 - w / 2;
397 y = screen_h / 2 - h / 2;
79b5de04 398 ret = osdl_setup_omapfb(pdata->layer_state, fd, 0, x, y, w, h,
0c7caf2b 399 width * height * ((bpp + 7) / 8), buffer_count);
5f4b1fd3 400 if (ret == 0) {
401 pdata->layer_x = x;
402 pdata->layer_y = y;
403 pdata->layer_w = w;
404 pdata->layer_h = h;
405 }
f641fccb 406
f11c56a6 407 retval = ret;
408out:
409 close(fd);
410 return retval;
f641fccb 411}
412
455c8c43 413void *osdl_video_set_mode(struct SDL_PrivateVideoData *pdata,
414 int border_l, int border_r, int border_t, int border_b,
090bcb85 415 int width, int height, int bpp, int *doublebuf,
416 const char *wm_title)
f641fccb 417{
0c7caf2b 418 int buffers_try, buffers_set;
f641fccb 419 const char *fbname;
455c8c43 420 void *result;
f641fccb 421 int ret;
422
b9a19b44 423 fbname = get_fb_device();
f641fccb 424
425 if (pdata->fbdev != NULL) {
426 vout_fbdev_finish(pdata->fbdev);
427 pdata->fbdev = NULL;
428 }
429
0c7caf2b 430 buffers_try = buffers_set = 1;
431 if (*doublebuf)
432 /* actually try tripple buffering for reduced chance of tearing */
433 buffers_try = buffers_set = 3;
434
435 ret = osdl_setup_omap_layer(pdata, fbname, width, height, bpp, &buffers_set);
f641fccb 436 if (ret < 0)
0c7caf2b 437 goto fail;
f641fccb 438
0c7caf2b 439 pdata->fbdev = vout_fbdev_init(fbname, &width, &height, bpp, buffers_set);
f641fccb 440 if (pdata->fbdev == NULL)
0c7caf2b 441 goto fail;
455c8c43 442
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,
0c7caf2b 447 border_l, border_r, border_t, border_b, buffers_set);
455c8c43 448 }
449 else {
450 result = osdl_video_flip(pdata);
451 }
0c7caf2b 452 if (result == NULL)
453 goto fail;
f641fccb 454
f9b3f440 455 if (!pdata->xenv_up) {
090bcb85 456 int xenv_flags = XENV_CAP_KEYS | XENV_CAP_MOUSE;
457 ret = xenv_init(&xenv_flags, wm_title);
e1837b2c 458 if (ret == 0) {
f9b3f440 459 pdata->xenv_up = 1;
090bcb85 460 pdata->xenv_mouse = (xenv_flags & XENV_CAP_MOUSE) ? 1 : 0;
e1837b2c 461 }
f641fccb 462 }
463
79b5de04 464 ret = osdl_setup_omapfb_enable(pdata->layer_state,
3af567be 465 vout_fbdev_get_fd(pdata->fbdev), 1);
466 if (ret != 0) {
467 err("layer enable failed");
468 goto fail;
469 }
470
0c7caf2b 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;
475 }
476
455c8c43 477 return result;
0c7caf2b 478
479fail:
480 osdl_video_finish(pdata);
481 return NULL;
f641fccb 482}
483
484void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
485{
52a1749b 486 void *ret;
487
f641fccb 488 if (pdata->fbdev == NULL)
489 return NULL;
490
52a1749b 491 ret = vout_fbdev_flip(pdata->fbdev);
492
5f4b1fd3 493 if (pdata->cfg_force_vsync)
f641fccb 494 vout_fbdev_wait_vsync(pdata->fbdev);
495
52a1749b 496 return ret;
f641fccb 497}
498
5cc6dcb7 499void *osdl_video_get_active_buffer(struct SDL_PrivateVideoData *pdata)
500{
501 if (pdata->fbdev == NULL)
502 return NULL;
503
504 return vout_fbdev_get_active_mem(pdata->fbdev);
505}
506
090bcb85 507int osdl_video_pause(struct SDL_PrivateVideoData *pdata, int is_pause)
508{
79b5de04 509 struct omapfb_state *state = pdata->layer_state;
090bcb85 510 struct omapfb_plane_info pi;
511 struct omapfb_mem_info mi;
512 int enabled;
513 int fd = -1;
514 int ret;
515
516 if (pdata->fbdev != NULL)
517 fd = vout_fbdev_get_fd(pdata->fbdev);
518 if (fd == -1) {
519 err("bad fd %d", fd);
520 return -1;
521 }
522 if (state == NULL) {
523 err("missing layer state\n");
524 return -1;
525 }
526
527 if (is_pause) {
528 ret = vout_fbdev_save(pdata->fbdev);
529 if (ret != 0)
530 return ret;
531 pi = state->pi_old;
532 mi = state->mi_old;
533 enabled = pi.enabled;
534 } else {
535 pi = state->pi;
536 mi = state->mi;
537 enabled = 1;
538 }
539 pi.enabled = 0;
540 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
541 if (ret != 0) {
542 err_perror("SETUP_PLANE");
543 return -1;
544 }
545
546 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
547 if (ret != 0)
548 err_perror("SETUP_MEM");
549
550 if (!is_pause) {
551 ret = vout_fbdev_restore(pdata->fbdev);
552 if (ret != 0) {
553 err("fbdev_restore failed\n");
554 return ret;
555 }
556 }
557
558 if (enabled) {
559 pi.enabled = 1;
560 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
561 if (ret != 0) {
562 err_perror("SETUP_PLANE");
563 return -1;
564 }
565 }
566
567 return 0;
568}
569
ee7e6b2d 570int osdl_video_get_window(void **display, int *screen, void **window)
571{
572 return xenv_get_window(display, screen, window);
573}
574
f641fccb 575void osdl_video_finish(struct SDL_PrivateVideoData *pdata)
576{
577 static const char *fbname;
578
b9a19b44 579 fbname = get_fb_device();
f641fccb 580 if (pdata->fbdev != NULL) {
581 vout_fbdev_finish(pdata->fbdev);
582 pdata->fbdev = NULL;
583 }
584
585 /* restore the OMAP layer */
79b5de04 586 if (pdata->layer_state != NULL) {
587 struct omapfb_state *slayer = pdata->layer_state;
f641fccb 588 int fd;
589
79b5de04 590 if (slayer->tv_layer)
591 switch_tv_layer(slayer, 0);
592
f641fccb 593 fd = open(fbname, O_RDWR);
594 if (fd != -1) {
090bcb85 595 int enabled = slayer->pi_old.enabled;
f641fccb 596
597 /* be sure to disable while setting up */
090bcb85 598 slayer->pi_old.enabled = 0;
599 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
600 ioctl(fd, OMAPFB_SETUP_MEM, &slayer->mi_old);
f641fccb 601 if (enabled) {
090bcb85 602 slayer->pi_old.enabled = enabled;
603 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi_old);
f641fccb 604 }
605 close(fd);
606 }
607 free(slayer);
79b5de04 608 pdata->layer_state = NULL;
f641fccb 609 }
610
f9b3f440 611 if (pdata->xenv_up) {
612 xenv_finish();
613 pdata->xenv_up = 0;
f641fccb 614 }
615}
616