2 * video driver for framebuffer device
3 * copyright (C) 2003 Joey Parrish <joey@nicewarrior.org>
5 * This file is part of MPlayer.
7 * MPlayer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * MPlayer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <sys/ioctl.h>
34 #include "video_out.h"
35 #include "video_out_internal.h"
36 #include "fastmemcpy.h"
40 static const vo_info_t info
= {
43 "Joey Parrish <joey@nicewarrior.org>",
47 const LIBVO_EXTERN(fbdev2
)
49 static void set_bpp(struct fb_var_screeninfo
*p
, int bpp
)
51 p
->bits_per_pixel
= (bpp
+ 1) & ~1;
52 p
->red
.msb_right
= p
->green
.msb_right
= p
->blue
.msb_right
= p
->transp
.msb_right
= 0;
53 p
->transp
.offset
= p
->transp
.length
= 0;
57 p
->transp
.offset
= 24;
90 static char *fb_dev_name
= NULL
; // such as /dev/fb0
91 static int fb_dev_fd
; // handle for fb_dev_name
92 static uint8_t *frame_buffer
= NULL
; // mmap'd access to fbdev
93 static uint8_t *center
= NULL
; // where to begin writing our image (centered?)
94 static struct fb_fix_screeninfo fb_finfo
; // fixed info
95 static struct fb_var_screeninfo fb_vinfo
; // variable info
96 static struct fb_var_screeninfo fb_orig_vinfo
; // variable info to restore later
97 static unsigned short fb_ored
[256], fb_ogreen
[256], fb_oblue
[256];
98 static struct fb_cmap fb_oldcmap
= { 0, 256, fb_ored
, fb_ogreen
, fb_oblue
};
99 static int fb_cmap_changed
= 0; // to restore map
100 static int fb_pixel_size
; // 32: 4 24: 3 16: 2 15: 2
101 static int fb_bpp
; // 32: 32 24: 24 16: 16 15: 15
102 static size_t fb_size
; // size of frame_buffer
103 static int fb_line_len
; // length of one line in bytes
104 static void (*draw_alpha_p
)(int w
, int h
, unsigned char *src
,
105 unsigned char *srca
, int stride
, unsigned char *dst
,
108 static uint8_t *next_frame
= NULL
; // for double buffering
110 static int in_height
;
111 static int out_width
;
112 static int out_height
;
114 static struct fb_cmap
*make_directcolor_cmap(struct fb_var_screeninfo
*var
)
116 int i
, cols
, rcols
, gcols
, bcols
;
117 uint16_t *red
, *green
, *blue
;
118 struct fb_cmap
*cmap
;
120 rcols
= 1 << var
->red
.length
;
121 gcols
= 1 << var
->green
.length
;
122 bcols
= 1 << var
->blue
.length
;
124 /* Make our palette the length of the deepest color */
125 cols
= (rcols
> gcols
? rcols
: gcols
);
126 cols
= (cols
> bcols
? cols
: bcols
);
128 red
= malloc(cols
* sizeof(red
[0]));
130 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate red palette with %d entries.\n", cols
);
133 for(i
=0; i
< rcols
; i
++)
134 red
[i
] = (65535/(rcols
-1)) * i
;
136 green
= malloc(cols
* sizeof(green
[0]));
138 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate green palette with %d entries.\n", cols
);
142 for(i
=0; i
< gcols
; i
++)
143 green
[i
] = (65535/(gcols
-1)) * i
;
145 blue
= malloc(cols
* sizeof(blue
[0]));
147 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate blue palette with %d entries.\n", cols
);
152 for(i
=0; i
< bcols
; i
++)
153 blue
[i
] = (65535/(bcols
-1)) * i
;
155 cmap
= malloc(sizeof(struct fb_cmap
));
157 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate color map\n");
174 static int fb_preinit(int reset
)
176 static int fb_preinit_done
= 0;
177 static int fb_err
= -1;
188 if (!fb_dev_name
&& !(fb_dev_name
= getenv("FRAMEBUFFER")))
189 fb_dev_name
= strdup("/dev/fb0");
191 mp_msg(MSGT_VO
, MSGL_V
, "[fbdev2] Using device %s\n", fb_dev_name
);
193 if ((fb_dev_fd
= open(fb_dev_name
, O_RDWR
)) == -1) {
194 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't open %s: %s\n", fb_dev_name
, strerror(errno
));
197 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
198 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't get VSCREENINFO: %s\n", strerror(errno
));
201 fb_orig_vinfo
= fb_vinfo
;
203 fb_bpp
= fb_vinfo
.bits_per_pixel
;
205 /* 16 and 15 bpp is reported as 16 bpp */
207 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+
208 fb_vinfo
.blue
.length
;
213 if (fb_dev_fd
>= 0) close(fb_dev_fd
);
219 static int preinit(const char *subdevice
)
223 if (fb_dev_name
) free(fb_dev_name
);
224 fb_dev_name
= strdup(subdevice
);
226 return fb_preinit(0);
229 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
230 uint32_t d_height
, uint32_t flags
, char *title
,
233 struct fb_cmap
*cmap
;
234 int fs
= flags
& VOFLAG_FULLSCREEN
;
242 out_width
= fb_vinfo
.xres
;
243 out_height
= fb_vinfo
.yres
;
246 if (out_width
< in_width
|| out_height
< in_height
) {
247 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Screensize is smaller than video size (%dx%d < %dx%d)\n",
248 out_width
, out_height
, in_width
, in_height
);
253 case 32: draw_alpha_p
= vo_draw_alpha_rgb32
; break;
254 case 24: draw_alpha_p
= vo_draw_alpha_rgb24
; break;
255 case 16: draw_alpha_p
= vo_draw_alpha_rgb16
; break;
256 case 15: draw_alpha_p
= vo_draw_alpha_rgb15
; break;
257 case 12: draw_alpha_p
= vo_draw_alpha_rgb12
; break;
261 if (vo_config_count
== 0) {
262 if (ioctl(fb_dev_fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
263 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't get FSCREENINFO: %s\n", strerror(errno
));
267 if (fb_finfo
.type
!= FB_TYPE_PACKED_PIXELS
) {
268 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] type %d not supported\n", fb_finfo
.type
);
272 switch (fb_finfo
.visual
) {
273 case FB_VISUAL_TRUECOLOR
:
275 case FB_VISUAL_DIRECTCOLOR
:
276 mp_msg(MSGT_VO
, MSGL_V
, "[fbdev2] creating cmap for directcolor\n");
277 if (ioctl(fb_dev_fd
, FBIOGETCMAP
, &fb_oldcmap
)) {
278 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] can't get cmap: %s\n", strerror(errno
));
281 if (!(cmap
= make_directcolor_cmap(&fb_vinfo
)))
283 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, cmap
)) {
284 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] can't put cmap: %s\n", strerror(errno
));
294 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] visual: %d not yet supported\n", fb_finfo
.visual
);
298 fb_size
= fb_finfo
.smem_len
;
299 fb_line_len
= fb_finfo
.line_length
;
300 if ((frame_buffer
= (uint8_t *) mmap(0, fb_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fb_dev_fd
, 0)) == (uint8_t *) -1) {
301 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't mmap %s: %s\n", fb_dev_name
, strerror(errno
));
306 center
= frame_buffer
+
307 ( (out_width
- in_width
) / 2 ) * fb_pixel_size
+
308 ( (out_height
- in_height
) / 2 ) * fb_line_len
;
310 #ifndef USE_CONVERT2FB
311 if (!(next_frame
= realloc(next_frame
, in_width
* in_height
* fb_pixel_size
))) {
312 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't malloc next_frame: %s\n", strerror(errno
));
316 if (fs
) memset(frame_buffer
, '\0', fb_line_len
* fb_vinfo
.yres
);
321 static int query_format(uint32_t format
)
323 // open the device, etc.
324 if (fb_preinit(0)) return 0;
325 if ((format
& IMGFMT_BGR_MASK
) == IMGFMT_BGR
) {
326 int fb_target_bpp
= format
& 0xff;
327 set_bpp(&fb_vinfo
, fb_target_bpp
);
328 fb_vinfo
.xres_virtual
= fb_vinfo
.xres
;
329 fb_vinfo
.yres_virtual
= fb_vinfo
.yres
;
330 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
))
331 // Needed for Intel framebuffer with 32 bpp
332 fb_vinfo
.transp
.length
= fb_vinfo
.transp
.offset
= 0;
333 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
)) {
334 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't put VSCREENINFO: %s\n", strerror(errno
));
337 fb_pixel_size
= fb_vinfo
.bits_per_pixel
/ 8;
338 fb_bpp
= fb_vinfo
.bits_per_pixel
;
340 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+ fb_vinfo
.blue
.length
;
341 if (fb_bpp
== fb_target_bpp
)
342 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
|VFCAP_ACCEPT_STRIDE
;
347 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
,
348 unsigned char *srca
, int stride
)
353 #ifdef USE_CONVERT2FB
354 dst
= center
+ (fb_line_len
* y0
) + (x0
* fb_pixel_size
);
355 dstride
= fb_line_len
;
357 dst
= next_frame
+ (in_width
* y0
+ x0
) * fb_pixel_size
;
358 dstride
= in_width
* fb_pixel_size
;
360 (*draw_alpha_p
)(w
, h
, src
, srca
, stride
, dst
, dstride
);
363 static void draw_osd(void)
365 vo_draw_text(in_width
, in_height
, draw_alpha
);
368 // all csp support stride
369 static int draw_frame(uint8_t *src
[]) { return 1; }
371 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
, int y
)
373 uint8_t *in
= src
[0];
374 #ifdef USE_CONVERT2FB
375 uint8_t *dest
= center
+ (fb_line_len
* y
) + (x
* fb_pixel_size
);
376 int next
= fb_line_len
;
378 uint8_t *dest
= next_frame
+ (in_width
* y
+ x
) * fb_pixel_size
;
379 int next
= in_width
* fb_pixel_size
;
383 for (i
= 0; i
< h
; i
++) {
384 fast_memcpy(dest
, in
, w
* fb_pixel_size
);
391 static void check_events(void)
395 static void flip_page(void)
397 #ifndef USE_CONVERT2FB
398 int i
, out_offset
= 0, in_offset
= 0;
400 for (i
= 0; i
< in_height
; i
++) {
401 fast_memcpy(center
+ out_offset
, next_frame
+ in_offset
,
402 in_width
* fb_pixel_size
);
403 out_offset
+= fb_line_len
;
404 in_offset
+= in_width
* fb_pixel_size
;
409 static void uninit(void)
411 if (fb_cmap_changed
) {
412 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, &fb_oldcmap
))
413 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't restore original cmap\n");
416 if(next_frame
) free(next_frame
);
417 if (fb_dev_fd
>= 0) {
418 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_orig_vinfo
))
419 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't reset original fb_var_screeninfo: %s\n", strerror(errno
));
423 if(frame_buffer
) munmap(frame_buffer
, fb_size
);
424 next_frame
= frame_buffer
= NULL
;
425 fb_preinit(1); // so that later calls to preinit don't fail
428 static int control(uint32_t request
, void *data
, ...)
431 case VOCTRL_QUERY_FORMAT
:
432 return query_format(*((uint32_t*)data
));