4 * Copyright (C) Alban Bedel - 03/2003
6 * This file is part of MPlayer, a free movie player.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with GNU Make; see the file COPYING. If not, write to
20 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <sys/types.h>
32 #include <sys/ioctl.h>
35 #include "video_out.h"
36 #include "video_out_internal.h"
40 #include "fastmemcpy.h"
41 #include "drivers/tdfx_vid.h"
44 static vo_info_t info
=
54 LIBVO_EXTERN(tdfx_vid
)
56 static tdfx_vid_config_t tdfx_cfg
;
58 static unsigned char* agp_mem
= NULL
;
59 static int tdfx_fd
= -1;
61 static uint32_t img_fmt
; // The real input format
62 static uint32_t src_width
, src_height
, src_fmt
, src_bpp
, src_stride
;
63 static uint32_t dst_width
, dst_height
, dst_fmt
, dst_bpp
, dst_stride
;
65 static uint32_t tdfx_page
;
66 static uint32_t front_buffer
;
67 static uint32_t back_buffer
;
68 static uint8_t num_buffer
= 3;
69 static uint32_t buffer_size
; // Max size
70 static uint8_t current_buffer
= 0;
71 static uint8_t current_ip_buf
= 0;
72 static uint32_t buffer_stride
[3];
74 static int use_overlay
= 1;
75 static tdfx_vid_overlay_t tdfx_ov
;
78 static void clear_screen(void) {
79 tdfx_vid_agp_move_t mov
;
81 memset(agp_mem
,0,tdfx_cfg
.screen_width
*dst_bpp
*tdfx_cfg
.screen_height
);
83 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
84 mov
.width
= tdfx_cfg
.screen_width
*dst_bpp
;
85 mov
.height
= tdfx_cfg
.screen_height
;
87 mov
.src_stride
= tdfx_cfg
.screen_width
*dst_bpp
;
88 mov
.dst
= front_buffer
;
89 mov
.dst_stride
= tdfx_cfg
.screen_stride
;
91 printf("Move %d(%d) x %d => %d \n", mov
.width
,mov
.src_stride
,mov
.height
,mov
.dst_stride
);
93 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
94 printf("tdfx_vid: AGP move failed to clear the screen\n");
98 static uint32_t draw_slice(uint8_t *image
[], int stride
[], int w
,int h
,int x
,int y
)
103 printf("Draw slices %d\n",current_buffer
);
112 // copy :( to agp_mem
113 // still faster than tdfxfb wich directly copy to the video mem :)
114 mem2agpcpy_pic(agp_mem
+ current_buffer
* buffer_size
+
115 y
*buffer_stride
[0] + x
* src_bpp
,
117 src_bpp
*w
,h
,buffer_stride
[0],stride
[0]);
123 ptr
[0] = agp_mem
+ current_buffer
* buffer_size
;
124 mem2agpcpy_pic(ptr
[0] + y
* buffer_stride
[0] + x
,image
[0],w
,h
,
125 buffer_stride
[0],stride
[0]);
126 ptr
[1] = ptr
[0] + (src_height
*src_width
);
127 mem2agpcpy_pic(ptr
[1] + y
/2 * buffer_stride
[1] + x
/2,image
[1],w
/2,h
/2,
128 buffer_stride
[1],stride
[1]);
129 ptr
[2] = ptr
[1] + (src_height
*src_width
/4);
130 mem2agpcpy_pic(ptr
[2] + y
/2 * buffer_stride
[2] + x
/2,image
[2],w
/2,h
/2,
131 buffer_stride
[2],stride
[2]);
138 static void draw_osd(void)
145 tdfx_vid_blit_t blit
;
152 // TDFX_VID_OVERLAY_ON does nothing if the overlay is alredy on
153 if(!ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) { // X11 killed the overlay :(
154 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
155 mp_msg(MSGT_VO
, MSGL_ERR
, "tdfx_vid: set_overlay failed\n");
157 // These formats need conversion
162 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
163 blit
.src
= back_buffer
;
164 blit
.src_stride
= src_stride
;
167 blit
.src_w
= src_width
;
168 blit
.src_h
= src_height
;
169 blit
.src_format
= src_fmt
;
171 blit
.dst
= front_buffer
;
172 blit
.dst_stride
= dst_stride
;
175 blit
.dst_w
= src_width
;
176 blit
.dst_h
= src_height
;
177 blit
.dst_format
= IMGFMT_BGR16
;
178 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
179 printf("tdfx_vid: Blit failed\n");
183 memset(&blit
,0,sizeof(tdfx_vid_blit_t
));
184 blit
.src
= back_buffer
;
185 blit
.src_stride
= src_stride
;
188 blit
.src_w
= src_width
;
189 blit
.src_h
= src_height
;
190 blit
.src_format
= src_fmt
;
192 blit
.dst
= front_buffer
;
193 blit
.dst_stride
= dst_stride
;
196 blit
.dst_w
= dst_width
;
197 blit
.dst_h
= dst_height
;
198 blit
.dst_format
= dst_fmt
;
200 if(ioctl(tdfx_fd
,TDFX_VID_BLIT
,&blit
))
201 printf("tdfx_vid: Blit failed\n");
205 draw_frame(uint8_t *src
[])
207 int stride
[] = { src_stride
, 0, 0};
208 return draw_slice(src
,stride
,src_width
, src_height
,0,0);
212 query_format(uint32_t format
)
216 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
217 return 3 | VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
227 if(tdfx_cfg
.screen_format
== TDFX_VID_FORMAT_BGR8
)
229 return 3 | VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
235 config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t fullscreen
, char *title
, uint32_t format
)
240 // When we are run as sub vo we must follow the size gaven to us
241 if(!(fullscreen
& VOFLAG_XOVERLAY_SUB_VO
)) {
243 vo_screenwidth
= tdfx_cfg
.screen_width
;
245 vo_screenheight
= tdfx_cfg
.screen_height
;
247 aspect_save_orig(width
,height
);
248 aspect_save_prescale(d_width
,d_height
);
249 aspect_save_screenres(vo_screenwidth
,vo_screenheight
);
251 if(fullscreen
&0x01) { /* -fs */
252 aspect(&d_width
,&d_height
,A_ZOOM
);
255 aspect(&d_width
,&d_height
,A_NOZOOM
);
262 buffer_stride
[0] = 0;
269 printf("tdfx_vid: Non-native overlay format need conversion\n");
272 src_bpp
= ((format
& 0x3F)+7)/8;
276 buffer_size
= src_width
* src_height
* 3 / 2;
277 buffer_stride
[0] = ((src_width
+1)/2)*2;
278 buffer_stride
[1] = buffer_stride
[2] = buffer_stride
[0]/2;
279 src_fmt
= TDFX_VID_FORMAT_YUY2
;
285 printf("Unsupported input format 0x%x\n",format
);
290 src_stride
= src_width
*src_bpp
;
291 // The overlay need a 4 bytes aligned stride
293 src_stride
= ((src_stride
+3)/4)*4;
297 buffer_size
= src_stride
*src_height
;
298 if(!buffer_stride
[0])
299 buffer_stride
[0] = src_stride
;
301 dst_fmt
= tdfx_cfg
.screen_format
;
302 dst_bpp
= ((dst_fmt
& 0x3F)+7)/8;
304 dst_height
= d_height
;
305 dst_stride
= tdfx_cfg
.screen_stride
;
307 tdfx_page
= tdfx_cfg
.screen_stride
*tdfx_cfg
.screen_height
;
308 front_buffer
= tdfx_cfg
.screen_start
;
309 back_buffer
= front_buffer
+ tdfx_page
;
312 tdfx_vid_overlay_t ov
;
313 uint32_t ov_fmt
= src_fmt
, ov_stride
= src_stride
;
315 back_buffer
= (((back_buffer
+3)/4)*4);
316 // With the overlay the front buffer is not on the screen
317 // so we take the back buffer
318 front_buffer
= back_buffer
;
323 back_buffer
= front_buffer
+ 2*(src_stride
*src_height
);
324 ov_stride
= dst_stride
= src_width
<<1;
325 ov_fmt
= IMGFMT_BGR16
;
328 ov
.src
[0] = front_buffer
;
329 ov
.src
[1] = front_buffer
+ (src_stride
*src_height
);
330 ov
.src_width
= src_width
;
331 ov
.src_height
= src_height
;
332 ov
.src_stride
= ov_stride
;
334 ov
.dst_width
= dst_width
;
335 ov
.dst_height
= dst_height
;
340 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&ov
)) {
341 printf("tdfxvid: Overlay setup failed\n");
346 if(use_overlay
== 1) {
347 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_ON
)) {
348 printf("tdfx_vid: Overlay on failed\n");
355 printf("tdfx_vid: Overlay ready : %d(%d) x %d @ %d => %d(%d) x %d @ %d\n",
356 src_width
,src_stride
,src_height
,src_bpp
,
357 dst_width
,dst_stride
,dst_height
,dst_bpp
);
362 printf("tdfx_vid : Texture blit ready %d(%d) x %d @ %d => %d(%d) x %d @ %d\n",
363 src_width
,src_stride
,src_height
,src_bpp
,
364 dst_width
,dst_stride
,dst_height
,dst_bpp
);
372 if(use_overlay
== 2) {
373 if(ioctl(tdfx_fd
,TDFX_VID_OVERLAY_OFF
))
374 printf("tdfx_vid: Overlay off failed\n");
382 static void check_events(void)
386 static uint32_t preinit(const char *arg
)
389 tdfx_fd
= open(arg
? arg
: "/dev/tdfx_vid", O_RDWR
);
391 printf("tdfx_vid: Can't open %s: %s\n",arg
? arg
: "/dev/tdfx_vid",
396 if(ioctl(tdfx_fd
,TDFX_VID_GET_CONFIG
,&tdfx_cfg
)) {
397 printf("tdfx_vid: Can't get current cfg: %s\n",strerror(errno
));
401 printf("tdfx_vid version %d\n"
404 " Format: %c%c%c%d\n",
407 tdfx_cfg
.screen_width
, tdfx_cfg
.screen_height
,
408 tdfx_cfg
.screen_format
>>24,(tdfx_cfg
.screen_format
>>16)&0xFF,
409 (tdfx_cfg
.screen_format
>>8)&0xFF,tdfx_cfg
.screen_format
&0xFF);
411 // For now just allocate more than i ever need
412 agp_mem
= mmap( NULL
, 1024*768*4, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
415 if(agp_mem
== MAP_FAILED
) {
416 printf("tdfx_vid: Memmap failed !!!!!\n");
420 memset(agp_mem
,0,1024*768*4);
425 static uint32_t get_image(mp_image_t
*mpi
) {
429 printf("Get image %d\n",buf
);
432 // Currently read are too slow bcs we read from the
433 // agp aperture and not the memory directly
434 //if(mpi->flags&MP_IMGFLAG_READABLE) return VO_FALSE;
436 if(mpi
->flags
&MP_IMGFLAG_READABLE
&&
437 (mpi
->type
==MP_IMGTYPE_IPB
|| mpi
->type
==MP_IMGTYPE_IP
)){
438 // reference (I/P) frame of IP or IPB:
439 if(num_buffer
<2) return VO_FALSE
; // not enough
441 // for IPB with 2 buffers we can DR only one of the 2 P frames:
442 if(mpi
->type
==MP_IMGTYPE_IPB
&& num_buffer
<3 && current_ip_buf
) return VO_FALSE
;
444 if(mpi
->type
==MP_IMGTYPE_IPB
) ++buf
; // preserve space for B
447 switch(mpi
->imgfmt
) {
455 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
456 mpi
->stride
[0] = src_stride
;
460 mpi
->planes
[0] = agp_mem
+ buf
* buffer_size
;
461 mpi
->stride
[0] = mpi
->width
;
462 mpi
->planes
[1] = mpi
->planes
[0] + mpi
->stride
[0]*mpi
->height
;
463 mpi
->stride
[1] = mpi
->chroma_width
;
464 mpi
->planes
[2] = mpi
->planes
[1] + mpi
->stride
[1]*mpi
->chroma_height
;
465 mpi
->stride
[2] = mpi
->chroma_width
;
468 printf("Get image todo\n");
471 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
472 mpi
->priv
= (void*)buf
;
477 static uint32_t start_slice(mp_image_t
*mpi
){
481 printf("Start slices %d\n",buf
);
484 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
485 buf
= (int)mpi
->priv
;
486 current_buffer
= buf
;
491 static uint32_t draw_image(mp_image_t
*mpi
){
493 tdfx_vid_agp_move_t mov
;
500 printf("Draw image %d\n",buf
);
503 if(mpi
->flags
& MP_IMGFLAG_DIRECT
)
504 buf
= (int)mpi
->priv
;
506 switch(mpi
->imgfmt
) {
514 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
519 planes
[0] = agp_mem
+ buf
* buffer_size
;
520 mem2agpcpy_pic(planes
[0],mpi
->planes
[0],src_bpp
*mpi
->width
,mpi
->height
,
521 buffer_stride
[0],mpi
->stride
[0]);
523 planes
[0] = agp_mem
+ buf
* buffer_size
;
525 mov
.move2
= TDFX_VID_MOVE_2_PACKED
;
526 mov
.width
= mpi
->width
*((mpi
->bpp
+7)/8);
527 mov
.height
= mpi
->height
;
528 mov
.src
= planes
[0] - agp_mem
;
529 mov
.src_stride
= buffer_stride
[0];
531 mov
.dst
= back_buffer
;
532 mov
.dst_stride
= src_stride
;
534 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
))
535 printf("tdfx_vid: AGP move failed\n");
540 if(!(mpi
->flags
&(MP_IMGFLAG_DIRECT
|MP_IMGFLAG_DRAW_CALLBACK
))) {
545 planes
[0] = agp_mem
+ buf
* buffer_size
;
546 memcpy_pic(planes
[0],mpi
->planes
[0],mpi
->width
,mpi
->height
,
547 buffer_stride
[0],mpi
->stride
[0]);
548 planes
[1] = planes
[0] + (mpi
->height
*mpi
->stride
[0]);
549 memcpy_pic(planes
[1],mpi
->planes
[1],mpi
->chroma_width
,mpi
->chroma_height
,
550 buffer_stride
[1],mpi
->stride
[1]);
551 planes
[2] = planes
[1] + (mpi
->chroma_height
*mpi
->stride
[1]);
552 memcpy_pic(planes
[2],mpi
->planes
[2],mpi
->chroma_width
,mpi
->chroma_height
,
553 buffer_stride
[2],mpi
->stride
[2]);
555 planes
[0] = agp_mem
+ buf
* buffer_size
;
556 planes
[1] = planes
[0] + buffer_stride
[0] * src_height
;
557 planes
[2] = planes
[1] + buffer_stride
[1] * src_height
/2;
560 // Setup the yuv thing
561 yuv
.base
= back_buffer
;
562 yuv
.stride
= src_stride
;
563 if(ioctl(tdfx_fd
,TDFX_VID_SET_YUV
,&yuv
)) {
564 printf("tdfx_vid: Set yuv failed\n");
571 mov
.move2
= TDFX_VID_MOVE_2_YUV
;
572 mov
.width
= mpi
->width
;
573 mov
.height
= mpi
->height
;
574 mov
.src
= planes
[0] - agp_mem
;
575 mov
.src_stride
= buffer_stride
[0];
577 mov
.dst_stride
= TDFX_VID_YUV_STRIDE
;
579 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
580 printf("tdfx_vid: AGP move failed on Y plane\n");
585 p
= mpi
->imgfmt
== IMGFMT_YV12
? 1 : 2;
586 mov
.width
= mpi
->chroma_width
;
587 mov
.height
= mpi
->chroma_height
;
588 mov
.src
= planes
[p
] - agp_mem
;
589 mov
.src_stride
= buffer_stride
[p
];
590 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
591 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
592 printf("tdfx_vid: AGP move failed on U plane\n");
596 p
= mpi
->imgfmt
== IMGFMT_YV12
? 2 : 1;
597 mov
.src
= planes
[p
] - agp_mem
;
598 mov
.src_stride
= buffer_stride
[p
];
599 mov
.dst
+= TDFX_VID_YUV_PLANE_SIZE
;
600 if(ioctl(tdfx_fd
,TDFX_VID_AGP_MOVE
,&mov
)) {
601 printf("tdfx_vid: AGP move failed on U plane\n");
606 printf("What's that for a format 0x%x\n",mpi
->imgfmt
);
613 static uint32_t fullscreen(void) {
615 aspect(&dst_width
,&dst_height
,vo_fs
? A_ZOOM
: A_NOZOOM
);
616 // This does not work :((
621 static uint32_t set_window(mp_win_t
* w
) {
622 if(!use_overlay
) return VO_FALSE
;
624 tdfx_ov
.dst_x
= w
->x
;
625 tdfx_ov
.dst_y
= w
->y
;
626 tdfx_ov
.dst_width
= w
->w
;
627 tdfx_ov
.dst_height
= w
->h
;
629 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
630 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set window failed\n");
635 static uint32_t set_colorkey(mp_colorkey_t
* colork
) {
636 if(!use_overlay
) return VO_FALSE
;
638 tdfx_ov
.colorkey
[0] = tdfx_ov
.colorkey
[1] = colork
->x11
;
639 tdfx_ov
.use_colorkey
= 1;
640 tdfx_ov
.invert_colorkey
= 0;
642 if(ioctl(tdfx_fd
,TDFX_VID_SET_OVERLAY
,&tdfx_ov
))
643 mp_msg(MSGT_VO
, MSGL_V
, "tdfx_vid: set colorkey failed\n");
648 static uint32_t control(uint32_t request
, void *data
, ...)
651 case VOCTRL_QUERY_FORMAT
:
652 return query_format(*((uint32_t*)data
));
653 case VOCTRL_GET_IMAGE
:
654 return get_image(data
);
655 case VOCTRL_DRAW_IMAGE
:
656 return draw_image(data
);
657 case VOCTRL_START_SLICE
:
658 return start_slice(data
);
659 case VOCTRL_FULLSCREEN
:
661 case VOCTRL_XOVERLAY_SUPPORT
:
663 case VOCTRL_XOVERLAY_SET_COLORKEY
:
664 return set_colorkey(data
);
665 case VOCTRL_XOVERLAY_SET_WIN
:
666 return set_window(data
);