2 * video driver for framebuffer device
3 * copyright (C) 2001 Szabolcs Berecz <szabi@inf.elte.hu>
5 * Some idea and code borrowed from Chris Lawrence's ppmtofb-0.27
6 * Some fixes and small improvements by Joey Parrish <joey@nicewarrior.org>
8 * This file is part of MPlayer.
10 * MPlayer is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * MPlayer is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <sys/ioctl.h>
39 #include "video_out.h"
40 #include "video_out_internal.h"
41 #include "fastmemcpy.h"
45 #include "vosub_vidix.h"
49 #include "libavutil/common.h"
51 static const vo_info_t info
= {
54 "Szabolcs Berecz <szabi@inf.elte.hu>",
61 /* Name of VIDIX driver */
62 static const char *vidix_name
= NULL
;
63 static vidix_grkey_t gr_key
;
65 static signed int pre_init_err
= -2;
66 /******************************
68 ******************************/
70 static range_t
*monitor_hfreq
= NULL
;
71 static range_t
*monitor_vfreq
= NULL
;
72 static range_t
*monitor_dotclock
= NULL
;
76 uint32_t xres
, yres
, vxres
, vyres
, depth
;
77 uint32_t pixclock
, left
, right
, upper
, lower
, hslen
, vslen
;
82 #define MAX_NR_TOKEN 16
84 #define MAX_LINE_LEN 1000
89 static int validate_mode(fb_mode_t
*m
)
92 mp_msg(MSGT_VO
, MSGL_V
, "needs geometry ");
96 mp_msg(MSGT_VO
, MSGL_V
, "needs timings ");
103 static int line_num
= 0;
105 static char *token
[MAX_NR_TOKEN
];
107 static int get_token(int num
)
109 static int read_nextline
= 1;
114 if (num
>= MAX_NR_TOKEN
) {
115 mp_msg(MSGT_VO
, MSGL_V
, "get_token(): max >= MAX_NR_TOKEN!\n");
120 if (!fgets(line
, MAX_LINE_LEN
, fp
))
126 for (i
= 0; i
< num
; i
++) {
127 while (isspace(line
[line_pos
]))
129 if (line
[line_pos
] == '\0' || line
[line_pos
] == '#') {
133 token
[i
] = line
+ line_pos
;
135 if (c
== '"' || c
== '\'') {
137 while (line
[++line_pos
] != c
&& line
[line_pos
])
141 line
[line_pos
] = ' ';
143 for (/* NOTHING */; !isspace(line
[line_pos
]) &&
144 line
[line_pos
]; line_pos
++)
147 if (!line
[line_pos
]) {
153 line
[line_pos
++] = '\0';
163 static fb_mode_t
*fb_modes
= NULL
;
164 static int nr_modes
= 0;
166 static int parse_fbmode_cfg(char *cfgfile
)
168 #define CHECK_IN_MODE_DEF\
170 mp_msg(MSGT_VO, MSGL_V, "'needs 'mode' first");\
171 goto err_out_print_linenum;\
173 fb_mode_t
*mode
= NULL
;
174 char *endptr
; // strtoul()...
178 /* If called more than once, reuse parsed data */
182 mp_msg(MSGT_VO
, MSGL_V
, "Reading %s: ", cfgfile
);
184 if ((fp
= fopen(cfgfile
, "r")) == NULL
) {
185 mp_msg(MSGT_VO
, MSGL_V
, "can't open '%s': %s\n", cfgfile
, strerror(errno
));
189 if ((line
= malloc(MAX_LINE_LEN
+ 1)) == NULL
) {
190 mp_msg(MSGT_VO
, MSGL_V
, "can't get memory for 'line': %s\n", strerror(errno
));
195 * check if the cfgfile starts with 'mode'
197 while ((tmp
= get_token(1)) == RET_EOL
)
201 if (!strcmp(token
[0], "mode"))
203 goto err_out_parse_error
;
205 while ((tmp
= get_token(1)) != RET_EOF
) {
208 if (!strcmp(token
[0], "mode")) {
210 mp_msg(MSGT_VO
, MSGL_V
, "'endmode' required");
211 goto err_out_print_linenum
;
213 if (!validate_mode(mode
))
214 goto err_out_not_valid
;
217 realloc(fb_modes
, sizeof(fb_mode_t
) * (nr_modes
+ 1)))) {
218 mp_msg(MSGT_VO
, MSGL_V
, "can't realloc 'fb_modes' (nr_modes = %d):"
219 " %s\n", nr_modes
, strerror(errno
));
222 mode
= fb_modes
+ nr_modes
;
224 memset(mode
, 0, sizeof(fb_mode_t
));
226 if (get_token(1) < 0)
227 goto err_out_parse_error
;
228 for (i
= 0; i
< nr_modes
- 1; i
++) {
229 if (!strcmp(token
[0], fb_modes
[i
].name
)) {
230 mp_msg(MSGT_VO
, MSGL_V
, "mode name '%s' isn't unique", token
[0]);
231 goto err_out_print_linenum
;
234 if (!(mode
->name
= strdup(token
[0]))) {
235 mp_msg(MSGT_VO
, MSGL_V
, "can't strdup -> 'name': %s\n", strerror(errno
));
239 } else if (!strcmp(token
[0], "geometry")) {
241 if (get_token(5) < 0)
242 goto err_out_parse_error
;
243 mode
->xres
= strtoul(token
[0], &endptr
, 0);
245 goto err_out_parse_error
;
246 mode
->yres
= strtoul(token
[1], &endptr
, 0);
248 goto err_out_parse_error
;
249 mode
->vxres
= strtoul(token
[2], &endptr
, 0);
251 goto err_out_parse_error
;
252 mode
->vyres
= strtoul(token
[3], &endptr
, 0);
254 goto err_out_parse_error
;
255 mode
->depth
= strtoul(token
[4], &endptr
, 0);
257 goto err_out_parse_error
;
258 } else if (!strcmp(token
[0], "timings")) {
260 if (get_token(7) < 0)
261 goto err_out_parse_error
;
262 mode
->pixclock
= strtoul(token
[0], &endptr
, 0);
264 goto err_out_parse_error
;
265 mode
->left
= strtoul(token
[1], &endptr
, 0);
267 goto err_out_parse_error
;
268 mode
->right
= strtoul(token
[2], &endptr
, 0);
270 goto err_out_parse_error
;
271 mode
->upper
= strtoul(token
[3], &endptr
, 0);
273 goto err_out_parse_error
;
274 mode
->lower
= strtoul(token
[4], &endptr
, 0);
276 goto err_out_parse_error
;
277 mode
->hslen
= strtoul(token
[5], &endptr
, 0);
279 goto err_out_parse_error
;
280 mode
->vslen
= strtoul(token
[6], &endptr
, 0);
282 goto err_out_parse_error
;
283 } else if (!strcmp(token
[0], "endmode")) {
286 } else if (!strcmp(token
[0], "accel")) {
288 if (get_token(1) < 0)
289 goto err_out_parse_error
;
291 * it's only used for text acceleration
292 * so we just ignore it.
294 } else if (!strcmp(token
[0], "hsync")) {
296 if (get_token(1) < 0)
297 goto err_out_parse_error
;
298 if (!strcmp(token
[0], "low"))
299 mode
->sync
&= ~FB_SYNC_HOR_HIGH_ACT
;
300 else if (!strcmp(token
[0], "high"))
301 mode
->sync
|= FB_SYNC_HOR_HIGH_ACT
;
303 goto err_out_parse_error
;
304 } else if (!strcmp(token
[0], "vsync")) {
306 if (get_token(1) < 0)
307 goto err_out_parse_error
;
308 if (!strcmp(token
[0], "low"))
309 mode
->sync
&= ~FB_SYNC_VERT_HIGH_ACT
;
310 else if (!strcmp(token
[0], "high"))
311 mode
->sync
|= FB_SYNC_VERT_HIGH_ACT
;
313 goto err_out_parse_error
;
314 } else if (!strcmp(token
[0], "csync")) {
316 if (get_token(1) < 0)
317 goto err_out_parse_error
;
318 if (!strcmp(token
[0], "low"))
319 mode
->sync
&= ~FB_SYNC_COMP_HIGH_ACT
;
320 else if (!strcmp(token
[0], "high"))
321 mode
->sync
|= FB_SYNC_COMP_HIGH_ACT
;
323 goto err_out_parse_error
;
324 } else if (!strcmp(token
[0], "extsync")) {
326 if (get_token(1) < 0)
327 goto err_out_parse_error
;
328 if (!strcmp(token
[0], "false"))
329 mode
->sync
&= ~FB_SYNC_EXT
;
330 else if (!strcmp(token
[0], "true"))
331 mode
->sync
|= FB_SYNC_EXT
;
333 goto err_out_parse_error
;
334 } else if (!strcmp(token
[0], "laced")) {
336 if (get_token(1) < 0)
337 goto err_out_parse_error
;
338 if (!strcmp(token
[0], "false"))
339 mode
->vmode
= FB_VMODE_NONINTERLACED
;
340 else if (!strcmp(token
[0], "true"))
341 mode
->vmode
= FB_VMODE_INTERLACED
;
343 goto err_out_parse_error
;
344 } else if (!strcmp(token
[0], "double")) {
346 if (get_token(1) < 0)
347 goto err_out_parse_error
;
348 if (!strcmp(token
[0], "false"))
350 else if (!strcmp(token
[0], "true"))
351 mode
->vmode
= FB_VMODE_DOUBLE
;
353 goto err_out_parse_error
;
355 goto err_out_parse_error
;
357 if (!validate_mode(mode
))
358 goto err_out_not_valid
;
360 mp_msg(MSGT_VO
, MSGL_V
, "%d modes\n", nr_modes
);
365 mp_msg(MSGT_VO
, MSGL_V
, "parse error");
366 err_out_print_linenum
:
367 mp_msg(MSGT_VO
, MSGL_V
, " at line %d\n", line_num
);
378 mp_msg(MSGT_VO
, MSGL_V
, "previous mode is not correct");
379 goto err_out_print_linenum
;
382 static fb_mode_t
*find_mode_by_name(char *name
)
386 for (i
= 0; i
< nr_modes
; i
++)
387 if (!strcmp(name
, fb_modes
[i
].name
))
392 static float dcf(fb_mode_t
*m
) //driving clock frequency
394 return 1e12f
/ m
->pixclock
;
397 static float hsf(fb_mode_t
*m
) //horizontal scan frequency
399 int htotal
= m
->left
+ m
->xres
+ m
->right
+ m
->hslen
;
400 return dcf(m
) / htotal
;
403 static float vsf(fb_mode_t
*m
) //vertical scan frequency
405 int vtotal
= m
->upper
+ m
->yres
+ m
->lower
+ m
->vslen
;
406 return hsf(m
) / vtotal
;
410 static int mode_works(fb_mode_t
*m
, range_t
*hfreq
, range_t
*vfreq
,
418 mp_msg(MSGT_VO
, MSGL_DBG2
, "mode %dx%d:", m
->xres
, m
->yres
);
419 if (!in_range(hfreq
, h
)) {
421 mp_msg(MSGT_VO
, MSGL_DBG2
, " hsync out of range.");
423 if (!in_range(vfreq
, v
)) {
425 mp_msg(MSGT_VO
, MSGL_DBG2
, " vsync out of range.");
427 if (!in_range(dotclock
, d
)) {
429 mp_msg(MSGT_VO
, MSGL_DBG2
, " dotclock out of range.");
432 mp_msg(MSGT_VO
, MSGL_DBG2
, " hsync, vsync, dotclock ok.\n");
434 mp_msg(MSGT_VO
, MSGL_DBG2
, "\n");
439 static fb_mode_t
*find_best_mode(int xres
, int yres
, range_t
*hfreq
,
440 range_t
*vfreq
, range_t
*dotclock
)
443 fb_mode_t
*best
= fb_modes
;
446 mp_msg(MSGT_VO
, MSGL_DBG2
, "Searching for first working mode\n");
448 for (i
= 0; i
< nr_modes
; i
++, best
++)
449 if (mode_works(best
, hfreq
, vfreq
, dotclock
))
454 if (i
== nr_modes
- 1)
457 mp_msg(MSGT_VO
, MSGL_DBG2
, "First working mode: %dx%d\n", best
->xres
, best
->yres
);
458 mp_msg(MSGT_VO
, MSGL_DBG2
, "Searching for better modes\n");
460 for (curr
= best
+ 1; i
< nr_modes
- 1; i
++, curr
++) {
461 if (!mode_works(curr
, hfreq
, vfreq
, dotclock
))
464 if (best
->xres
< xres
|| best
->yres
< yres
) {
465 if (curr
->xres
> best
->xres
|| curr
->yres
> best
->yres
) {
466 mp_msg(MSGT_VO
, MSGL_DBG2
, "better than %dx%d, which is too small.\n",
467 best
->xres
, best
->yres
);
470 mp_msg(MSGT_VO
, MSGL_DBG2
, "too small.\n");
471 } else if (curr
->xres
== best
->xres
&& curr
->yres
== best
->yres
&&
472 vsf(curr
) > vsf(best
)) {
473 mp_msg(MSGT_VO
, MSGL_DBG2
, "faster screen refresh.\n");
475 } else if ((curr
->xres
<= best
->xres
&& curr
->yres
<= best
->yres
) &&
476 (curr
->xres
>= xres
&& curr
->yres
>= yres
)) {
477 mp_msg(MSGT_VO
, MSGL_DBG2
, "better than %dx%d, which is too large.\n",
478 best
->xres
, best
->yres
);
481 if (curr
->xres
< xres
|| curr
->yres
< yres
)
482 mp_msg(MSGT_VO
, MSGL_DBG2
, "too small.\n");
483 else if (curr
->xres
> best
->xres
|| curr
->yres
> best
->yres
)
484 mp_msg(MSGT_VO
, MSGL_DBG2
, "too large.\n");
486 mp_msg(MSGT_VO
, MSGL_DBG2
, "it's worse, don't know why.\n");
493 static void set_bpp(struct fb_var_screeninfo
*p
, int bpp
)
495 p
->bits_per_pixel
= FFALIGN(bpp
, 2);
496 p
->red
.msb_right
= p
->green
.msb_right
= p
->blue
.msb_right
= p
->transp
.msb_right
= 0;
497 p
->transp
.offset
= p
->transp
.length
= 0;
501 p
->transp
.offset
= 24;
502 p
->transp
.length
= 8;
534 static void fb_mode2fb_vinfo(fb_mode_t
*m
, struct fb_var_screeninfo
*v
)
538 v
->xres_virtual
= m
->vxres
;
539 v
->yres_virtual
= m
->vyres
;
540 set_bpp(v
, m
->depth
);
541 v
->pixclock
= m
->pixclock
;
542 v
->left_margin
= m
->left
;
543 v
->right_margin
= m
->right
;
544 v
->upper_margin
= m
->upper
;
545 v
->lower_margin
= m
->lower
;
546 v
->hsync_len
= m
->hslen
;
547 v
->vsync_len
= m
->vslen
;
553 /******************************
555 ******************************/
557 /* command line/config file options */
558 static char *fb_dev_name
= NULL
;
559 char *fb_mode_cfgfile
= NULL
;
560 char *fb_mode_name
= NULL
;
562 static fb_mode_t
*fb_mode
= NULL
;
564 /* vo_fbdev related variables */
565 static int fb_dev_fd
;
566 static int fb_tty_fd
= -1;
567 static size_t fb_size
;
568 static uint8_t *frame_buffer
;
569 static uint8_t *center
;
570 static struct fb_fix_screeninfo fb_finfo
;
571 static struct fb_var_screeninfo fb_orig_vinfo
;
572 static struct fb_var_screeninfo fb_vinfo
;
573 static unsigned short fb_ored
[256], fb_ogreen
[256], fb_oblue
[256];
574 static struct fb_cmap fb_oldcmap
= { 0, 256, fb_ored
, fb_ogreen
, fb_oblue
};
575 static int fb_cmap_changed
= 0;
576 static int fb_pixel_size
; // 32: 4 24: 3 16: 2 15: 2
577 static int fb_bpp
; // 32: 32 24: 24 16: 16 15: 15
578 static int fb_bpp_we_want
; // 32: 32 24: 24 16: 16 15: 15
579 static int fb_line_len
;
583 static void (*draw_alpha_p
)(int w
, int h
, unsigned char *src
,
584 unsigned char *srca
, int stride
,
585 unsigned char *dst
, int dstride
);
588 static int in_height
;
589 static int out_width
;
590 static int out_height
;
591 static int first_row
;
593 static uint32_t pixel_format
;
597 * Note: this function is completely cut'n'pasted from
598 * Chris Lawrence's code.
599 * (modified a bit to fit in my code...)
601 static struct fb_cmap
*make_directcolor_cmap(struct fb_var_screeninfo
*var
)
603 /* Hopefully any DIRECTCOLOR device will have a big enough palette
604 * to handle mapping the full color depth.
605 * e.g. 8 bpp -> 256 entry palette
607 * We could handle some sort of gamma here
609 int i
, cols
, rcols
, gcols
, bcols
;
610 uint16_t *red
, *green
, *blue
;
611 struct fb_cmap
*cmap
;
613 rcols
= 1 << var
->red
.length
;
614 gcols
= 1 << var
->green
.length
;
615 bcols
= 1 << var
->blue
.length
;
617 /* Make our palette the length of the deepest color */
618 cols
= FFMAX3(rcols
, gcols
, bcols
);
620 red
= malloc(cols
* sizeof(red
[0]));
622 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate red palette with %d entries.\n", cols
);
625 for (i
= 0; i
< rcols
; i
++)
626 red
[i
] = (65535 / (rcols
- 1)) * i
;
628 green
= malloc(cols
* sizeof(green
[0]));
630 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate green palette with %d entries.\n", cols
);
634 for (i
= 0; i
< gcols
; i
++)
635 green
[i
] = (65535 / (gcols
- 1)) * i
;
637 blue
= malloc(cols
* sizeof(blue
[0]));
639 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate blue palette with %d entries.\n", cols
);
644 for (i
= 0; i
< bcols
; i
++)
645 blue
[i
] = (65535 / (bcols
- 1)) * i
;
647 cmap
= malloc(sizeof(struct fb_cmap
));
649 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate color map\n");
667 static int fb_preinit(int reset
)
669 static int fb_preinit_done
= 0;
670 static int fb_works
= 0;
680 fb_dev_fd
= fb_tty_fd
= -1;
682 if (!fb_dev_name
&& !(fb_dev_name
= getenv("FRAMEBUFFER")))
683 fb_dev_name
= strdup("/dev/fb0");
684 mp_msg(MSGT_VO
, MSGL_V
, "using %s\n", fb_dev_name
);
686 if ((fb_dev_fd
= open(fb_dev_name
, O_RDWR
)) == -1) {
687 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't open %s: %s\n", fb_dev_name
, strerror(errno
));
690 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
691 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't get VSCREENINFO: %s\n", strerror(errno
));
694 fb_orig_vinfo
= fb_vinfo
;
696 if ((fb_tty_fd
= open("/dev/tty", O_RDWR
)) < 0) {
697 mp_msg(MSGT_VO
, MSGL_ERR
, "notice: Can't open /dev/tty: %s\n", strerror(errno
));
700 fb_bpp
= fb_vinfo
.bits_per_pixel
;
702 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+ fb_vinfo
.blue
.length
;
704 if (fb_bpp
== 8 && !vo_dbpp
) {
705 mp_msg(MSGT_VO
, MSGL_ERR
, "8 bpp output is not supported.\n");
710 if (vo_dbpp
!= 12 && vo_dbpp
!= 15 && vo_dbpp
!= 16
711 && vo_dbpp
!= 24 && vo_dbpp
!= 32) {
712 mp_msg(MSGT_VO
, MSGL_ERR
, "can't switch to %d bpp\n", vo_dbpp
);
718 if (!fb_mode_cfgfile
)
719 fb_mode_cfgfile
= strdup("/etc/fb.modes");
736 static void vt_set_textarea(int u
, int l
)
738 /* how can I determine the font height?
739 * just use 16 for now
741 int urow
= ((u
+ 15) / 16) + 1;
744 mp_msg(MSGT_VO
, MSGL_DBG2
, "vt_set_textarea(%d,%d): %d,%d\n", u
, l
, urow
, lrow
);
745 if (fb_tty_fd
>= 0) {
746 char modestring
[100];
747 snprintf(modestring
, sizeof(modestring
), "\33[%d;%dr\33[%d;%dH", urow
, lrow
, lrow
, 0);
748 write(fb_tty_fd
, modestring
, strlen(modestring
));
753 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
754 uint32_t d_height
, uint32_t flags
, char *title
,
757 struct fb_cmap
*cmap
;
758 int vm
= flags
& VOFLAG_MODESWITCHING
;
759 int zoom
= flags
& VOFLAG_SWSCALE
;
761 fs
= flags
& VOFLAG_FULLSCREEN
;
763 if (pre_init_err
== -2) {
764 mp_msg(MSGT_VO
, MSGL_ERR
, "Internal fatal error: config() was called before preinit()\n");
771 if (fb_mode_name
&& !vm
) {
772 mp_msg(MSGT_VO
, MSGL_ERR
, "-fbmode can only be used with -vm\n");
775 if (vm
&& parse_fbmode_cfg(fb_mode_cfgfile
) < 0)
777 if (d_width
&& (fs
|| vm
)) {
779 out_height
= d_height
;
786 pixel_format
= format
;
789 if (!(fb_mode
= find_mode_by_name(fb_mode_name
))) {
790 mp_msg(MSGT_VO
, MSGL_ERR
, "can't find requested video mode\n");
793 fb_mode2fb_vinfo(fb_mode
, &fb_vinfo
);
795 monitor_hfreq
= str2range(monitor_hfreq_str
);
796 monitor_vfreq
= str2range(monitor_vfreq_str
);
797 monitor_dotclock
= str2range(monitor_dotclock_str
);
798 if (!monitor_hfreq
|| !monitor_vfreq
|| !monitor_dotclock
) {
799 mp_msg(MSGT_VO
, MSGL_ERR
, "you have to specify the capabilities of"
803 if (!(fb_mode
= find_best_mode(out_width
, out_height
, monitor_hfreq
,
804 monitor_vfreq
, monitor_dotclock
))) {
805 mp_msg(MSGT_VO
, MSGL_ERR
, "can't find best video mode\n");
808 mp_msg(MSGT_VO
, MSGL_V
, "using mode %dx%d @ %.1fHz\n", fb_mode
->xres
,
809 fb_mode
->yres
, vsf(fb_mode
));
810 fb_mode2fb_vinfo(fb_mode
, &fb_vinfo
);
812 fb_bpp_we_want
= fb_bpp
;
813 set_bpp(&fb_vinfo
, fb_bpp
);
814 fb_vinfo
.xres_virtual
= fb_vinfo
.xres
;
815 fb_vinfo
.yres_virtual
= fb_vinfo
.yres
;
817 if (vo_doublebuffering
) {
818 fb_vinfo
.yres_virtual
<<= 1;
819 fb_vinfo
.yoffset
= 0;
820 fb_page
= 1; // start writing into the page we don't display
823 if (fb_tty_fd
>= 0 && ioctl(fb_tty_fd
, KDSETMODE
, KD_GRAPHICS
) < 0) {
824 mp_msg(MSGT_VO
, MSGL_V
, "Can't set graphics mode: %s\n", strerror(errno
));
829 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
))
830 // Intel drivers fail if we request a transparency channel
831 fb_vinfo
.transp
.length
= fb_vinfo
.transp
.offset
= 0;
832 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
)) {
833 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't put VSCREENINFO: %s\n", strerror(errno
));
834 if (fb_tty_fd
>= 0 && ioctl(fb_tty_fd
, KDSETMODE
, KD_TEXT
) < 0) {
835 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't restore text mode: %s\n", strerror(errno
));
840 fb_pixel_size
= fb_vinfo
.bits_per_pixel
/ 8;
841 fb_bpp
= fb_vinfo
.bits_per_pixel
;
843 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+ fb_vinfo
.blue
.length
;
844 if (fb_bpp_we_want
!= fb_bpp
)
845 mp_msg(MSGT_VO
, MSGL_WARN
, "requested %d bpp, got %d bpp!!!\n",
846 fb_bpp_we_want
, fb_bpp
);
850 draw_alpha_p
= vo_draw_alpha_rgb32
;
853 draw_alpha_p
= vo_draw_alpha_rgb24
;
856 draw_alpha_p
= vo_draw_alpha_rgb16
;
859 draw_alpha_p
= vo_draw_alpha_rgb15
;
862 draw_alpha_p
= vo_draw_alpha_rgb12
;
868 fb_xres
= fb_vinfo
.xres
;
869 fb_yres
= fb_vinfo
.yres
;
873 out_height
= fb_yres
;
875 if (out_width
< in_width
|| out_height
< in_height
) {
876 mp_msg(MSGT_VO
, MSGL_ERR
, "screensize is smaller than video size\n");
880 first_row
= (out_height
- in_height
) / 2;
881 last_row
= (out_height
+ in_height
) / 2;
883 if (ioctl(fb_dev_fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
884 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't get FSCREENINFO: %s\n", strerror(errno
));
888 if (fb_finfo
.type
!= FB_TYPE_PACKED_PIXELS
) {
889 mp_msg(MSGT_VO
, MSGL_ERR
, "type %d not supported\n", fb_finfo
.type
);
893 switch (fb_finfo
.visual
) {
894 case FB_VISUAL_TRUECOLOR
:
896 case FB_VISUAL_DIRECTCOLOR
:
897 mp_msg(MSGT_VO
, MSGL_V
, "creating cmap for directcolor\n");
898 if (ioctl(fb_dev_fd
, FBIOGETCMAP
, &fb_oldcmap
)) {
899 mp_msg(MSGT_VO
, MSGL_ERR
, "can't get cmap: %s\n",
903 if (!(cmap
= make_directcolor_cmap(&fb_vinfo
)))
905 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, cmap
)) {
906 mp_msg(MSGT_VO
, MSGL_ERR
, "can't put cmap: %s\n",
917 mp_msg(MSGT_VO
, MSGL_ERR
, "visual: %d not yet supported\n",
922 fb_line_len
= fb_finfo
.line_length
;
923 fb_size
= fb_finfo
.smem_len
;
924 if (vo_doublebuffering
&& fb_size
< 2 * fb_yres
* fb_line_len
)
926 mp_msg(MSGT_VO
, MSGL_WARN
, "framebuffer too small for double-buffering, disabling\n");
927 vo_doublebuffering
= 0;
933 unsigned image_width
, image_height
, x_offset
, y_offset
;
935 aspect_save_orig(width
, height
);
936 aspect_save_prescale(d_width
, d_height
);
937 aspect_save_screenres(fb_xres
, fb_yres
);
938 aspect(&image_width
, &image_height
, fs
? A_ZOOM
: A_NOZOOM
);
941 image_height
= height
;
944 if (fb_xres
> image_width
)
945 x_offset
= (fb_xres
- image_width
) / 2;
948 if (fb_yres
> image_height
)
949 y_offset
= (fb_yres
- image_height
) / 2;
953 if (vidix_init(width
, height
, x_offset
, y_offset
, image_width
,
954 image_height
, format
, fb_bpp
, fb_xres
, fb_yres
) != 0) {
955 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't initialize VIDIX driver\n");
960 mp_msg(MSGT_VO
, MSGL_V
, "Using VIDIX\n");
962 if (vidix_grkey_support()) {
963 vidix_grkey_get(&gr_key
);
964 gr_key
.key_op
= KEYS_PUT
;
965 if (!(vo_colorkey
& 0xff000000)) {
966 gr_key
.ckey
.op
= CKEY_TRUE
;
967 gr_key
.ckey
.red
= (vo_colorkey
& 0x00ff0000) >> 16;
968 gr_key
.ckey
.green
= (vo_colorkey
& 0x0000ff00) >> 8;
969 gr_key
.ckey
.blue
= vo_colorkey
& 0x000000ff;
971 gr_key
.ckey
.op
= CKEY_FALSE
;
972 vidix_grkey_set(&gr_key
);
977 int x_offset
= 0, y_offset
= 0;
978 geometry(&x_offset
, &y_offset
, &out_width
, &out_height
, fb_xres
, fb_yres
);
980 frame_buffer
= mmap(0, fb_size
, PROT_READ
| PROT_WRITE
,
981 MAP_SHARED
, fb_dev_fd
, 0);
982 if (frame_buffer
== (uint8_t *) -1) {
983 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't mmap %s: %s\n", fb_dev_name
, strerror(errno
));
987 center
= frame_buffer
+
988 ( (out_width
- in_width
) / 2 ) * fb_pixel_size
+
989 ( (out_height
- in_height
) / 2 ) * fb_line_len
+
990 x_offset
* fb_pixel_size
+ y_offset
* fb_line_len
+
991 fb_page
* fb_yres
* fb_line_len
;
993 mp_msg(MSGT_VO
, MSGL_DBG2
, "frame_buffer @ %p\n", frame_buffer
);
994 mp_msg(MSGT_VO
, MSGL_DBG2
, "center @ %p\n", center
);
995 mp_msg(MSGT_VO
, MSGL_V
, "pixel per line: %d\n", fb_line_len
/ fb_pixel_size
);
998 int clear_size
= fb_line_len
* fb_yres
;
999 if (vo_doublebuffering
)
1001 memset(frame_buffer
, 0, clear_size
);
1005 vt_set_textarea(last_row
, fb_yres
);
1010 static int query_format(uint32_t format
)
1016 return vidix_query_fourcc(format
);
1018 if ((format
& IMGFMT_BGR_MASK
) == IMGFMT_BGR
) {
1019 int bpp
= format
& 0xff;
1022 return VFCAP_ACCEPT_STRIDE
| VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
;
1027 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
,
1028 unsigned char *srca
, int stride
)
1032 dst
= center
+ fb_line_len
* y0
+ fb_pixel_size
* x0
;
1034 (*draw_alpha_p
)(w
, h
, src
, srca
, stride
, dst
, fb_line_len
);
1037 static int draw_frame(uint8_t *src
[])
1042 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
, int y
)
1046 d
= center
+ fb_line_len
* y
+ fb_pixel_size
* x
;
1048 memcpy_pic2(d
, src
[0], w
* fb_pixel_size
, h
, fb_line_len
, stride
[0], 1);
1053 static void check_events(void)
1057 static void flip_page(void)
1059 int next_page
= !fb_page
;
1060 int page_delta
= next_page
- fb_page
;
1065 if (!vo_doublebuffering
)
1068 fb_vinfo
.yoffset
= fb_page
* fb_yres
;
1069 ioctl(fb_dev_fd
, FBIOPAN_DISPLAY
, &fb_vinfo
);
1071 center
+= page_delta
* fb_yres
* fb_line_len
;
1072 fb_page
= next_page
;
1075 static void draw_osd(void)
1077 vo_draw_text(in_width
, in_height
, draw_alpha
);
1080 static void uninit(void)
1082 if (fb_cmap_changed
) {
1083 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, &fb_oldcmap
))
1084 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't restore original cmap\n");
1085 fb_cmap_changed
= 0;
1087 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
))
1088 mp_msg(MSGT_VO
, MSGL_WARN
, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno
));
1089 fb_orig_vinfo
.xoffset
= fb_vinfo
.xoffset
;
1090 fb_orig_vinfo
.yoffset
= fb_vinfo
.yoffset
;
1091 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_orig_vinfo
))
1092 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't reset original fb_var_screeninfo: %s\n", strerror(errno
));
1093 if (fb_tty_fd
>= 0) {
1094 if (ioctl(fb_tty_fd
, KDSETMODE
, KD_TEXT
) < 0)
1095 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't restore text mode: %s\n", strerror(errno
));
1097 vt_set_textarea(0, fb_orig_vinfo
.yres
);
1101 munmap(frame_buffer
, fb_size
);
1102 frame_buffer
= NULL
;
1110 static int preinit(const char *vo_subdevice
)
1116 if (memcmp(vo_subdevice
, "vidix", 5) == 0)
1117 vidix_name
= &vo_subdevice
[5];
1119 pre_init_err
= vidix_preinit(vidix_name
, &video_out_fbdev
);
1125 fb_dev_name
= strdup(vo_subdevice
);
1129 return pre_init_err
= fb_preinit(0) ? 0 : -1;
1133 static uint32_t get_image(mp_image_t
*mpi
)
1135 if (!IMGFMT_IS_BGR(mpi
->imgfmt
) ||
1136 IMGFMT_BGR_DEPTH(mpi
->imgfmt
) != fb_bpp
||
1137 (mpi
->type
!= MP_IMGTYPE_STATIC
&& mpi
->type
!= MP_IMGTYPE_TEMP
) ||
1138 (mpi
->flags
& MP_IMGFLAG_PLANAR
) ||
1139 (mpi
->flags
& MP_IMGFLAG_YUV
) ||
1140 mpi
->width
!= in_width
||
1141 mpi
->height
!= in_height
1145 mpi
->planes
[0] = center
;
1146 mpi
->stride
[0] = fb_line_len
;
1147 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1151 static int control(uint32_t request
, void *data
, ...)
1154 case VOCTRL_GET_IMAGE
:
1155 return get_image(data
);
1156 case VOCTRL_QUERY_FORMAT
:
1157 return query_format(*(uint32_t*)data
);
1163 case VOCTRL_SET_EQUALIZER
:
1169 value
= va_arg(ap
, int);
1172 return vidix_control(request
, data
, value
);
1174 case VOCTRL_GET_EQUALIZER
:
1180 value
= va_arg(ap
, int*);
1183 return vidix_control(request
, data
, value
);