2 * copyright (C) 2006 Mark Sanderson <mmp@kiora.ath.cx>
4 * 30-Mar-2006 Modified from tdfxfb.c by Mark Zealey
6 * This file is part of MPlayer.
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 of the License, or
11 * (at your option) any later version.
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 along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * - Use -dr to get direct rendering
25 * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
33 #include <sys/ioctl.h>
39 #ifdef HAVE_SYS_MMAN_H
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
49 static const vo_info_t info
=
51 "S3 Virge over fbdev",
53 "Mark Sanderson <mmp@kiora.ath.cx>",
57 const LIBVO_EXTERN(s3fb
)
59 typedef struct vga_type
{
64 static vga_t
*v
= NULL
;
66 static struct fb_fix_screeninfo fb_finfo
;
67 static struct fb_var_screeninfo fb_vinfo
;
68 static uint32_t in_width
, in_height
, in_format
, in_depth
, in_s3_format
,
69 screenwidth
, screenheight
, screendepth
, screenstride
,
70 vidwidth
, vidheight
, vidx
, vidy
, page
, offset
, sreg
;
71 static char *inpage
, *inpage0
, *smem
= NULL
;
72 static void (*alpha_func
)();
74 static void clear_screen(void);
76 /* streams registers */
77 #define PSTREAM_CONTROL_REG 0x8180
78 #define COL_CHROMA_KEY_CONTROL_REG 0x8184
79 #define SSTREAM_CONTROL_REG 0x8190
80 #define CHROMA_KEY_UPPER_BOUND_REG 0x8194
81 #define SSTREAM_STRETCH_REG 0x8198
82 #define BLEND_CONTROL_REG 0x81A0
83 #define PSTREAM_FBADDR0_REG 0x81C0
84 #define PSTREAM_FBADDR1_REG 0x81C4
85 #define PSTREAM_STRIDE_REG 0x81C8
86 #define DOUBLE_BUFFER_REG 0x81CC
87 #define SSTREAM_FBADDR0_REG 0x81D0
88 #define SSTREAM_FBADDR1_REG 0x81D4
89 #define SSTREAM_STRIDE_REG 0x81D8
90 #define OPAQUE_OVERLAY_CONTROL_REG 0x81DC
91 #define K1_VSCALE_REG 0x81E0
92 #define K2_VSCALE_REG 0x81E4
93 #define DDA_VERT_REG 0x81E8
94 #define STREAMS_FIFO_REG 0x81EC
95 #define PSTREAM_START_REG 0x81F0
96 #define PSTREAM_WINDOW_SIZE_REG 0x81F4
97 #define SSTREAM_START_REG 0x81F8
98 #define SSTREAM_WINDOW_SIZE_REG 0x81FC
100 #define S3_MEMBASE sreg
101 #define S3_NEWMMIO_REGBASE 0x1000000 /* 16MB */
102 #define S3_NEWMMIO_REGSIZE 0x10000 /* 64KB */
103 #define S3V_MMIO_REGSIZE 0x8000 /* 32KB */
104 #define S3_NEWMMIO_VGABASE (S3_NEWMMIO_REGBASE + 0x8000)
106 #define OUTREG(mmreg, value) *(unsigned int *)(&v->mmio[mmreg]) = value
108 static int readcrtc(int reg
)
114 static void writecrtc(int reg
, int value
)
120 // enable S3 registers
121 static int enable(void)
128 v
= malloc(sizeof(vga_t
));
130 if (ioperm(0x3d4, 2, 1) == 0) {
131 fd
= open("/dev/mem", O_RDWR
);
133 v
->mmio
= mmap(0, S3_NEWMMIO_REGSIZE
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
,
134 S3_MEMBASE
+ S3_NEWMMIO_REGBASE
);
136 if (v
->mmio
!= MAP_FAILED
) {
137 v
->cr38
= readcrtc(0x38);
138 v
->cr39
= readcrtc(0x39);
139 v
->cr53
= readcrtc(0x53);
140 writecrtc(0x38, 0x48);
141 writecrtc(0x39, 0xa5);
142 writecrtc(0x53, 0x08);
154 static void disable(void)
157 writecrtc(0x53, v
->cr53
);
158 writecrtc(0x39, v
->cr39
);
159 writecrtc(0x38, v
->cr38
);
161 munmap(v
->mmio
, S3_NEWMMIO_REGSIZE
);
167 static int yuv_on(int format
, int src_w
, int src_h
, int dst_x
, int dst_y
,
168 int dst_w
, int dst_h
, int crop
, int xres
, int yres
,
169 int line_length
, int offset
)
171 int tmp
, pitch
, start
, src_wc
, src_hc
, bpp
;
173 if (format
== 0 || format
== 7)
175 else if (format
== 6)
180 src_wc
= src_w
- crop
* 2;
181 src_hc
= src_h
- crop
* 2;
184 // video card memory layout:
185 // 0-n: visible screen memory, n = width * height * bytes per pixel
186 // n-m: scaler source memory, n is aligned to a page boundary
187 // m+: scaler source memory for multiple buffers
189 // offset is the first aligned byte after the screen memory, where the scaler input buffer is
190 tmp
= (yres
* line_length
+ 4095) & ~4095;
193 // start is the top left viewable scaler input pixel
194 start
= offset
+ crop
* pitch
+ crop
* bpp
;
196 OUTREG(COL_CHROMA_KEY_CONTROL_REG
, 0x47000000);
197 OUTREG(CHROMA_KEY_UPPER_BOUND_REG
, 0x0);
198 OUTREG(BLEND_CONTROL_REG
, 0x00000020);
199 OUTREG(DOUBLE_BUFFER_REG
, 0x0); /* Choose fbaddr0 as stream source. */
200 OUTREG(OPAQUE_OVERLAY_CONTROL_REG
, 0x0);
202 OUTREG(PSTREAM_CONTROL_REG
, 0x06000000);
203 OUTREG(PSTREAM_FBADDR0_REG
, 0x0);
204 OUTREG(PSTREAM_FBADDR1_REG
, 0x0);
205 OUTREG(PSTREAM_STRIDE_REG
, line_length
);
206 OUTREG(PSTREAM_START_REG
, 0x00010001);
207 OUTREG(PSTREAM_WINDOW_SIZE_REG
, 0x00010001);
208 //OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((xres-1) << 16) | yres) & 0x7ff07ff);
214 /* format 1=YCbCr-16 2=YUV-16 3=BGR15 4=YUV-16/32(mixed 2/4byte stride) 5=BGR16 6=BGR24 0,7=BGR32 */
215 /* The YUV format pixel has a range of value from 0 to 255, while the YCbCr format pixel values are in the range of 16 to 240. */
216 OUTREG(SSTREAM_CONTROL_REG
, tmp
<< 28 | (format
<< 24) |
217 ((((src_wc
-1)<<1)-(dst_w
-1)) & 0xfff));
218 OUTREG(SSTREAM_STRETCH_REG
,
219 ((src_wc
- 1) & 0x7ff) | (((src_wc
- dst_w
-1) & 0x7ff) << 16));
220 OUTREG(SSTREAM_FBADDR0_REG
, start
& 0x3fffff );
221 OUTREG(SSTREAM_STRIDE_REG
, pitch
& 0xfff );
222 OUTREG(SSTREAM_START_REG
, ((dst_x
+ 1) << 16) | (dst_y
+ 1));
223 OUTREG(SSTREAM_WINDOW_SIZE_REG
, ( ((dst_w
-1) << 16) | (dst_h
) ) & 0x7ff07ff);
224 OUTREG(K1_VSCALE_REG
, src_hc
- 1 );
225 OUTREG(K2_VSCALE_REG
, (src_hc
- dst_h
) & 0x7ff );
226 /* 0xc000 = bw & vert interp */
227 /* 0x8000 = no bw save */
228 OUTREG(DDA_VERT_REG
, (((~dst_h
)-1) & 0xfff ) | 0xc000);
229 writecrtc(0x92, (((pitch
+ 7) / 8) >> 8) | 0x80);
230 writecrtc(0x93, (pitch
+ 7) / 8);
232 writecrtc(0x67, readcrtc(0x67) | 0x4);
237 static void yuv_off(void)
239 writecrtc(0x67, readcrtc(0x67) & ~0xc);
240 memset(v
->mmio
+ 0x8180, 0, 0x80);
241 OUTREG(0x81b8, 0x900);
242 OUTREG(0x81bc, 0x900);
243 OUTREG(0x81c8, 0x900);
244 OUTREG(0x81cc, 0x900);
246 OUTREG(0x81f8, 0x07ff07ff);
247 OUTREG(0x81fc, 0x00010001);
252 static int preinit(const char *arg
)
258 else if(!(name
= getenv("FRAMEBUFFER")))
261 if((fd
= open(name
, O_RDWR
)) == -1) {
262 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: can't open %s: %s\n", name
, strerror(errno
));
266 if(ioctl(fd
, FBIOGET_FSCREENINFO
, &fb_finfo
)) {
267 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: problem with FBITGET_FSCREENINFO ioctl: %s\n",
274 if(ioctl(fd
, FBIOGET_VSCREENINFO
, &fb_vinfo
)) {
275 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: problem with FBITGET_VSCREENINFO ioctl: %s\n",
282 // Check the depth now as config() musn't fail
283 switch(fb_vinfo
.bits_per_pixel
) {
289 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: %d bpp output is not supported\n", fb_vinfo
.bits_per_pixel
);
295 /* Open up a window to the hardware */
296 smem
= mmap(0, fb_finfo
.smem_len
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
297 sreg
= fb_finfo
.smem_start
;
299 if(smem
== (void *)-1) {
300 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: Couldn't map memory areas: %s\n", strerror(errno
));
308 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: Couldn't map S3 registers: %s\n", strerror(errno
));
317 /* And close our mess */
318 static void uninit(void)
327 munmap(smem
, fb_finfo
.smem_len
);
339 static void clear_screen(void)
344 memset(smem
, 0, screenheight
* screenstride
);
346 if (in_format
== IMGFMT_YUY2
) {
350 ptr
= (unsigned short *)inpage0
;
351 n
= in_width
* in_height
;
352 if (vo_doublebuffering
)
358 n
= in_depth
* in_width
* in_height
;
359 if (vo_doublebuffering
)
361 memset(inpage0
, 0, n
);
366 /* Setup output screen dimensions etc */
367 static void setup_screen(uint32_t full
)
371 aspect(&vidwidth
, &vidheight
, full
? A_ZOOM
: A_NOZOOM
);
374 vidx
= (screenwidth
- vidwidth
) / 2;
375 vidy
= (screenheight
- vidheight
) / 2;
377 geometry(&vidx
, &vidy
, &vidwidth
, &vidheight
, screenwidth
, screenheight
);
380 inpageoffset
= yuv_on(in_s3_format
, in_width
, in_height
, vidx
, vidy
, vidwidth
, vidheight
, 0, screenwidth
, screenheight
, screenstride
, 0);
381 inpage0
= smem
+ inpageoffset
;
383 mp_msg(MSGT_VO
, MSGL_INFO
, "s3fb: output is at %dx%d +%dx%d\n", vidx
, vidy
, vidwidth
, vidheight
);
388 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
,
389 uint32_t flags
, char *title
, uint32_t format
)
391 screenwidth
= fb_vinfo
.xres
;
392 screenheight
= fb_vinfo
.yres
;
393 screenstride
= fb_finfo
.line_length
;
394 aspect_save_screenres(fb_vinfo
.xres
,fb_vinfo
.yres
);
399 aspect_save_orig(width
,height
);
401 aspect_save_prescale(d_width
,d_height
);
403 /* Setup the screen for rendering to */
404 screendepth
= fb_vinfo
.bits_per_pixel
/ 8;
411 alpha_func
= vo_draw_alpha_yuy2
;
417 alpha_func
= vo_draw_alpha_rgb16
;
423 alpha_func
= vo_draw_alpha_rgb16
;
429 alpha_func
= vo_draw_alpha_rgb24
;
435 alpha_func
= vo_draw_alpha_rgb32
;
439 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: Eik! Something's wrong with control().\n");
443 offset
= in_width
* in_depth
* in_height
;
444 if (vo_doublebuffering
)
449 if(screenheight
* screenstride
+ page
+ offset
> fb_finfo
.smem_len
) {
450 mp_msg(MSGT_VO
, MSGL_FATAL
, "s3fb: Not enough video memory to play this movie. Try at a lower resolution\n");
454 setup_screen(flags
& VOFLAG_FULLSCREEN
);
455 if (vo_doublebuffering
)
456 inpage
= inpage0
+ page
;
458 mp_msg(MSGT_VO
, MSGL_INFO
, "s3fb: screen is %dx%d at %d bpp, in is %dx%d at %d bpp, norm is %dx%d\n",
459 screenwidth
, screenheight
, screendepth
* 8,
460 in_width
, in_height
, in_depth
* 8,
466 static void draw_alpha(int x
, int y
, int w
, int h
, unsigned char *src
,
467 unsigned char *srca
, int stride
)
469 char *dst
= inpage
+ (y
* in_width
+ x
) * in_depth
;
470 alpha_func(w
, h
, src
, srca
, stride
, dst
, in_width
* in_depth
);
473 static void draw_osd(void)
475 if (!vo_doublebuffering
)
476 vo_draw_text(in_width
, in_height
, draw_alpha
);
479 /* Render onto the screen */
480 static void flip_page(void)
482 if(vo_doublebuffering
) {
483 vo_draw_text(in_width
, in_height
, draw_alpha
);
484 yuv_on(in_s3_format
, in_width
, in_height
, vidx
, vidy
, vidwidth
, vidheight
, 0, screenwidth
, screenheight
, screenstride
, page
);
486 inpage
= inpage0
+ page
;
490 static int draw_frame(uint8_t *src
[])
492 mem2agpcpy(inpage
, src
[0], in_width
* in_depth
* in_height
);
496 static int draw_slice(uint8_t *i
[], int s
[], int w
, int h
, int x
, int y
)
501 /* Attempt to start doing DR */
502 static uint32_t get_image(mp_image_t
*mpi
)
505 if(mpi
->flags
& MP_IMGFLAG_READABLE
)
507 if(mpi
->type
== MP_IMGTYPE_STATIC
&& vo_doublebuffering
)
509 if(mpi
->type
> MP_IMGTYPE_TEMP
)
510 return VO_FALSE
; // TODO ??
518 mpi
->planes
[0] = inpage
;
519 mpi
->stride
[0] = in_width
* in_depth
;
526 mpi
->width
= in_width
;
527 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
532 static int control(uint32_t request
, void *data
, ...)
535 case VOCTRL_GET_IMAGE
:
536 return get_image(data
);
538 case VOCTRL_QUERY_FORMAT
:
539 switch(*((uint32_t*)data
)) {
545 return VFCAP_CSP_SUPPORTED
| VFCAP_CSP_SUPPORTED_BY_HW
|
546 VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
549 return 0; /* Not supported */
551 case VOCTRL_FULLSCREEN
:
552 setup_screen(!vo_fs
);
560 static void check_events(void) {}