9379cabf89dcdff3c81ae1c41c8d24fa7a4c344f
[sdl_omap.git] / src / video / omapdss / osdl_video.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2012
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
20 #include "osdl.h"
21 #include "omapfb.h"
22 #include "linux/fbdev.h"
23 #include "linux/xenv.h"
24
25 struct omapfb_saved_layer {
26         struct omapfb_plane_info pi;
27         struct omapfb_mem_info mi;
28 };
29
30 static const char *get_fb_device(void)
31 {
32         const char *fbname = getenv("SDL_FBDEV");
33         if (fbname == NULL)
34                 fbname = "/dev/fb1";
35
36         return fbname;
37 }
38
39 static int osdl_setup_omapfb(int fd, int enabled, int x, int y, int w, int h,
40         int mem, int *buffer_count)
41 {
42         struct omapfb_plane_info pi;
43         struct omapfb_mem_info mi;
44         int mem_blocks = *buffer_count;
45         int ret;
46
47         memset(&pi, 0, sizeof(pi));
48         memset(&mi, 0, sizeof(mi));
49
50         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
51         if (ret != 0) {
52                 err_perror("QUERY_PLANE");
53                 return -1;
54         }
55
56         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
57         if (ret != 0) {
58                 err_perror("QUERY_MEM");
59                 return -1;
60         }
61
62         /* must disable when changing stuff */
63         if (pi.enabled) {
64                 pi.enabled = 0;
65                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
66                 if (ret != 0)
67                         err_perror("SETUP_PLANE");
68         }
69
70         if (mi.size < mem * mem_blocks) {
71                 for (; mem_blocks > 0; mem_blocks--) {
72                         mi.size = mem * mem_blocks;
73                         ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
74                         if (ret == 0)
75                                 break;
76                 }
77                 if (ret != 0 || mem_blocks <= 0) {
78                         err("failed to allocate at least %d bytes of vram:\n", mem);
79                         err_perror("SETUP_MEM");
80                         return -1;
81                 }
82         }
83         *buffer_count = mem_blocks;
84
85         pi.pos_x = x;
86         pi.pos_y = y;
87         pi.out_width = w;
88         pi.out_height = h;
89         pi.enabled = enabled;
90
91         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
92         if (ret != 0) {
93                 err_perror("SETUP_PLANE");
94                 err("(%d %d %d %d)\n", x, y, w, h);
95                 return -1;
96         }
97
98         return 0;
99 }
100
101 static int read_sysfs(const char *fname, char *buff, size_t size)
102 {
103         FILE *f;
104         int ret;
105
106         f = fopen(fname, "r");
107         if (f == NULL) {
108                 err_perror("open %s: ", fname);
109                 return -1;
110         }
111
112         ret = fread(buff, 1, size - 1, f);
113         fclose(f);
114         if (ret <= 0) {
115                 err_perror("read %s: ", fname);
116                 return -1;
117         }
118
119         buff[ret] = 0;
120         for (ret--; ret >= 0 && isspace(buff[ret]); ret--)
121                 buff[ret] = 0;
122
123         return 0;
124 }
125
126 int read_vscreeninfo(const char *fbname, int *w, int *h)
127 {
128         struct fb_var_screeninfo fbvar;
129         int ret, fd;
130
131         fd = open(fbname, O_RDWR);
132         if (fd == -1) {
133                 err_perror("open %s", fbname);
134                 return -1;
135         }
136
137         ret = ioctl(fd, FBIOGET_VSCREENINFO, &fbvar);
138         close(fd);
139
140         if (ret == -1) {
141                 err_perror("ioctl %s", fbname);
142                 return -1;
143         }
144
145         if (fbvar.xres == 0 || fbvar.yres == 0)
146                 return -1;
147
148         *w = fbvar.xres;
149         *h = fbvar.yres;
150         return 0;
151 }
152
153 int osdl_video_detect_screen(struct SDL_PrivateVideoData *pdata)
154 {
155         int fb_id, overlay_id = -1, screen_id = -1;
156         char buff[64], screen_name[64];
157         const char *fbname;
158         struct stat status;
159         int fd, i, ret;
160         int w, h;
161         FILE *f;
162
163         pdata->phys_w = pdata->phys_h = 0;
164
165         fbname = get_fb_device();
166
167         /* Figure out screen resolution, we need to know default resolution
168          * to report to SDL and for centering stuff.
169          * The only way to achieve this seems to be walking some sysfs files.. */
170         ret = stat(fbname, &status);
171         if (ret != 0) {
172                 err_perror("can't stat %s", fbname);
173                 goto skip_screen;
174         }
175         fb_id = minor(status.st_rdev);
176
177         snprintf(buff, sizeof(buff), "/sys/class/graphics/fb%d/overlays", fb_id);
178         f = fopen(buff, "r");
179         if (f == NULL) {
180                 err("can't open %s, skip screen detection", buff);
181                 goto skip_screen;
182         }
183
184         ret = fscanf(f, "%d", &overlay_id);
185         fclose(f);
186         if (ret != 1) {
187                 err("can't parse %s, skip screen detection", buff);
188                 goto skip_screen;
189         }
190
191         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/overlay%d/manager", overlay_id);
192         ret = read_sysfs(buff, screen_name, sizeof(screen_name));
193         if (ret < 0) {
194                 err("skip screen detection");
195                 goto skip_screen;
196         }
197
198         for (i = 0; ; i++) {
199                 snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/name", i);
200                 ret = read_sysfs(buff, buff, sizeof(buff));
201                 if (ret < 0)
202                         break;
203
204                 if (strcmp(screen_name, buff) == 0) {
205                         screen_id = i;
206                         break;
207                 }
208         }
209
210         if (screen_id < 0) {
211                 err("could not find screen");
212                 goto skip_screen;
213         }
214
215         snprintf(buff, sizeof(buff), "/sys/devices/platform/omapdss/display%d/timings", screen_id);
216         f = fopen(buff, "r");
217         if (f == NULL) {
218                 err("can't open %s, skip screen detection", buff);
219                 goto skip_screen;
220         }
221
222         ret = fscanf(f, "%*d,%d/%*d/%*d/%*d,%d/%*d/%*d/%*d", &w, &h);
223         fclose(f);
224         if (ret != 2) {
225                 err("can't parse %s (%d), skip screen detection", buff, ret);
226                 goto skip_screen;
227         }
228
229         log("detected %dx%d '%s' (%d) screen attached to fb %d and overlay %d",
230                 w, h, screen_name, screen_id, fb_id, overlay_id);
231
232         pdata->phys_w = w;
233         pdata->phys_h = h;
234         return 0;
235
236 skip_screen:
237         /* attempt to extract this from FB then */
238         ret = read_vscreeninfo(fbname, &pdata->phys_w, &pdata->phys_h);
239         if (ret != 0 && strcmp(fbname, "/dev/fb0") != 0) {
240                 /* last resort */
241                 ret = read_vscreeninfo("/dev/fb0", &pdata->phys_w, &pdata->phys_h);
242         }
243
244         if (ret != 0) {
245                 err("VSCREENINFO has nothing meaningful");
246                 return -1;
247         }
248
249         return 0;
250 }
251
252 static int osdl_setup_omap_layer(struct SDL_PrivateVideoData *pdata,
253                 const char *fbname, int width, int height, int bpp, int *buffer_count)
254 {
255         int x = 0, y = 0, w = width, h = height; /* layer size and pos */
256         int screen_w = w, screen_h = h;
257         int tmp_w, tmp_h;
258         const char *tmp;
259         int retval = -1;
260         int ret, fd;
261
262         pdata->layer_x = pdata->layer_y = pdata->layer_w = pdata->layer_h = 0;
263
264         if (pdata->phys_w != 0)
265                 screen_w = pdata->phys_w;
266         if (pdata->phys_h != 0)
267                 screen_h = pdata->phys_h;
268
269         fd = open(fbname, O_RDWR);
270         if (fd == -1) {
271                 err_perror("open %s", fbname);
272                 return -1;
273         }
274
275         /* FIXME: assuming layer doesn't change here */
276         if (pdata->saved_layer == NULL) {
277                 struct omapfb_saved_layer *slayer;
278                 slayer = calloc(1, sizeof(*slayer));
279                 if (slayer == NULL)
280                         goto out;
281
282                 ret = ioctl(fd, OMAPFB_QUERY_PLANE, &slayer->pi);
283                 if (ret != 0) {
284                         err_perror("QUERY_PLANE");
285                         goto out;
286                 }
287
288                 ret = ioctl(fd, OMAPFB_QUERY_MEM, &slayer->mi);
289                 if (ret != 0) {
290                         err_perror("QUERY_MEM");
291                         goto out;
292                 }
293
294                 pdata->saved_layer = slayer;
295         }
296
297         tmp = getenv("SDL_OMAP_LAYER_SIZE");
298         if (tmp != NULL) {
299                 if (strcasecmp(tmp, "fullscreen") == 0)
300                         w = screen_w, h = screen_h;
301                 else if (sscanf(tmp, "%dx%d", &tmp_w, &tmp_h) == 2)
302                         w = tmp_w, h = tmp_h;
303                 else
304                         err("layer size specified incorrectly, "
305                                 "should be like 800x480");
306         }
307
308         /* the layer can't be set larger than screen */
309         tmp_w = w, tmp_h = h;
310         if (w > screen_w)
311                 w = screen_w;
312         if (h > screen_h)
313                 h = screen_h;
314         if (w != tmp_w || h != tmp_h)
315                 log("layer resized %dx%d -> %dx%d to fit screen", tmp_w, tmp_h, w, h);
316
317         x = screen_w / 2 - w / 2;
318         y = screen_h / 2 - h / 2;
319         ret = osdl_setup_omapfb(fd, 1, x, y, w, h,
320                                 width * height * ((bpp + 7) / 8), buffer_count);
321         if (ret == 0) {
322                 pdata->layer_x = x;
323                 pdata->layer_y = y;
324                 pdata->layer_w = w;
325                 pdata->layer_h = h;
326         }
327
328         retval = ret;
329 out:
330         close(fd);
331         return retval;
332 }
333
334 void *osdl_video_set_mode(struct SDL_PrivateVideoData *pdata,
335                           int border_l, int border_r, int border_t, int border_b,
336                           int width, int height, int bpp, int *doublebuf)
337 {
338         int buffers_try, buffers_set;
339         const char *fbname;
340         void *result;
341         int ret;
342
343         fbname = get_fb_device();
344
345         if (pdata->fbdev != NULL) {
346                 vout_fbdev_finish(pdata->fbdev);
347                 pdata->fbdev = NULL;
348         }
349
350         buffers_try = buffers_set = 1;
351         if (*doublebuf)
352                 /* actually try tripple buffering for reduced chance of tearing */
353                 buffers_try = buffers_set = 3;
354
355         ret = osdl_setup_omap_layer(pdata, fbname, width, height, bpp, &buffers_set);
356         if (ret < 0)
357                 goto fail;
358
359         pdata->fbdev = vout_fbdev_init(fbname, &width, &height, bpp, buffers_set);
360         if (pdata->fbdev == NULL)
361                 goto fail;
362
363         if (border_l | border_r | border_t | border_b) {
364                 width -= border_l + border_r;
365                 height -= border_t + border_b;
366                 result = vout_fbdev_resize(pdata->fbdev, width, height, bpp,
367                                 border_l, border_r, border_t, border_b, buffers_set);
368         }
369         else {
370                 result = osdl_video_flip(pdata);
371         }
372         if (result == NULL)
373                 goto fail;
374
375         if (!pdata->xenv_up) {
376                 int xenv_mouse = 0;
377                 ret = xenv_init(&xenv_mouse);
378                 if (ret == 0) {
379                         pdata->xenv_up = 1;
380                         pdata->xenv_mouse = xenv_mouse;
381                 }
382         }
383
384         if (buffers_try != buffers_set) {
385                 log("only %d/%d buffers available, expect tearing\n",
386                         buffers_set, buffers_try);
387                 *doublebuf = buffers_set > 1;
388         }
389
390         return result;
391
392 fail:
393         osdl_video_finish(pdata);
394         return NULL;
395 }
396
397 void *osdl_video_flip(struct SDL_PrivateVideoData *pdata)
398 {
399         void *ret;
400
401         if (pdata->fbdev == NULL)
402                 return NULL;
403
404         ret = vout_fbdev_flip(pdata->fbdev);
405
406         if (pdata->cfg_force_vsync)
407                 vout_fbdev_wait_vsync(pdata->fbdev);
408
409         return ret;
410 }
411
412 void osdl_video_finish(struct SDL_PrivateVideoData *pdata)
413 {
414         static const char *fbname;
415
416         fbname = get_fb_device();
417         if (pdata->fbdev != NULL) {
418                 vout_fbdev_finish(pdata->fbdev);
419                 pdata->fbdev = NULL;
420         }
421
422         /* restore the OMAP layer */
423         if (pdata->saved_layer != NULL) {
424                 struct omapfb_saved_layer *slayer = pdata->saved_layer;
425                 int fd;
426
427                 fd = open(fbname, O_RDWR);
428                 if (fd != -1) {
429                         int enabled = slayer->pi.enabled;
430
431                         /* be sure to disable while setting up */
432                         slayer->pi.enabled = 0;
433                         ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi);
434                         ioctl(fd, OMAPFB_SETUP_MEM, &slayer->mi);
435                         if (enabled) {
436                                 slayer->pi.enabled = enabled;
437                                 ioctl(fd, OMAPFB_SETUP_PLANE, &slayer->pi);
438                         }
439                         close(fd);
440                 }
441                 free(slayer);
442                 pdata->saved_layer = NULL;
443         }
444
445         if (pdata->xenv_up) {
446                 xenv_finish();
447                 pdata->xenv_up = 0;
448         }
449 }
450