2 * Video driver for Framebuffer device
3 * by Joey Parrish <joey@nicewarrior.org>
15 #include <sys/ioctl.h>
19 #include "video_out.h"
20 #include "video_out_internal.h"
21 #include "fastmemcpy.h"
25 static vo_info_t info
= {
28 "Joey Parrish <joey@nicewarrior.org>",
34 static void set_bpp(struct fb_var_screeninfo
*p
, int bpp
)
36 p
->bits_per_pixel
= (bpp
+ 1) & ~1;
37 p
->red
.msb_right
= p
->green
.msb_right
= p
->blue
.msb_right
= p
->transp
.msb_right
= 0;
38 p
->transp
.offset
= p
->transp
.length
= 0;
42 p
->transp
.offset
= 24;
68 static char *fb_dev_name
= NULL
; // such as /dev/fb0
69 static int fb_dev_fd
; // handle for fb_dev_name
70 static uint8_t *frame_buffer
= NULL
; // mmap'd access to fbdev
71 static uint8_t *center
= NULL
; // where to begin writing our image (centered?)
72 static struct fb_fix_screeninfo fb_finfo
; // fixed info
73 static struct fb_var_screeninfo fb_vinfo
; // variable info
74 static struct fb_var_screeninfo fb_orig_vinfo
; // variable info to restore later
75 static unsigned short fb_ored
[256], fb_ogreen
[256], fb_oblue
[256];
76 static struct fb_cmap fb_oldcmap
= { 0, 256, fb_ored
, fb_ogreen
, fb_oblue
};
77 static int fb_cmap_changed
= 0; // to restore map
78 static int fb_pixel_size
; // 32: 4 24: 3 16: 2 15: 2
79 static int fb_bpp
; // 32: 32 24: 24 16: 16 15: 15
80 static size_t fb_size
; // size of frame_buffer
81 static int fb_line_len
; // length of one line in bytes
82 static void (*draw_alpha_p
)(int w
, int h
, unsigned char *src
,
83 unsigned char *srca
, int stride
, unsigned char *dst
,
86 static uint8_t *next_frame
= NULL
; // for double buffering
90 static int out_height
;
92 static struct fb_cmap
*make_directcolor_cmap(struct fb_var_screeninfo
*var
)
94 int i
, cols
, rcols
, gcols
, bcols
;
95 uint16_t *red
, *green
, *blue
;
98 rcols
= 1 << var
->red
.length
;
99 gcols
= 1 << var
->green
.length
;
100 bcols
= 1 << var
->blue
.length
;
102 /* Make our palette the length of the deepest color */
103 cols
= (rcols
> gcols
? rcols
: gcols
);
104 cols
= (cols
> bcols
? cols
: bcols
);
106 red
= malloc(cols
* sizeof(red
[0]));
108 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate red palette with %d entries.\n", cols
);
111 for(i
=0; i
< rcols
; i
++)
112 red
[i
] = (65535/(rcols
-1)) * i
;
114 green
= malloc(cols
* sizeof(green
[0]));
116 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate green palette with %d entries.\n", cols
);
120 for(i
=0; i
< gcols
; i
++)
121 green
[i
] = (65535/(gcols
-1)) * i
;
123 blue
= malloc(cols
* sizeof(blue
[0]));
125 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate blue palette with %d entries.\n", cols
);
130 for(i
=0; i
< bcols
; i
++)
131 blue
[i
] = (65535/(bcols
-1)) * i
;
133 cmap
= malloc(sizeof(struct fb_cmap
));
135 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't allocate color map\n");
152 static int fb_preinit(int reset
)
154 static int fb_preinit_done
= 0;
155 static int fb_err
= -1;
166 if (!fb_dev_name
&& !(fb_dev_name
= getenv("FRAMEBUFFER")))
167 fb_dev_name
= strdup("/dev/fb0");
169 mp_msg(MSGT_VO
, MSGL_V
, "[fbdev2] Using device %s\n", fb_dev_name
);
171 if ((fb_dev_fd
= open(fb_dev_name
, O_RDWR
)) == -1) {
172 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't open %s: %s\n", fb_dev_name
, strerror(errno
));
175 if (ioctl(fb_dev_fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
176 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't get VSCREENINFO: %s\n", strerror(errno
));
179 fb_orig_vinfo
= fb_vinfo
;
181 fb_bpp
= fb_vinfo
.bits_per_pixel
;
183 /* 16 and 15 bpp is reported as 16 bpp */
185 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+
186 fb_vinfo
.blue
.length
;
191 if (fb_dev_fd
>= 0) close(fb_dev_fd
);
197 static int preinit(const char *subdevice
)
201 if (fb_dev_name
) free(fb_dev_name
);
202 fb_dev_name
= strdup(subdevice
);
204 return fb_preinit(0);
207 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
,
208 uint32_t d_height
, uint32_t flags
, char *title
,
211 struct fb_cmap
*cmap
;
212 int fs
= flags
& VOFLAG_FULLSCREEN
;
220 out_width
= fb_vinfo
.xres
;
221 out_height
= fb_vinfo
.yres
;
224 if (out_width
< in_width
|| out_height
< in_height
) {
225 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Screensize is smaller than video size (%dx%d < %dx%d)\n",
226 out_width
, out_height
, in_width
, in_height
);
231 case 32: draw_alpha_p
= vo_draw_alpha_rgb32
; break;
232 case 24: draw_alpha_p
= vo_draw_alpha_rgb24
; break;
233 case 16: draw_alpha_p
= vo_draw_alpha_rgb16
; break;
234 case 15: draw_alpha_p
= vo_draw_alpha_rgb15
; break;
238 if (vo_config_count
== 0) {
239 if (ioctl(fb_dev_fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
240 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't get FSCREENINFO: %s\n", strerror(errno
));
244 if (fb_finfo
.type
!= FB_TYPE_PACKED_PIXELS
) {
245 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] type %d not supported\n", fb_finfo
.type
);
249 switch (fb_finfo
.visual
) {
250 case FB_VISUAL_TRUECOLOR
:
252 case FB_VISUAL_DIRECTCOLOR
:
253 mp_msg(MSGT_VO
, MSGL_V
, "[fbdev2] creating cmap for directcolor\n");
254 if (ioctl(fb_dev_fd
, FBIOGETCMAP
, &fb_oldcmap
)) {
255 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] can't get cmap: %s\n", strerror(errno
));
258 if (!(cmap
= make_directcolor_cmap(&fb_vinfo
)))
260 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, cmap
)) {
261 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] can't put cmap: %s\n", strerror(errno
));
271 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] visual: %d not yet supported\n", fb_finfo
.visual
);
275 fb_size
= fb_finfo
.smem_len
;
276 fb_line_len
= fb_finfo
.line_length
;
277 if ((frame_buffer
= (uint8_t *) mmap(0, fb_size
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fb_dev_fd
, 0)) == (uint8_t *) -1) {
278 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't mmap %s: %s\n", fb_dev_name
, strerror(errno
));
283 center
= frame_buffer
+
284 ( (out_width
- in_width
) / 2 ) * fb_pixel_size
+
285 ( (out_height
- in_height
) / 2 ) * fb_line_len
;
287 #ifndef USE_CONVERT2FB
288 if (!(next_frame
= (uint8_t *) realloc(next_frame
, in_width
* in_height
* fb_pixel_size
))) {
289 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't malloc next_frame: %s\n", strerror(errno
));
293 if (fs
) memset(frame_buffer
, '\0', fb_line_len
* fb_vinfo
.yres
);
298 static int query_format(uint32_t format
)
300 // open the device, etc.
301 if (fb_preinit(0)) return 0;
302 if ((format
& IMGFMT_BGR_MASK
) == IMGFMT_BGR
) {
303 int fb_target_bpp
= format
& 0xff;
304 set_bpp(&fb_vinfo
, fb_target_bpp
);
305 fb_vinfo
.xres_virtual
= fb_vinfo
.xres
;
306 fb_vinfo
.yres_virtual
= fb_vinfo
.yres
;
307 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_vinfo
)) {
308 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't put VSCREENINFO: %s\n", strerror(errno
));
311 fb_pixel_size
= fb_vinfo
.bits_per_pixel
/ 8;
312 fb_bpp
= fb_vinfo
.red
.length
+ fb_vinfo
.green
.length
+
313 fb_vinfo
.blue
.length
+ fb_vinfo
.transp
.length
;
314 if (fb_bpp
== fb_target_bpp
)
315 return VFCAP_CSP_SUPPORTED
|VFCAP_CSP_SUPPORTED_BY_HW
|VFCAP_ACCEPT_STRIDE
;
320 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
,
321 unsigned char *srca
, int stride
)
326 #ifdef USE_CONVERT2FB
327 dst
= center
+ (fb_line_len
* y0
) + (x0
* fb_pixel_size
);
328 dstride
= fb_line_len
;
330 dst
= next_frame
+ (in_width
* y0
+ x0
) * fb_pixel_size
;
331 dstride
= in_width
* fb_pixel_size
;
333 (*draw_alpha_p
)(w
, h
, src
, srca
, stride
, dst
, dstride
);
336 static void draw_osd(void)
338 vo_draw_text(in_width
, in_height
, draw_alpha
);
341 // all csp support stride
342 static int draw_frame(uint8_t *src
[]) { return 1; }
344 static int draw_slice(uint8_t *src
[], int stride
[], int w
, int h
, int x
, int y
)
346 uint8_t *in
= src
[0];
347 #ifdef USE_CONVERT2FB
348 uint8_t *dest
= center
+ (fb_line_len
* y
) + (x
* fb_pixel_size
);
349 int next
= fb_line_len
;
351 uint8_t *dest
= next_frame
+ (in_width
* y
+ x
) * fb_pixel_size
;
352 int next
= in_width
* fb_pixel_size
;
356 for (i
= 0; i
< h
; i
++) {
357 memcpy(dest
, in
, w
* fb_pixel_size
);
364 static void check_events(void)
368 static void flip_page(void)
370 #ifndef USE_CONVERT2FB
371 int i
, out_offset
= 0, in_offset
= 0;
373 for (i
= 0; i
< in_height
; i
++) {
374 memcpy(center
+ out_offset
, next_frame
+ in_offset
,
375 in_width
* fb_pixel_size
);
376 out_offset
+= fb_line_len
;
377 in_offset
+= in_width
* fb_pixel_size
;
382 static void uninit(void)
384 if (fb_cmap_changed
) {
385 if (ioctl(fb_dev_fd
, FBIOPUTCMAP
, &fb_oldcmap
))
386 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't restore original cmap\n");
389 if(next_frame
) free(next_frame
);
390 if (fb_dev_fd
>= 0) {
391 if (ioctl(fb_dev_fd
, FBIOPUT_VSCREENINFO
, &fb_orig_vinfo
))
392 mp_msg(MSGT_VO
, MSGL_ERR
, "[fbdev2] Can't reset original fb_var_screeninfo: %s\n", strerror(errno
));
396 if(frame_buffer
) munmap(frame_buffer
, fb_size
);
397 next_frame
= frame_buffer
= NULL
;
398 fb_preinit(1); // so that later calls to preinit don't fail
401 static int control(uint32_t request
, void *data
, ...)
404 case VOCTRL_QUERY_FORMAT
:
405 return query_format(*((uint32_t*)data
));