2 * Video driver for Framebuffer device
3 * by Szabolcs Berecz <szabi@inf.elte.hu>
6 * Some idea and code borrowed from Chris Lawrence's ppmtofb-0.27
7 * Some fixes and small improvements by Joey Parrish <joey@nicewarrior.org>
19 #include <sys/ioctl.h>
24 #include "video_out.h"
25 #include "video_out_internal.h"
26 #include "fastmemcpy.h"
29 #include "vosub_vidix.h"
34 static vo_info_t info
= {
37 "Szabolcs Berecz <szabi@inf.elte.hu>",
44 /* Name of VIDIX driver */
45 static const char *vidix_name
= NULL
;
46 static vidix_grkey_t gr_key
;
48 static signed int pre_init_err
= -2;
49 /******************************
51 ******************************/
53 extern char *monitor_hfreq_str
;
54 extern char *monitor_vfreq_str
;
55 extern char *monitor_dotclock_str
;
57 static range_t
*monitor_hfreq
= NULL
;
58 static range_t
*monitor_vfreq
= NULL
;
59 static range_t
*monitor_dotclock
= NULL
;
63 uint32_t xres
, yres
, vxres
, vyres
, depth
;
64 uint32_t pixclock
, left
, right
, upper
, lower
, hslen
, vslen
;
69 #define MAX_NR_TOKEN 16
71 #define MAX_LINE_LEN 1000
76 static int validate_mode(fb_mode_t
*m
)
79 mp_msg(MSGT_VO
, MSGL_V
, "needs geometry ");
83 mp_msg(MSGT_VO
, MSGL_V
, "needs timings ");
90 static int line_num
= 0;
92 static char *token
[MAX_NR_TOKEN
];
94 static int get_token(int num
)
96 static int read_nextline
= 1;
101 if (num
>= MAX_NR_TOKEN
) {
102 mp_msg(MSGT_VO
, MSGL_V
, "get_token(): max >= MAX_NR_TOKEN!\n");
107 if (!fgets(line
, MAX_LINE_LEN
, fp
))
113 for (i
= 0; i
< num
; i
++) {
114 while (isspace(line
[line_pos
]))
116 if (line
[line_pos
] == '\0' || line
[line_pos
] == '#') {
120 token
[i
] = line
+ line_pos
;
122 if (c
== '"' || c
== '\'') {
124 while (line
[++line_pos
] != c
&& line
[line_pos
])
128 line
[line_pos
] = ' ';
130 for (/* NOTHING */; !isspace(line
[line_pos
]) &&
131 line
[line_pos
]; line_pos
++)
134 if (!line
[line_pos
]) {
140 line
[line_pos
++] = '\0';
150 static fb_mode_t
*fb_modes
= NULL
;
151 static int nr_modes
= 0;
153 static int parse_fbmode_cfg(char *cfgfile
)
155 #define CHECK_IN_MODE_DEF\
157 mp_msg(MSGT_VO, MSGL_V, "'needs 'mode' first");\
158 goto err_out_print_linenum;\
160 fb_mode_t
*mode
= NULL
;
161 char *endptr
; // strtoul()...
165 /* If called more than once, reuse parsed data */
169 mp_msg(MSGT_VO
, MSGL_V
, "Reading %s: ", cfgfile
);
171 if ((fp
= fopen(cfgfile
, "r")) == NULL
) {
172 mp_msg(MSGT_VO
, MSGL_V
, "can't open '%s': %s\n", cfgfile
, strerror(errno
));
176 if ((line
= (char *) malloc(MAX_LINE_LEN
+ 1)) == NULL
) {
177 mp_msg(MSGT_VO
, MSGL_V
, "can't get memory for 'line': %s\n", strerror(errno
));
182 * check if the cfgfile starts with 'mode'
184 while ((tmp
= get_token(1)) == RET_EOL
)
188 if (!strcmp(token
[0], "mode"))
190 goto err_out_parse_error
;
192 while ((tmp
= get_token(1)) != RET_EOF
) {
195 if (!strcmp(token
[0], "mode")) {
197 mp_msg(MSGT_VO
, MSGL_V
, "'endmode' required");
198 goto err_out_print_linenum
;
200 if (!validate_mode(mode
))
201 goto err_out_not_valid
;
203 if (!(fb_modes
= (fb_mode_t
*) realloc(fb_modes
,
204 sizeof(fb_mode_t
) * (nr_modes
+ 1)))) {
205 mp_msg(MSGT_VO
, MSGL_V
, "can't realloc 'fb_modes' (nr_modes = %d):"
206 " %s\n", nr_modes
, strerror(errno
));
209 mode
=fb_modes
+ nr_modes
;
211 memset(mode
,0,sizeof(fb_mode_t
));
213 if (get_token(1) < 0)
214 goto err_out_parse_error
;
215 for (i
= 0; i
< nr_modes
- 1; i
++) {
216 if (!strcmp(token
[0], fb_modes
[i
].name
)) {
217 mp_msg(MSGT_VO
, MSGL_V
, "mode name '%s' isn't unique", token
[0]);
218 goto err_out_print_linenum
;
221 if (!(mode
->name
= strdup(token
[0]))) {
222 mp_msg(MSGT_VO
, MSGL_V
, "can't strdup -> 'name': %s\n", strerror(errno
));
226 } else if (!strcmp(token
[0], "geometry")) {
228 if (get_token(5) < 0)
229 goto err_out_parse_error
;
230 mode
->xres
= strtoul(token
[0], &endptr
, 0);
232 goto err_out_parse_error
;
233 mode
->yres
= strtoul(token
[1], &endptr
, 0);
235 goto err_out_parse_error
;
236 mode
->vxres
= strtoul(token
[2], &endptr
, 0);
238 goto err_out_parse_error
;
239 mode
->vyres
= strtoul(token
[3], &endptr
, 0);
241 goto err_out_parse_error
;
242 mode
->depth
= strtoul(token
[4], &endptr
, 0);
244 goto err_out_parse_error
;
245 } else if (!strcmp(token
[0], "timings")) {
247 if (get_token(7) < 0)
248 goto err_out_parse_error
;
249 mode
->pixclock
= strtoul(token
[0], &endptr
, 0);
251 goto err_out_parse_error
;
252 mode
->left
= strtoul(token
[1], &endptr
, 0);
254 goto err_out_parse_error
;
255 mode
->right
= strtoul(token
[2], &endptr
, 0);
257 goto err_out_parse_error
;
258 mode
->upper
= strtoul(token
[3], &endptr
, 0);
260 goto err_out_parse_error
;
261 mode
->lower
= strtoul(token
[4], &endptr
, 0);
263 goto err_out_parse_error
;
264 mode
->hslen
= strtoul(token
[5], &endptr
, 0);
266 goto err_out_parse_error
;
267 mode
->vslen
= strtoul(token
[6], &endptr
, 0);
269 goto err_out_parse_error
;
270 } else if (!strcmp(token
[0], "endmode")) {
273 } else if (!strcmp(token
[0], "accel")) {
275 if (get_token(1) < 0)
276 goto err_out_parse_error
;
278 * it's only used for text acceleration
279 * so we just ignore it.
281 } else if (!strcmp(token
[0], "hsync")) {
283 if (get_token(1) < 0)
284 goto err_out_parse_error
;
285 if (!strcmp(token
[0], "low"))
286 mode
->sync
&= ~FB_SYNC_HOR_HIGH_ACT
;
287 else if(!strcmp(token
[0], "high"))
288 mode
->sync
|= FB_SYNC_HOR_HIGH_ACT
;
290 goto err_out_parse_error
;
291 } else if (!strcmp(token
[0], "vsync")) {
293 if (get_token(1) < 0)
294 goto err_out_parse_error
;
295 if (!strcmp(token
[0], "low"))
296 mode
->sync
&= ~FB_SYNC_VERT_HIGH_ACT
;
297 else if(!strcmp(token
[0], "high"))
298 mode
->sync
|= FB_SYNC_VERT_HIGH_ACT
;
300 goto err_out_parse_error
;
301 } else if (!strcmp(token
[0], "csync")) {
303 if (get_token(1) < 0)
304 goto err_out_parse_error
;
305 if (!strcmp(token
[0], "low"))
306 mode
->sync
&= ~FB_SYNC_COMP_HIGH_ACT
;
307 else if(!strcmp(token
[0], "high"))
308 mode
->sync
|= FB_SYNC_COMP_HIGH_ACT
;
310 goto err_out_parse_error
;
311 } else if (!strcmp(token
[0], "extsync")) {
313 if (get_token(1) < 0)
314 goto err_out_parse_error
;
315 if (!strcmp(token
[0], "false"))
316 mode
->sync
&= ~FB_SYNC_EXT
;
317 else if(!strcmp(token
[0], "true"))
318 mode
->sync
|= FB_SYNC_EXT
;
320 goto err_out_parse_error
;
321 } else if (!strcmp(token
[0], "laced")) {
323 if (get_token(1) < 0)
324 goto err_out_parse_error
;
325 if (!strcmp(token
[0], "false"))
326 mode
->vmode
= FB_VMODE_NONINTERLACED
;
327 else if (!strcmp(token
[0], "true"))
328 mode
->vmode
= FB_VMODE_INTERLACED
;
330 goto err_out_parse_error
;
331 } else if (!strcmp(token
[0], "double")) {
333 if (get_token(1) < 0)
334 goto err_out_parse_error
;
335 if (!strcmp(token
[0], "false"))
337 else if (!strcmp(token
[0], "true"))
338 mode
->vmode
= FB_VMODE_DOUBLE
;
340 goto err_out_parse_error
;
342 goto err_out_parse_error
;
344 if (!validate_mode(mode
))
345 goto err_out_not_valid
;
347 mp_msg(MSGT_VO
, MSGL_V
, "%d modes\n", nr_modes
);
352 mp_msg(MSGT_VO
, MSGL_V
, "parse error");
353 err_out_print_linenum
:
354 mp_msg(MSGT_VO
, MSGL_V
, " at line %d\n", line_num
);
365 mp_msg(MSGT_VO
, MSGL_V
, "previous mode is not correct");
366 goto err_out_print_linenum
;
369 static fb_mode_t
*find_mode_by_name(char *name
)
373 for (i
= 0; i
< nr_modes
; i
++)
374 if (!strcmp(name
, fb_modes
[i
].name
))
379 static float dcf(fb_mode_t
*m
) //driving clock frequency
381 return 1e12f
/ m
->pixclock
;
384 static float hsf(fb_mode_t
*m
) //horizontal scan frequency
386 int htotal
= m
->left
+ m
->xres
+ m
->right
+ m
->hslen
;
387 return dcf(m
) / htotal
;
390 static float vsf(fb_mode_t
*m
) //vertical scan frequency
392 int vtotal
= m
->upper
+ m
->yres
+ m
->lower
+ m
->vslen
;
393 return hsf(m
) / vtotal
;
397 static int mode_works(fb_mode_t
*m
, range_t
*hfreq
, range_t
*vfreq
,
405 mp_msg(MSGT_VO
, MSGL_DBG2
, "mode %dx%d:", m
->xres
, m
->yres
);
406 if (!in_range(hfreq
, h
)) {
408 mp_msg(MSGT_VO
, MSGL_DBG2
, " hsync out of range.");
410 if (!in_range(vfreq
, v
)) {
412 mp_msg(MSGT_VO
, MSGL_DBG2
, " vsync out of range.");
414 if (!in_range(dotclock
, d
)) {
416 mp_msg(MSGT_VO
, MSGL_DBG2
, " dotclock out of range.");
419 mp_msg(MSGT_VO
, MSGL_DBG2
, " hsync, vsync, dotclock ok.\n");
421 mp_msg(MSGT_VO
, MSGL_DBG2
, "\n");
426 static fb_mode_t
*find_best_mode(int xres
, int yres
, range_t
*hfreq
,
427 range_t
*vfreq
, range_t
*dotclock
)
430 fb_mode_t
*best
= fb_modes
;
433 mp_msg(MSGT_VO
, MSGL_DBG2
, "Searching for first working mode\n");
435 for (i
= 0; i
< nr_modes
; i
++, best
++)
436 if (mode_works(best
, hfreq
, vfreq
, dotclock
))
441 if (i
== nr_modes
- 1)
444 mp_msg(MSGT_VO
, MSGL_DBG2
, "First working mode: %dx%d\n", best
->xres
, best
->yres
);
445 mp_msg(MSGT_VO
, MSGL_DBG2
, "Searching for better modes\n");
447 for (curr
= best
+ 1; i
< nr_modes
- 1; i
++, curr
++) {
448 if (!mode_works(curr
, hfreq
, vfreq
, dotclock
))
451 if (best
->xres
< xres
|| best
->yres
< yres
) {
452 if (curr
->xres
> best
->xres
|| curr
->yres
> best
->yres
) {
453 mp_msg(MSGT_VO
, MSGL_DBG2
, "better than %dx%d, which is too small.\n",
454 best
->xres
, best
->yres
);
457 mp_msg(MSGT_VO
, MSGL_DBG2
, "too small.\n");
458 } else if (curr
->xres
== best
->xres
&& curr
->yres
== best
->yres
&&
459 vsf(curr
) > vsf(best
)) {
460 mp_msg(MSGT_VO
, MSGL_DBG2
, "faster screen refresh.\n");
462 } else if ((curr
->xres
<= best
->xres
&& curr
->yres
<= best
->yres
) &&
463 (curr
->xres
>= xres
&& curr
->yres
>= yres
)) {
464 mp_msg(MSGT_VO
, MSGL_DBG2
, "better than %dx%d, which is too large.\n",
465 best
->xres
, best
->yres
);
468 if (curr
->xres
< xres
|| curr
->yres
< yres
)
469 mp_msg(MSGT_VO
, MSGL_DBG2
, "too small.\n");
470 else if (curr
->xres
> best
->xres
|| curr
->yres
> best
->yres
)
471 mp_msg(MSGT_VO
, MSGL_DBG2
, "too large.\n");
472 else mp_msg(MSGT_VO
, MSGL_DBG2
, "it's worse, don't know why.\n");
479 static void set_bpp(struct fb_var_screeninfo
*p
, int bpp
)
481 p
->bits_per_pixel
= (bpp
+ 1) & ~1;
482 p
->red
.msb_right
= p
->green
.msb_right
= p
->blue
.msb_right
= p
->transp
.msb_right
= 0;
483 p
->transp
.offset
= p
->transp
.length
= 0;
487 p
->transp
.offset
= 24;
488 p
->transp
.length
= 8;
513 static void fb_mode2fb_vinfo(fb_mode_t
*m
, struct fb_var_screeninfo
*v
)
517 v
->xres_virtual
= m
->vxres
;
518 v
->yres_virtual
= m
->vyres
;
519 set_bpp(v
, m
->depth
);
520 v
->pixclock
= m
->pixclock
;
521 v
->left_margin
= m
->left
;
522 v
->right_margin
= m
->right
;
523 v
->upper_margin
= m
->upper
;
524 v
->lower_margin
= m
->lower
;
525 v
->hsync_len
= m
->hslen
;
526 v
->vsync_len
= m
->vslen
;
532 /******************************
534 ******************************/
536 /* command line/config file options */
537 char *fb_dev_name
= NULL
;
538 char *fb_mode_cfgfile
= NULL
;
539 char *fb_mode_name
= NULL
;
541 static fb_mode_t
*fb_mode
= NULL
;
543 /* vt related variables */
544 static FILE *vt_fp
= NULL
;
545 static int vt_doit
= 1;
547 /* vo_fbdev related variables */
548 static int fb_dev_fd
;
549 static int fb_tty_fd
= -1;
550 static size_t fb_size
;
551 static uint8_t *frame_buffer
;
552 static uint8_t *center
; /* thx .so :) */
553 static struct fb_fix_screeninfo fb_finfo
;
554 static struct fb_var_screeninfo fb_orig_vinfo
;
555 static struct fb_var_screeninfo fb_vinfo
;
556 static unsigned short fb_ored
[256], fb_ogreen
[256], fb_oblue
[256];
557 static struct fb_cmap fb_oldcmap
= { 0, 256, fb_ored
, fb_ogreen
, fb_oblue
};
558 static int fb_cmap_changed
= 0;
559 static int fb_pixel_size
; // 32: 4 24: 3 16: 2 15: 2
560 static int fb_bpp
; // 32: 32 24: 24 16: 16 15: 15
561 static int fb_bpp_we_want
; // 32: 32 24: 24 16: 16 15: 15
562 static int fb_line_len
;
565 static void (*draw_alpha_p
)(int w
, int h
, unsigned char *src
,
566 unsigned char *srca
, int stride
, unsigned char *dst
,
570 static int in_height
;
571 static int out_width
;
572 static int out_height
;
573 static int first_row
;
575 static uint32_t pixel_format
;
579 * Note: this function is completely cut'n'pasted from
580 * Chris Lawrence's code.
581 * (modified a bit to fit in my code...)
583 static struct fb_cmap
*make_directcolor_cmap(struct fb_var_screeninfo
*var
)
585 /* Hopefully any DIRECTCOLOR device will have a big enough palette
586 * to handle mapping the full color depth.
587 * e.g. 8 bpp -> 256 entry palette
589 * We could handle some sort of gamma here
591 int i
, cols
, rcols
, gcols
, bcols
;
592 uint16_t *red
, *green
, *blue
;
593 struct fb_cmap
*cmap
;
595 rcols
= 1 << var
->red
.length
;
596 gcols
= 1 << var
->green
.length
;
597 bcols
= 1 << var
->blue
.length
;
599 /* Make our palette the length of the deepest color */
600 cols
= (rcols
> gcols
? rcols
: gcols
);
601 cols
= (cols
> bcols
? cols
: bcols
);
603 red
= malloc(cols
* sizeof(red
[0]));
605 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate red palette with %d entries.\n", cols
);
608 for(i
=0; i
< rcols
; i
++)
609 red
[i
] = (65535/(rcols
-1)) * i
;
611 green
= malloc(cols
* sizeof(green
[0]));
613 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate green palette with %d entries.\n", cols
);
617 for(i
=0; i
< gcols
; i
++)
618 green
[i
] = (65535/(gcols
-1)) * i
;
620 blue
= malloc(cols
* sizeof(blue
[0]));
622 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate blue palette with %d entries.\n", cols
);
627 for(i
=0; i
< bcols
; i
++)
628 blue
[i
] = (65535/(bcols
-1)) * i
;
630 cmap
= malloc(sizeof(struct fb_cmap
));
632 mp_msg(MSGT_VO
, MSGL_V
, "Can't allocate color map\n");
650 static int fb_preinit(int reset
)
652 static int fb_preinit_done
= 0;
653 static int fb_works
= 0;
664 if (!fb_dev_name
&& !(fb_dev_name
= getenv("FRAMEBUFFER")))
665 fb_dev_name
= strdup("/dev/fb0");
666 mp_msg(MSGT_VO
, MSGL_V
, "using %s\n", fb_dev_name
);
668 if ((fb_dev_fd
= open(fb_dev_name
, O_RDWR
)) == -1) {
669 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't open %s: %s\n", fb_dev_name
, strerror(errno
));
672 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
673 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't get VSCREENINFO: %s\n", strerror(errno
));
676 fb_orig_vinfo
= fb_vinfo
;
678 if ((fb_tty_fd
= open("/dev/tty", O_RDWR
)) < 0) {
679 mp_msg(MSGT_VO
, MSGL_ERR
, "notice: Can't open /dev/tty: %s\n", strerror(errno
));
682 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+
683 fb_vinfo
.blue
.length
+ fb_vinfo
.transp
.length
;
685 if (fb_bpp
== 8 && !vo_dbpp
) {
686 mp_msg(MSGT_VO
, MSGL_ERR
, "8 bpp output is not supported.\n");
691 if (vo_dbpp
!= 15 && vo_dbpp
!= 16 && vo_dbpp
!= 24 &&
693 mp_msg(MSGT_VO
, MSGL_ERR
, "can't switch to %d bpp\n", vo_dbpp
);
699 if (!fb_mode_cfgfile
)
700 fb_mode_cfgfile
= strdup("/etc/fb.modes");
717 static void lots_of_printf(void)
719 mp_msg(MSGT_VO
, MSGL_V
, "var info:\n");
720 mp_msg(MSGT_VO
, MSGL_V
, "xres: %u\n", fb_vinfo
.xres
);
721 mp_msg(MSGT_VO
, MSGL_V
, "yres: %u\n", fb_vinfo
.yres
);
722 mp_msg(MSGT_VO
, MSGL_V
, "xres_virtual: %u\n", fb_vinfo
.xres_virtual
);
723 mp_msg(MSGT_VO
, MSGL_V
, "yres_virtual: %u\n", fb_vinfo
.yres_virtual
);
724 mp_msg(MSGT_VO
, MSGL_V
, "xoffset: %u\n", fb_vinfo
.xoffset
);
725 mp_msg(MSGT_VO
, MSGL_V
, "yoffset: %u\n", fb_vinfo
.yoffset
);
726 mp_msg(MSGT_VO
, MSGL_V
, "bits_per_pixel: %u\n", fb_vinfo
.bits_per_pixel
);
727 mp_msg(MSGT_VO
, MSGL_V
, "grayscale: %u\n", fb_vinfo
.grayscale
);
728 mp_msg(MSGT_VO
, MSGL_V
, "red: %lu %lu %lu\n",
729 (unsigned long) fb_vinfo
.red
.offset
,
730 (unsigned long) fb_vinfo
.red
.length
,
731 (unsigned long) fb_vinfo
.red
.msb_right
);
732 mp_msg(MSGT_VO
, MSGL_V
, "green: %lu %lu %lu\n",
733 (unsigned long) fb_vinfo
.green
.offset
,
734 (unsigned long) fb_vinfo
.green
.length
,
735 (unsigned long) fb_vinfo
.green
.msb_right
);
736 mp_msg(MSGT_VO
, MSGL_V
, "blue: %lu %lu %lu\n",
737 (unsigned long) fb_vinfo
.blue
.offset
,
738 (unsigned long) fb_vinfo
.blue
.length
,
739 (unsigned long) fb_vinfo
.blue
.msb_right
);
740 mp_msg(MSGT_VO
, MSGL_V
, "transp: %lu %lu %lu\n",
741 (unsigned long) fb_vinfo
.transp
.offset
,
742 (unsigned long) fb_vinfo
.transp
.length
,
743 (unsigned long) fb_vinfo
.transp
.msb_right
);
744 mp_msg(MSGT_VO
, MSGL_V
, "nonstd: %u\n", fb_vinfo
.nonstd
);
745 mp_msg(MSGT_VO
, MSGL_DBG2
, "activate: %u\n", fb_vinfo
.activate
);
746 mp_msg(MSGT_VO
, MSGL_DBG2
, "height: %u\n", fb_vinfo
.height
);
747 mp_msg(MSGT_VO
, MSGL_DBG2
, "width: %u\n", fb_vinfo
.width
);
748 mp_msg(MSGT_VO
, MSGL_DBG2
, "accel_flags: %u\n", fb_vinfo
.accel_flags
);
749 mp_msg(MSGT_VO
, MSGL_DBG2
, "timing:\n");
750 mp_msg(MSGT_VO
, MSGL_DBG2
, "pixclock: %u\n", fb_vinfo
.pixclock
);
751 mp_msg(MSGT_VO
, MSGL_DBG2
, "left_margin: %u\n", fb_vinfo
.left_margin
);
752 mp_msg(MSGT_VO
, MSGL_DBG2
, "right_margin: %u\n", fb_vinfo
.right_margin
);
753 mp_msg(MSGT_VO
, MSGL_DBG2
, "upper_margin: %u\n", fb_vinfo
.upper_margin
);
754 mp_msg(MSGT_VO
, MSGL_DBG2
, "lower_margin: %u\n", fb_vinfo
.lower_margin
);
755 mp_msg(MSGT_VO
, MSGL_DBG2
, "hsync_len: %u\n", fb_vinfo
.hsync_len
);
756 mp_msg(MSGT_VO
, MSGL_DBG2
, "vsync_len: %u\n", fb_vinfo
.vsync_len
);
757 mp_msg(MSGT_VO
, MSGL_DBG2
, "sync: %u\n", fb_vinfo
.sync
);
758 mp_msg(MSGT_VO
, MSGL_DBG2
, "vmode: %u\n", fb_vinfo
.vmode
);
759 mp_msg(MSGT_VO
, MSGL_V
, "fix info:\n");
760 mp_msg(MSGT_VO
, MSGL_V
, "framebuffer size: %d bytes\n", fb_finfo
.smem_len
);
761 mp_msg(MSGT_VO
, MSGL_V
, "type: %lu\n", (unsigned long) fb_finfo
.type
);
762 mp_msg(MSGT_VO
, MSGL_V
, "type_aux: %lu\n", (unsigned long) fb_finfo
.type_aux
);
763 mp_msg(MSGT_VO
, MSGL_V
, "visual: %lu\n", (unsigned long) fb_finfo
.visual
);
764 mp_msg(MSGT_VO
, MSGL_V
, "line_length: %lu bytes\n", (unsigned long) fb_finfo
.line_length
);
765 mp_msg(MSGT_VO
, MSGL_DBG2
, "id: %.16s\n", fb_finfo
.id
);
766 mp_msg(MSGT_VO
, MSGL_DBG2
, "smem_start: %p\n", (void *) fb_finfo
.smem_start
);
767 mp_msg(MSGT_VO
, MSGL_DBG2
, "xpanstep: %u\n", fb_finfo
.xpanstep
);
768 mp_msg(MSGT_VO
, MSGL_DBG2
, "ypanstep: %u\n", fb_finfo
.ypanstep
);
769 mp_msg(MSGT_VO
, MSGL_DBG2
, "ywrapstep: %u\n", fb_finfo
.ywrapstep
);
770 mp_msg(MSGT_VO
, MSGL_DBG2
, "mmio_start: %p\n", (void *) fb_finfo
.mmio_start
);
771 mp_msg(MSGT_VO
, MSGL_DBG2
, "mmio_len: %u bytes\n", fb_finfo
.mmio_len
);
772 mp_msg(MSGT_VO
, MSGL_DBG2
, "accel: %u\n", fb_finfo
.accel
);
773 mp_msg(MSGT_VO
, MSGL_V
, "fb_bpp: %d\n", fb_bpp
);
774 mp_msg(MSGT_VO
, MSGL_V
, "fb_pixel_size: %d bytes\n", fb_pixel_size
);
775 mp_msg(MSGT_VO
, MSGL_V
, "other:\n");
776 mp_msg(MSGT_VO
, MSGL_V
, "in_width: %d\n", in_width
);
777 mp_msg(MSGT_VO
, MSGL_V
, "in_height: %d\n", in_height
);
778 mp_msg(MSGT_VO
, MSGL_V
, "out_width: %d\n", out_width
);
779 mp_msg(MSGT_VO
, MSGL_V
, "out_height: %d\n", out_height
);
780 mp_msg(MSGT_VO
, MSGL_V
, "first_row: %d\n", first_row
);
781 mp_msg(MSGT_VO
, MSGL_V
, "last_row: %d\n", last_row
);
782 mp_msg(MSGT_VO
, MSGL_DBG2
, "draw_alpha_p:%dbpp = %p\n", fb_bpp
, draw_alpha_p
);
785 static void vt_set_textarea(int u
, int l
)
787 /* how can I determine the font height?
788 * just use 16 for now
790 int urow
= ((u
+ 15) / 16) + 1;
793 mp_msg(MSGT_VO
, MSGL_DBG2
, "vt_set_textarea(%d,%d): %d,%d\n", u
, l
, urow
, lrow
);
795 fprintf(vt_fp
, "\33[%d;%dr\33[%d;%dH", urow
, lrow
, lrow
, 0);
800 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
801 uint32_t d_height
, uint32_t flags
, char *title
,
804 struct fb_cmap
*cmap
;
805 int vm
= flags
& VOFLAG_MODESWITCHING
;
806 int zoom
= flags
& VOFLAG_SWSCALE
;
809 fs
= flags
& VOFLAG_FULLSCREEN
;
811 if(pre_init_err
== -2)
813 mp_msg(MSGT_VO
, MSGL_ERR
, "Internal fatal error: config() was called before preinit()\n");
817 if (pre_init_err
) return 1;
819 if (fb_mode_name
&& !vm
) {
820 mp_msg(MSGT_VO
, MSGL_ERR
, "-fbmode can only be used with -vm\n");
823 if (vm
&& (parse_fbmode_cfg(fb_mode_cfgfile
) < 0))
825 if (d_width
&& (fs
|| vm
)) {
827 out_height
= d_height
;
834 pixel_format
= format
;
837 if (!(fb_mode
= find_mode_by_name(fb_mode_name
))) {
838 mp_msg(MSGT_VO
, MSGL_ERR
, "can't find requested video mode\n");
841 fb_mode2fb_vinfo(fb_mode
, &fb_vinfo
);
843 monitor_hfreq
= str2range(monitor_hfreq_str
);
844 monitor_vfreq
= str2range(monitor_vfreq_str
);
845 monitor_dotclock
= str2range(monitor_dotclock_str
);
846 if (!monitor_hfreq
|| !monitor_vfreq
|| !monitor_dotclock
) {
847 mp_msg(MSGT_VO
, MSGL_ERR
, "you have to specify the capabilities of"
851 if (!(fb_mode
= find_best_mode(out_width
, out_height
,
852 monitor_hfreq
, monitor_vfreq
,
853 monitor_dotclock
))) {
854 mp_msg(MSGT_VO
, MSGL_ERR
, "can't find best video mode\n");
857 mp_msg(MSGT_VO
, MSGL_V
, "using mode %dx%d @ %.1fHz\n", fb_mode
->xres
,
858 fb_mode
->yres
, vsf(fb_mode
));
859 fb_mode2fb_vinfo(fb_mode
, &fb_vinfo
);
861 fb_bpp_we_want
= fb_bpp
;
862 set_bpp(&fb_vinfo
, fb_bpp
);
863 fb_vinfo
.xres_virtual
= fb_vinfo
.xres
;
864 fb_vinfo
.yres_virtual
= fb_vinfo
.yres
;
866 if (fb_tty_fd
>= 0 && ioctl(fb_tty_fd
, KDSETMODE
, KD_GRAPHICS
) < 0) {
867 mp_msg(MSGT_VO
, MSGL_V
, "Can't set graphics mode: %s\n", strerror(errno
));
872 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
)) {
873 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't put VSCREENINFO: %s\n", strerror(errno
));
874 if (fb_tty_fd
>= 0 && ioctl(fb_tty_fd
, KDSETMODE
, KD_TEXT
) < 0) {
875 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't restore text mode: %s\n", strerror(errno
));
880 fb_pixel_size
= fb_vinfo
.bits_per_pixel
/ 8;
881 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+
882 fb_vinfo
.blue
.length
+ fb_vinfo
.transp
.length
;
883 if (fb_bpp_we_want
!= fb_bpp
)
884 mp_msg(MSGT_VO
, MSGL_WARN
, "requested %d bpp, got %d bpp!!!\n",
885 fb_bpp_we_want
, fb_bpp
);
888 case 32: draw_alpha_p
= vo_draw_alpha_rgb32
; break;
889 case 24: draw_alpha_p
= vo_draw_alpha_rgb24
; break;
890 case 16: draw_alpha_p
= vo_draw_alpha_rgb16
; break;
891 case 15: draw_alpha_p
= vo_draw_alpha_rgb15
; break;
895 fb_xres
= fb_vinfo
.xres
;
896 fb_yres
= fb_vinfo
.yres
;
900 out_height
= fb_yres
;
902 if (out_width
< in_width
|| out_height
< in_height
) {
903 mp_msg(MSGT_VO
, MSGL_ERR
, "screensize is smaller than video size\n");
907 first_row
= (out_height
- in_height
) / 2;
908 last_row
= (out_height
+ in_height
) / 2;
910 if (ioctl(fb_dev_fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
911 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't get FSCREENINFO: %s\n", strerror(errno
));
917 if (fb_finfo
.type
!= FB_TYPE_PACKED_PIXELS
) {
918 mp_msg(MSGT_VO
, MSGL_ERR
, "type %d not supported\n", fb_finfo
.type
);
922 switch (fb_finfo
.visual
) {
923 case FB_VISUAL_TRUECOLOR
:
925 case FB_VISUAL_DIRECTCOLOR
:
926 mp_msg(MSGT_VO
, MSGL_V
, "creating cmap for directcolor\n");
927 if (ioctl(fb_dev_fd
, FBIOGETCMAP
, &fb_oldcmap
)) {
928 mp_msg(MSGT_VO
, MSGL_ERR
, "can't get cmap: %s\n",
932 if (!(cmap
= make_directcolor_cmap(&fb_vinfo
)))
934 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, cmap
)) {
935 mp_msg(MSGT_VO
, MSGL_ERR
, "can't put cmap: %s\n",
946 mp_msg(MSGT_VO
, MSGL_ERR
, "visual: %d not yet supported\n",
951 fb_line_len
= fb_finfo
.line_length
;
952 fb_size
= fb_finfo
.smem_len
;
957 unsigned image_width
,image_height
,x_offset
,y_offset
;
959 aspect_save_orig(width
,height
);
960 aspect_save_prescale(d_width
,d_height
);
961 aspect_save_screenres(fb_xres
,fb_yres
);
962 aspect(&image_width
,&image_height
,fs
? A_ZOOM
: A_NOZOOM
);
968 if(fb_xres
> image_width
)
969 x_offset
= (fb_xres
- image_width
) / 2;
971 if(fb_yres
> image_height
)
972 y_offset
= (fb_yres
- image_height
) / 2;
975 if(vidix_init(width
,height
,x_offset
,y_offset
,image_width
,
976 image_height
,format
,fb_bpp
,
977 fb_xres
,fb_yres
) != 0)
979 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't initialize VIDIX driver\n");
984 else mp_msg(MSGT_VO
, MSGL_V
, "Using VIDIX\n");
986 if (vidix_grkey_support())
988 vidix_grkey_get(&gr_key
);
989 gr_key
.key_op
= KEYS_PUT
;
990 if (!(vo_colorkey
& 0xff000000))
992 gr_key
.ckey
.op
= CKEY_TRUE
;
993 gr_key
.ckey
.red
= (vo_colorkey
& 0x00ff0000) >> 16;
994 gr_key
.ckey
.green
= (vo_colorkey
& 0x0000ff00) >> 8;
995 gr_key
.ckey
.blue
= vo_colorkey
& 0x000000ff;
998 gr_key
.ckey
.op
= CKEY_FALSE
;
999 vidix_grkey_set(&gr_key
);
1005 int x_offset
=0,y_offset
=0;
1006 if ((frame_buffer
= (uint8_t *) mmap(0, fb_size
, PROT_READ
| PROT_WRITE
,
1007 MAP_SHARED
, fb_dev_fd
, 0)) == (uint8_t *) -1) {
1008 mp_msg(MSGT_VO
, MSGL_ERR
, "Can't mmap %s: %s\n", fb_dev_name
, strerror(errno
));
1012 center
= frame_buffer
+
1013 ( (out_width
- in_width
) / 2 ) * fb_pixel_size
+
1014 ( (out_height
- in_height
) / 2 ) * fb_line_len
+
1015 x_offset
* fb_pixel_size
+ y_offset
* fb_line_len
;
1017 mp_msg(MSGT_VO
, MSGL_DBG2
, "frame_buffer @ %p\n", frame_buffer
);
1018 mp_msg(MSGT_VO
, MSGL_DBG2
, "center @ %p\n", center
);
1019 mp_msg(MSGT_VO
, MSGL_V
, "pixel per line: %d\n", fb_line_len
/ fb_pixel_size
);
1022 memset(frame_buffer
, '\0', fb_line_len
* fb_yres
);
1024 if (vt_doit
&& (vt_fd
= open("/dev/tty", O_WRONLY
)) == -1) {
1025 mp_msg(MSGT_VO
, MSGL_ERR
, "can't open /dev/tty: %s\n", strerror(errno
));
1028 if (vt_doit
&& !(vt_fp
= fdopen(vt_fd
, "w"))) {
1029 mp_msg(MSGT_VO
, MSGL_ERR
, "can't fdopen /dev/tty: %s\n", strerror(errno
));
1034 vt_set_textarea(last_row
, fb_yres
);
1039 static int query_format(uint32_t format
)
1045 return (vidix_query_fourcc(format
));
1047 if ((format
& IMGFMT_BGR_MASK
) == IMGFMT_BGR
) {
1048 int bpp
= format
& 0xff;
1051 return VFCAP_ACCEPT_STRIDE
| VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
;
1056 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
,
1057 unsigned char *srca
, int stride
)
1061 dst
= center
+ fb_line_len
* y0
+ fb_pixel_size
* x0
;
1063 (*draw_alpha_p
)(w
, h
, src
, srca
, stride
, dst
, fb_line_len
);
1066 static int draw_frame(uint8_t *src
[]) { return 1; }
1068 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
,
1074 d
= center
+ fb_line_len
* y
+ fb_pixel_size
* x
;
1078 memcpy(d
, s
, w
* fb_pixel_size
);
1087 static void check_events(void)
1091 static void flip_page(void)
1095 static void draw_osd(void)
1097 vo_draw_text(in_width
, in_height
, draw_alpha
);
1100 static void uninit(void)
1102 if (fb_cmap_changed
) {
1103 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, &fb_oldcmap
))
1104 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't restore original cmap\n");
1105 fb_cmap_changed
= 0;
1107 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
))
1108 mp_msg(MSGT_VO
, MSGL_WARN
, "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno
));
1109 fb_orig_vinfo
.xoffset
= fb_vinfo
.xoffset
;
1110 fb_orig_vinfo
.yoffset
= fb_vinfo
.yoffset
;
1111 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_orig_vinfo
))
1112 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't reset original fb_var_screeninfo: %s\n", strerror(errno
));
1113 if (fb_tty_fd
>= 0) {
1114 if (ioctl(fb_tty_fd
, KDSETMODE
, KD_TEXT
) < 0)
1115 mp_msg(MSGT_VO
, MSGL_WARN
, "Can't restore text mode: %s\n", strerror(errno
));
1118 vt_set_textarea(0, fb_orig_vinfo
.yres
);
1121 if(frame_buffer
) munmap(frame_buffer
, fb_size
);
1122 frame_buffer
= NULL
;
1124 if(vidix_name
) vidix_term();
1129 static int preinit(const char *vo_subdevice
)
1136 if (memcmp(vo_subdevice
, "vidix", 5) == 0)
1137 vidix_name
= &vo_subdevice
[5];
1139 pre_init_err
= vidix_preinit(vidix_name
,&video_out_fbdev
);
1143 if (fb_dev_name
) free(fb_dev_name
);
1144 fb_dev_name
= strdup(vo_subdevice
);
1147 if(!pre_init_err
) return (pre_init_err
=(fb_preinit(0)?0:-1));
1151 static uint32_t get_image(mp_image_t
*mpi
)
1154 !IMGFMT_IS_BGR(mpi
->imgfmt
) ||
1155 (IMGFMT_BGR_DEPTH(mpi
->imgfmt
) != fb_bpp
) ||
1156 ((mpi
->type
!= MP_IMGTYPE_STATIC
) && (mpi
->type
!= MP_IMGTYPE_TEMP
)) ||
1157 (mpi
->flags
& MP_IMGFLAG_PLANAR
) ||
1158 (mpi
->flags
& MP_IMGFLAG_YUV
) ||
1159 (mpi
->width
!= in_width
) ||
1160 (mpi
->height
!= in_height
)
1164 mpi
->planes
[0] = center
;
1165 mpi
->stride
[0] = fb_line_len
;
1166 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1170 static int control(uint32_t request
, void *data
, ...)
1173 case VOCTRL_GET_IMAGE
:
1174 return get_image(data
);
1175 case VOCTRL_QUERY_FORMAT
:
1176 return query_format(*((uint32_t*)data
));
1182 case VOCTRL_SET_EQUALIZER
:
1188 value
= va_arg(ap
, int);
1191 return vidix_control(request
, data
, (int *)value
);
1193 case VOCTRL_GET_EQUALIZER
:
1199 value
= va_arg(ap
, int*);
1202 return vidix_control(request
, data
, value
);