2 * copyright (C) 2002 Mark Zealey <mark@zealos.org>
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 * 30/03/02: An almost total rewrite, added DR support and support for modes
23 * other than 16bpp. Fixed the crash when playing multiple files
24 * 07/04/02: Fixed DR support, added YUY2 support, fixed OSD stuff.
25 * 08/04/02: Fixed a wierd sound corruption problem caused by some optomizations
27 * 09/04/02: Fixed a problem with changing the variables passed to draw_slice().
28 * Fixed DR support for YV12 et al. Added BGR support. Removed lots of dud code.
29 * 10/04/02: Changed the memcpy functions to mem2agpcpy.. should be a tad
31 * 11/04/02: Added a compile option so you can watch the film with the console
32 * as the background, or not.
33 * 13/04/02: Fix rough OSD stuff by rendering it straight onto the output
34 * buffer. Added double-buffering. Supports hardware zoom/reduce zoom modes.
35 * 13/04/02: Misc cleanups of the code.
36 * 22/10/02: Added geometry support to it
39 * - Use -dr to get direct rendering
40 * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
41 * - To get a black background and nice smooth OSD, use -double
42 * - To get the console as a background, but with scaled OSD, use -nodouble
43 * - The driver supports both scaling and shrinking the image using the -x and
44 * -y options on the mplayer commandline. Also repositioning via the -geometry
53 #include <sys/ioctl.h>
61 #include "fastmemcpy.h"
62 #include "video_out.h"
63 #include "video_out_internal.h"
64 #include "drivers/3dfx.h"
68 static const vo_info_t info
=
70 "3Dfx Banshee/Voodoo3/Voodoo5",
72 "Mark Zealey <mark@zealos.org>",
76 const LIBVO_EXTERN(tdfxfb
)
78 /* Some registers on the card */
79 #define S2S_STRECH_BLT 2 // BLT + Strech
80 #define S2S_IMMED (1 << 8) // Do it immediatly
81 #define S2S_ROP (0xCC << 24) // ???
83 /* Stepping between the different YUV plane registers */
84 #define YUV_STRIDE 1024
92 static struct fb_fix_screeninfo fb_finfo
;
93 static struct fb_var_screeninfo fb_vinfo
;
94 static uint32_t in_width
, in_height
, in_format
, in_depth
, in_voodoo_format
,
95 screenwidth
, screenheight
, screendepth
, vidwidth
, vidheight
, vidx
, vidy
,
96 vid_voodoo_format
, *vidpage
, *hidpage
, *inpage
, vidpageoffset
,
97 hidpageoffset
, inpageoffset
, *memBase0
= NULL
, *memBase1
= NULL
, r_width
, r_height
;
98 static volatile voodoo_io_reg
*reg_IO
;
99 static volatile voodoo_2d_reg
*reg_2d
;
100 static voodoo_yuv_reg
*reg_YUV
;
101 static struct YUV_plane
*YUV
;
102 static void (*alpha_func
)(), (*alpha_func_double
)();
104 static int preinit(const char *arg
)
110 else if(!(name
= getenv("FRAMEBUFFER")))
113 if((fd
= open(name
, O_RDWR
)) == -1) {
114 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_CantOpen
, name
, strerror(errno
));
118 if(ioctl(fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
119 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ProblemWithFbitgetFscreenInfo
,
126 if(ioctl(fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
127 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ProblemWithFbitgetVscreenInfo
,
134 /* BANSHEE means any of the series aparently */
135 if (fb_finfo
.accel
!= FB_ACCEL_3DFX_BANSHEE
) {
136 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_ThisDriverOnlySupports
);
142 // Check the depth now as config() musn't fail
143 switch(fb_vinfo
.bits_per_pixel
) {
149 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_OutputIsNotSupported
, fb_vinfo
.bits_per_pixel
);
155 /* Open up a window to the hardware */
156 memBase1
= mmap(0, fb_finfo
.smem_len
, PROT_READ
| PROT_WRITE
,
158 memBase0
= mmap(0, fb_finfo
.mmio_len
, PROT_READ
| PROT_WRITE
,
159 MAP_SHARED
, fd
, fb_finfo
.smem_len
);
161 if((long)memBase0
== -1 || (long)memBase1
== -1) {
162 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_CouldntMapMemoryAreas
, strerror(errno
));
163 if((long)memBase0
!= -1)
164 munmap(memBase0
, fb_finfo
.smem_len
);
165 if((long)memBase1
!= -1)
166 munmap(memBase1
, fb_finfo
.smem_len
);
167 memBase0
= memBase1
= NULL
;
171 /* Set up global pointers to the voodoo's regs */
172 reg_IO
= (void *)memBase0
+ VOODOO_IO_REG_OFFSET
;
173 reg_2d
= (void *)memBase0
+ VOODOO_2D_REG_OFFSET
;
174 reg_YUV
= (void *)memBase0
+ VOODOO_YUV_REG_OFFSET
;
175 YUV
= (void *)memBase0
+ VOODOO_YUV_PLANE_OFFSET
;
180 static void uninit(void)
183 /* Restore the screen (Linux lives at 0) */
184 reg_IO
->vidDesktopStartAddr
= 0;
188 /* And close our mess */
190 munmap(memBase1
, fb_finfo
.smem_len
);
195 munmap(memBase0
, fb_finfo
.mmio_len
);
205 static void clear_screen()
207 /* There needs to be some sort of delay here or else things seriously
208 * screw up. Causes the image to not be the right size on screen if
209 * this isn't like this. A printf before the memset call also seems to
210 * work, but this made more sense since it actually checks the status of
213 if(vo_doublebuffering
) {
214 /* first wait for the card to be ready, do not try to write
215 * every time - alex */
216 do {} while((reg_IO
->status
& 0x1f) < 1);
217 memset(vidpage
, 0, screenwidth
* screenheight
* screendepth
);
218 memset(hidpage
, 0, screenwidth
* screenheight
* screendepth
);
222 /* Setup output screen dimensions etc */
223 static void setup_screen(uint32_t full
)
225 aspect(&vidwidth
, &vidheight
, full
? A_ZOOM
: A_NOZOOM
);
227 geometry(&vidx
, &vidy
, &vidwidth
, &vidheight
, screenwidth
, screenheight
);
232 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
,
233 uint32_t flags
, char *title
, uint32_t format
)
235 screenwidth
= fb_vinfo
.xres
;
236 screenheight
= fb_vinfo
.yres
;
237 aspect_save_screenres(fb_vinfo
.xres
,fb_vinfo
.yres
);
242 aspect_save_orig(width
,height
);
246 aspect_save_prescale(d_width
,d_height
);
248 /* Setup the screen for rendering to */
249 switch(fb_vinfo
.bits_per_pixel
) {
252 vid_voodoo_format
= VOODOO_BLT_FORMAT_16
;
253 alpha_func_double
= vo_draw_alpha_rgb16
;
258 vid_voodoo_format
= VOODOO_BLT_FORMAT_24
;
259 alpha_func_double
= vo_draw_alpha_rgb24
;
264 vid_voodoo_format
= VOODOO_BLT_FORMAT_32
;
265 alpha_func_double
= vo_draw_alpha_rgb32
;
269 mp_msg(MSGT_VO
, MSGL_ERR
, MSGTR_LIBVO_TDFXFB_BppOutputIsNotSupported
, fb_vinfo
.bits_per_pixel
);
273 vid_voodoo_format
|= screenwidth
* screendepth
;
275 /* Some defaults here */
276 in_voodoo_format
= VOODOO_BLT_FORMAT_YUYV
;
278 alpha_func
= vo_draw_alpha_yuy2
;
287 in_voodoo_format
= VOODOO_BLT_FORMAT_UYVY
;
290 in_voodoo_format
= VOODOO_BLT_FORMAT_16
;
291 alpha_func
= vo_draw_alpha_rgb16
;
296 in_voodoo_format
= VOODOO_BLT_FORMAT_24
;
297 alpha_func
= vo_draw_alpha_rgb24
;
302 in_voodoo_format
= VOODOO_BLT_FORMAT_32
;
303 alpha_func
= vo_draw_alpha_rgb32
;
307 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_SomethingIsWrongWithControl
);
311 in_voodoo_format
|= in_width
* in_depth
;
313 /* Linux lives in the first frame */
314 if(vo_doublebuffering
) {
315 vidpageoffset
= screenwidth
* screenheight
* screendepth
;
316 hidpageoffset
= vidpageoffset
+ screenwidth
* screenheight
* screendepth
;
318 vidpageoffset
= hidpageoffset
= 0; /* Console background */
321 inpageoffset
= hidpageoffset
+ screenwidth
* screenheight
* screendepth
;
323 if(inpageoffset
+ in_width
* in_depth
* in_height
> fb_finfo
.smem_len
) {
324 mp_msg(MSGT_VO
,MSGL_ERR
, MSGTR_LIBVO_TDFXFB_NotEnoughVideoMemoryToPlay
);
328 vidpage
= (void *)memBase1
+ (unsigned long)vidpageoffset
;
329 hidpage
= (void *)memBase1
+ (unsigned long)hidpageoffset
;
330 inpage
= (void *)memBase1
+ (unsigned long)inpageoffset
;
332 setup_screen(flags
& VOFLAG_FULLSCREEN
);
334 memset(inpage
, 0, in_width
* in_height
* in_depth
);
336 mp_msg(MSGT_VO
,MSGL_INFO
, MSGTR_LIBVO_TDFXFB_ScreenIs
,
337 screenwidth
, screenheight
, screendepth
* 8,
338 in_width
, in_height
, in_depth
* 8,
344 /* Double-buffering draw_alpha */
345 static void draw_alpha_double(int x
, int y
, int w
, int h
, unsigned char *src
,
346 unsigned char *srca
, int stride
)
348 char *dst
= (char *)vidpage
+ ((y
+ vidy
) * screenwidth
+ x
+ vidx
) * screendepth
;
349 alpha_func_double(w
, h
, src
, srca
, stride
, dst
, screenwidth
* screendepth
);
352 /* Single-buffering draw_alpha */
353 static void draw_alpha(int x
, int y
, int w
, int h
, unsigned char *src
,
354 unsigned char *srca
, int stride
)
356 char *dst
= (char *)inpage
+ (y
* in_width
+ x
) * in_depth
;
357 alpha_func(w
, h
, src
, srca
, stride
, dst
, in_width
* in_depth
);
360 static void draw_osd(void)
362 if(!vo_doublebuffering
)
363 vo_draw_text(in_width
, in_height
, draw_alpha
);
366 /* Render onto the screen */
367 static void flip_page(void)
369 voodoo_2d_reg regs
= *reg_2d
; /* Copy the regs */
372 if(vo_doublebuffering
) {
373 /* Flip to an offscreen buffer for rendering */
374 uint32_t t
= vidpageoffset
;
379 vidpageoffset
= hidpageoffset
;
383 reg_2d
->commandExtra
= 0;
384 reg_2d
->clip0Min
= 0;
385 reg_2d
->clip0Max
= 0xffffffff;
387 reg_2d
->srcBaseAddr
= inpageoffset
;
389 reg_2d
->srcFormat
= in_voodoo_format
;
390 reg_2d
->srcSize
= XYREG(in_width
, in_height
);
392 reg_2d
->dstBaseAddr
= vidpageoffset
;
393 reg_2d
->dstXY
= XYREG(vidx
, vidy
);
394 reg_2d
->dstFormat
= vid_voodoo_format
;
395 reg_2d
->dstSize
= XYREG(vidwidth
, vidheight
);
396 reg_2d
->command
= S2S_STRECH_BLT
| S2S_IMMED
| S2S_ROP
;
398 /* Wait for the command to finish (If we don't do this, we get wierd
399 * sound corruption... */
400 while((reg_IO
->status
& 0x1f) < 1)
403 *((volatile uint32_t *)((uint32_t *)reg_IO
+ COMMAND_3D
)) = COMMAND_3D_NOP
;
406 if(!(reg_IO
->status
& STATUS_BUSY
))
409 /* Restore the old regs now */
410 reg_2d
->commandExtra
= regs
.commandExtra
;
411 reg_2d
->clip0Min
= regs
.clip0Min
;
412 reg_2d
->clip0Max
= regs
.clip0Max
;
414 reg_2d
->srcBaseAddr
= regs
.srcBaseAddr
;
415 reg_2d
->srcXY
= regs
.srcXY
;
416 reg_2d
->srcFormat
= regs
.srcFormat
;
417 reg_2d
->srcSize
= regs
.srcSize
;
419 reg_2d
->dstBaseAddr
= regs
.dstBaseAddr
;
420 reg_2d
->dstXY
= regs
.dstXY
;
421 reg_2d
->dstFormat
= regs
.dstFormat
;
422 reg_2d
->dstSize
= regs
.dstSize
;
426 /* Render any text onto this buffer */
427 if(vo_doublebuffering
)
428 vo_draw_text(vidwidth
, vidheight
, draw_alpha_double
);
430 /* And flip to the new buffer! */
431 reg_IO
->vidDesktopStartAddr
= vidpageoffset
;
434 static int draw_frame(uint8_t *src
[])
436 mem2agpcpy(inpage
, src
[0], in_width
* in_depth
* in_height
);
440 static int draw_slice(uint8_t *i
[], int s
[], int w
, int h
, int x
, int y
)
442 /* We want to render to the YUV to the input page + the location
443 * of the stripes we're doing */
444 reg_YUV
->yuvBaseAddr
= inpageoffset
+ in_width
* in_depth
* y
+ x
;
445 reg_YUV
->yuvStride
= in_width
* in_depth
;
447 /* Put the YUV channels into the voodoos internal combiner unit
449 mem2agpcpy_pic(YUV
->Y
, i
[0], s
[0], h
, YUV_STRIDE
, s
[0]);
450 mem2agpcpy_pic(YUV
->U
, i
[1], s
[1], h
/ 2, YUV_STRIDE
, s
[1]);
451 mem2agpcpy_pic(YUV
->V
, i
[2], s
[2], h
/ 2, YUV_STRIDE
, s
[2]);
455 /* Attempt to start doing DR */
456 static uint32_t get_image(mp_image_t
*mpi
)
459 if(mpi
->flags
& MP_IMGFLAG_READABLE
)
461 if(mpi
->type
== MP_IMGTYPE_STATIC
&& vo_doublebuffering
)
463 if(mpi
->type
> MP_IMGTYPE_TEMP
)
464 return VO_FALSE
; // TODO ??
472 mpi
->planes
[0] = (char *)inpage
;
473 mpi
->stride
[0] = in_width
* in_depth
;
479 if(!(mpi
->flags
& MP_IMGFLAG_ACCEPT_STRIDE
) && mpi
->w
!= YUV_STRIDE
)
481 mpi
->planes
[0] = YUV
->Y
;
482 mpi
->planes
[1] = YUV
->U
;
483 mpi
->planes
[2] = YUV
->V
;
484 mpi
->stride
[0] = mpi
->stride
[1] = mpi
->stride
[2] = YUV_STRIDE
;
485 reg_YUV
->yuvBaseAddr
= inpageoffset
;
486 reg_YUV
->yuvStride
= in_width
* in_depth
;
493 mpi
->width
= in_width
;
494 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
499 static int control(uint32_t request
, void *data
)
502 case VOCTRL_GET_IMAGE
:
503 return get_image(data
);
505 case VOCTRL_QUERY_FORMAT
:
506 switch(*((uint32_t*)data
)) {
515 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
|
516 VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
519 return 0; /* Not supported */
521 case VOCTRL_FULLSCREEN
:
522 setup_screen(!vo_fs
);
530 static void check_events(void) {}