1 /* Copyright (C) Mark Zealey, 2002, <mark@zealos.org>. Released under the terms
2 * and conditions of the GPL.
4 * 30/03/02: An almost total rewrite, added DR support and support for modes
5 * other than 16bpp. Fixed the crash when playing multiple files
6 * 07/04/02: Fixed DR support, added YUY2 support, fixed OSD stuff.
7 * 08/04/02: Fixed a wierd sound corruption problem caused by some optomizations
9 * 09/04/02: Fixed a problem with changing the variables passed to draw_slice().
10 * Fixed DR support for YV12 et al. Added BGR support. Removed lots of dud code.
11 * 10/04/02: Changed the memcpy functions to mem2agpcpy.. should be a tad
13 * 11/04/02: Added a compile option so you can watch the film with the console
14 * as the background, or not.
15 * 13/04/02: Fix rough OSD stuff by rendering it straight onto the output
16 * buffer. Added double-buffering. Supports hardware zoom/reduce zoom modes.
17 * 13/04/02: Misc cleanups of the code.
18 * 22/10/02: Added geometry support to it
21 * - Use -dr to get direct rendering
22 * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
23 * - To get a black background and nice smooth OSD, use -double
24 * - To get the console as a background, but with scaled OSD, use -nodouble
25 * - The driver supports both scaling and shrinking the image using the -x and
26 * -y options on the mplayer commandline. Also repositioning via the -geometry
35 #include <sys/ioctl.h>
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
46 #include "drivers/3dfx.h"
50 static const vo_info_t info
=
52 "3Dfx Banshee/Voodoo3/Voodoo5",
54 "Mark Zealey <mark@zealos.org>",
58 const LIBVO_EXTERN(tdfxfb
)
60 /* Some registers on the card */
61 #define S2S_STRECH_BLT 2 // BLT + Strech
62 #define S2S_IMMED (1 << 8) // Do it immediatly
63 #define S2S_ROP (0xCC << 24) // ???
65 /* Stepping between the different YUV plane registers */
66 #define YUV_STRIDE 1024
74 static struct fb_fix_screeninfo fb_finfo
;
75 static struct fb_var_screeninfo fb_vinfo
;
76 static uint32_t in_width
, in_height
, in_format
, in_depth
, in_voodoo_format
,
77 screenwidth
, screenheight
, screendepth
, vidwidth
, vidheight
, vidx
, vidy
,
78 vid_voodoo_format
, *vidpage
, *hidpage
, *inpage
, vidpageoffset
,
79 hidpageoffset
, inpageoffset
, *memBase0
= NULL
, *memBase1
= NULL
, r_width
, r_height
;
80 static volatile voodoo_io_reg
*reg_IO
;
81 static volatile voodoo_2d_reg
*reg_2d
;
82 static voodoo_yuv_reg
*reg_YUV
;
83 static struct YUV_plane
*YUV
;
84 static void (*alpha_func
)(), (*alpha_func_double
)();
86 static int preinit(const char *arg
)
92 else if(!(name
= getenv("FRAMEBUFFER")))
95 if((fd
= open(name
, O_RDWR
)) == -1) {
96 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_CantOpen
, name
, strerror(errno
));
100 if(ioctl(fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
101 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ProblemWithFbitgetFscreenInfo
,
108 if(ioctl(fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
109 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ProblemWithFbitgetVscreenInfo
,
116 /* BANSHEE means any of the series aparently */
117 if (fb_finfo
.accel
!= FB_ACCEL_3DFX_BANSHEE
) {
118 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ThisDriverOnlySupports
);
124 // Check the depth now as config() musn't fail
125 switch(fb_vinfo
.bits_per_pixel
) {
131 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_OutputIsNotSupported
, fb_vinfo
.bits_per_pixel
);
137 /* Open up a window to the hardware */
138 memBase1
= mmap(0, fb_finfo
.smem_len
, PROT_READ
| PROT_WRITE
,
140 memBase0
= mmap(0, fb_finfo
.mmio_len
, PROT_READ
| PROT_WRITE
,
141 MAP_SHARED
, fd
, fb_finfo
.smem_len
);
143 if((long)memBase0
== -1 || (long)memBase1
== -1) {
144 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_CouldntMapMemoryAreas
, strerror(errno
));
145 if((long)memBase0
!= -1)
146 munmap(memBase0
, fb_finfo
.smem_len
);
147 if((long)memBase1
!= -1)
148 munmap(memBase1
, fb_finfo
.smem_len
);
149 memBase0
= memBase1
= NULL
;
153 /* Set up global pointers to the voodoo's regs */
154 reg_IO
= (void *)memBase0
+ VOODOO_IO_REG_OFFSET
;
155 reg_2d
= (void *)memBase0
+ VOODOO_2D_REG_OFFSET
;
156 reg_YUV
= (void *)memBase0
+ VOODOO_YUV_REG_OFFSET
;
157 YUV
= (void *)memBase0
+ VOODOO_YUV_PLANE_OFFSET
;
162 static void uninit(void)
165 /* Restore the screen (Linux lives at 0) */
166 reg_IO
->vidDesktopStartAddr
= 0;
170 /* And close our mess */
172 munmap(memBase1
, fb_finfo
.smem_len
);
177 munmap(memBase0
, fb_finfo
.mmio_len
);
187 static void clear_screen()
189 /* There needs to be some sort of delay here or else things seriously
190 * screw up. Causes the image to not be the right size on screen if
191 * this isn't like this. A printf before the memset call also seems to
192 * work, but this made more sense since it actually checks the status of
195 if(vo_doublebuffering
) {
196 /* first wait for the card to be ready, do not try to write
197 * every time - alex */
198 do {} while((reg_IO
->status
& 0x1f) < 1);
199 memset(vidpage
, 0, screenwidth
* screenheight
* screendepth
);
200 memset(hidpage
, 0, screenwidth
* screenheight
* screendepth
);
204 /* Setup output screen dimensions etc */
205 static void setup_screen(uint32_t full
)
207 aspect(&vidwidth
, &vidheight
, full
? A_ZOOM
: A_NOZOOM
);
209 geometry(&vidx
, &vidy
, &vidwidth
, &vidheight
, screenwidth
, screenheight
);
214 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
,
215 uint32_t flags
, char *title
, uint32_t format
)
217 screenwidth
= fb_vinfo
.xres
;
218 screenheight
= fb_vinfo
.yres
;
219 aspect_save_screenres(fb_vinfo
.xres
,fb_vinfo
.yres
);
224 aspect_save_orig(width
,height
);
228 aspect_save_prescale(d_width
,d_height
);
230 /* Setup the screen for rendering to */
231 switch(fb_vinfo
.bits_per_pixel
) {
234 vid_voodoo_format
= VOODOO_BLT_FORMAT_16
;
235 alpha_func_double
= vo_draw_alpha_rgb16
;
240 vid_voodoo_format
= VOODOO_BLT_FORMAT_24
;
241 alpha_func_double
= vo_draw_alpha_rgb24
;
246 vid_voodoo_format
= VOODOO_BLT_FORMAT_32
;
247 alpha_func_double
= vo_draw_alpha_rgb32
;
251 mp_msg(MSGT_VO
, MSGL_ERR
, MSGTR_LIBVO_TDFXFB_BppOutputIsNotSupported
, fb_vinfo
.bits_per_pixel
);
255 vid_voodoo_format
|= screenwidth
* screendepth
;
257 /* Some defaults here */
258 in_voodoo_format
= VOODOO_BLT_FORMAT_YUYV
;
260 alpha_func
= vo_draw_alpha_yuy2
;
269 in_voodoo_format
= VOODOO_BLT_FORMAT_UYVY
;
272 in_voodoo_format
= VOODOO_BLT_FORMAT_16
;
273 alpha_func
= vo_draw_alpha_rgb16
;
278 in_voodoo_format
= VOODOO_BLT_FORMAT_24
;
279 alpha_func
= vo_draw_alpha_rgb24
;
284 in_voodoo_format
= VOODOO_BLT_FORMAT_32
;
285 alpha_func
= vo_draw_alpha_rgb32
;
289 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_SomethingIsWrongWithControl
);
293 in_voodoo_format
|= in_width
* in_depth
;
295 /* Linux lives in the first frame */
296 if(vo_doublebuffering
) {
297 vidpageoffset
= screenwidth
* screenheight
* screendepth
;
298 hidpageoffset
= vidpageoffset
+ screenwidth
* screenheight
* screendepth
;
300 vidpageoffset
= hidpageoffset
= 0; /* Console background */
303 inpageoffset
= hidpageoffset
+ screenwidth
* screenheight
* screendepth
;
305 if(inpageoffset
+ in_width
* in_depth
* in_height
> fb_finfo
.smem_len
) {
306 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_NotEnoughVideoMemoryToPlay
);
310 vidpage
= (void *)memBase1
+ (unsigned long)vidpageoffset
;
311 hidpage
= (void *)memBase1
+ (unsigned long)hidpageoffset
;
312 inpage
= (void *)memBase1
+ (unsigned long)inpageoffset
;
314 setup_screen(flags
& VOFLAG_FULLSCREEN
);
316 memset(inpage
, 0, in_width
* in_height
* in_depth
);
318 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXFB_ScreenIs
,
319 screenwidth
, screenheight
, screendepth
* 8,
320 in_width
, in_height
, in_depth
* 8,
326 /* Double-buffering draw_alpha */
327 static void draw_alpha_double(int x
, int y
, int w
, int h
, unsigned char *src
,
328 unsigned char *srca
, int stride
)
330 char *dst
= (char *)vidpage
+ ((y
+ vidy
) * screenwidth
+ x
+ vidx
) * screendepth
;
331 alpha_func_double(w
, h
, src
, srca
, stride
, dst
, screenwidth
* screendepth
);
334 /* Single-buffering draw_alpha */
335 static void draw_alpha(int x
, int y
, int w
, int h
, unsigned char *src
,
336 unsigned char *srca
, int stride
)
338 char *dst
= (char *)inpage
+ (y
* in_width
+ x
) * in_depth
;
339 alpha_func(w
, h
, src
, srca
, stride
, dst
, in_width
* in_depth
);
342 static void draw_osd(void)
344 if(!vo_doublebuffering
)
345 vo_draw_text(in_width
, in_height
, draw_alpha
);
348 /* Render onto the screen */
349 static void flip_page(void)
351 voodoo_2d_reg regs
= *reg_2d
; /* Copy the regs */
354 if(vo_doublebuffering
) {
355 /* Flip to an offscreen buffer for rendering */
356 uint32_t t
= vidpageoffset
;
361 vidpageoffset
= hidpageoffset
;
365 reg_2d
->commandExtra
= 0;
366 reg_2d
->clip0Min
= 0;
367 reg_2d
->clip0Max
= 0xffffffff;
369 reg_2d
->srcBaseAddr
= inpageoffset
;
371 reg_2d
->srcFormat
= in_voodoo_format
;
372 reg_2d
->srcSize
= XYREG(in_width
, in_height
);
374 reg_2d
->dstBaseAddr
= vidpageoffset
;
375 reg_2d
->dstXY
= XYREG(vidx
, vidy
);
376 reg_2d
->dstFormat
= vid_voodoo_format
;
377 reg_2d
->dstSize
= XYREG(vidwidth
, vidheight
);
378 reg_2d
->command
= S2S_STRECH_BLT
| S2S_IMMED
| S2S_ROP
;
380 /* Wait for the command to finish (If we don't do this, we get wierd
381 * sound corruption... */
382 while((reg_IO
->status
& 0x1f) < 1)
385 *((volatile uint32_t *)((uint32_t *)reg_IO
+ COMMAND_3D
)) = COMMAND_3D_NOP
;
388 if(!(reg_IO
->status
& STATUS_BUSY
))
391 /* Restore the old regs now */
392 reg_2d
->commandExtra
= regs
.commandExtra
;
393 reg_2d
->clip0Min
= regs
.clip0Min
;
394 reg_2d
->clip0Max
= regs
.clip0Max
;
396 reg_2d
->srcBaseAddr
= regs
.srcBaseAddr
;
397 reg_2d
->srcXY
= regs
.srcXY
;
398 reg_2d
->srcFormat
= regs
.srcFormat
;
399 reg_2d
->srcSize
= regs
.srcSize
;
401 reg_2d
->dstBaseAddr
= regs
.dstBaseAddr
;
402 reg_2d
->dstXY
= regs
.dstXY
;
403 reg_2d
->dstFormat
= regs
.dstFormat
;
404 reg_2d
->dstSize
= regs
.dstSize
;
408 /* Render any text onto this buffer */
409 if(vo_doublebuffering
)
410 vo_draw_text(vidwidth
, vidheight
, draw_alpha_double
);
412 /* And flip to the new buffer! */
413 reg_IO
->vidDesktopStartAddr
= vidpageoffset
;
416 static int draw_frame(uint8_t *src
[])
418 mem2agpcpy(inpage
, src
[0], in_width
* in_depth
* in_height
);
422 static int draw_slice(uint8_t *i
[], int s
[], int w
, int h
, int x
, int y
)
424 /* We want to render to the YUV to the input page + the location
425 * of the stripes we're doing */
426 reg_YUV
->yuvBaseAddr
= inpageoffset
+ in_width
* in_depth
* y
+ x
;
427 reg_YUV
->yuvStride
= in_width
* in_depth
;
429 /* Put the YUV channels into the voodoos internal combiner unit
431 mem2agpcpy_pic(YUV
->Y
, i
[0], s
[0], h
, YUV_STRIDE
, s
[0]);
432 mem2agpcpy_pic(YUV
->U
, i
[1], s
[1], h
/ 2, YUV_STRIDE
, s
[1]);
433 mem2agpcpy_pic(YUV
->V
, i
[2], s
[2], h
/ 2, YUV_STRIDE
, s
[2]);
437 /* Attempt to start doing DR */
438 static uint32_t get_image(mp_image_t
*mpi
)
441 if(mpi
->flags
& MP_IMGFLAG_READABLE
)
443 if(mpi
->type
== MP_IMGTYPE_STATIC
&& vo_doublebuffering
)
445 if(mpi
->type
> MP_IMGTYPE_TEMP
)
446 return VO_FALSE
; // TODO ??
454 mpi
->planes
[0] = (char *)inpage
;
455 mpi
->stride
[0] = in_width
* in_depth
;
461 if(!(mpi
->flags
& MP_IMGFLAG_ACCEPT_STRIDE
) && mpi
->w
!= YUV_STRIDE
)
463 mpi
->planes
[0] = YUV
->Y
;
464 mpi
->planes
[1] = YUV
->U
;
465 mpi
->planes
[2] = YUV
->V
;
466 mpi
->stride
[0] = mpi
->stride
[1] = mpi
->stride
[2] = YUV_STRIDE
;
467 reg_YUV
->yuvBaseAddr
= inpageoffset
;
468 reg_YUV
->yuvStride
= in_width
* in_depth
;
475 mpi
->width
= in_width
;
476 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
481 static int control(uint32_t request
, void *data
)
484 case VOCTRL_GET_IMAGE
:
485 return get_image(data
);
487 case VOCTRL_QUERY_FORMAT
:
488 switch(*((uint32_t*)data
)) {
497 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
|
498 VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
501 return 0; /* Not supported */
503 case VOCTRL_FULLSCREEN
:
504 setup_screen(!vo_fs
);
512 static void check_events(void) {}