Merge svn changes up to r27441
[mplayer.git] / libvo / vo_s3fb.c
blobe36348e8cd8f348810c6c9561a4f19795c75356b
1 /* Copyright (C) Mark Sanderson, 2006, <mmp@kiora.ath.cx>.
2 * Released under the terms and conditions of the GPL.
4 * 30-Mar-2006 Modified from tdfxfb.c by Mark Zealey
5 *
6 * Hints and tricks:
7 * - Use -dr to get direct rendering
8 * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
9 */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <sys/ioctl.h>
17 #include <fcntl.h>
18 #include <linux/fb.h>
19 #include <sys/io.h>
21 #include "config.h"
22 #ifdef HAVE_SYS_MMAN_H
23 #include <sys/mman.h>
24 #endif
25 #include "mp_msg.h"
26 #include "fastmemcpy.h"
27 #include "video_out.h"
28 #include "video_out_internal.h"
29 #include "aspect.h"
30 #include "sub.h"
32 static const vo_info_t info =
34 "S3 Virge over fbdev",
35 "s3fb",
36 "Mark Sanderson <mmp@kiora.ath.cx>",
40 const LIBVO_EXTERN(s3fb)
42 typedef struct vga_type {
43 int cr38, cr39, cr53;
44 unsigned char *mmio;
45 } vga_t;
47 static vga_t *v = NULL;
48 static int fd = -1;
49 static struct fb_fix_screeninfo fb_finfo;
50 static struct fb_var_screeninfo fb_vinfo;
51 static uint32_t in_width, in_height, in_format, in_depth, in_s3_format,
52 screenwidth, screenheight, screendepth, screenstride,
53 vidwidth, vidheight, vidx, vidy, page, offset, sreg;
54 static char *inpage, *inpage0, *smem = NULL;
55 static void (*alpha_func)();
57 static void clear_screen();
59 /* streams registers */
60 #define PSTREAM_CONTROL_REG 0x8180
61 #define COL_CHROMA_KEY_CONTROL_REG 0x8184
62 #define SSTREAM_CONTROL_REG 0x8190
63 #define CHROMA_KEY_UPPER_BOUND_REG 0x8194
64 #define SSTREAM_STRETCH_REG 0x8198
65 #define BLEND_CONTROL_REG 0x81A0
66 #define PSTREAM_FBADDR0_REG 0x81C0
67 #define PSTREAM_FBADDR1_REG 0x81C4
68 #define PSTREAM_STRIDE_REG 0x81C8
69 #define DOUBLE_BUFFER_REG 0x81CC
70 #define SSTREAM_FBADDR0_REG 0x81D0
71 #define SSTREAM_FBADDR1_REG 0x81D4
72 #define SSTREAM_STRIDE_REG 0x81D8
73 #define OPAQUE_OVERLAY_CONTROL_REG 0x81DC
74 #define K1_VSCALE_REG 0x81E0
75 #define K2_VSCALE_REG 0x81E4
76 #define DDA_VERT_REG 0x81E8
77 #define STREAMS_FIFO_REG 0x81EC
78 #define PSTREAM_START_REG 0x81F0
79 #define PSTREAM_WINDOW_SIZE_REG 0x81F4
80 #define SSTREAM_START_REG 0x81F8
81 #define SSTREAM_WINDOW_SIZE_REG 0x81FC
83 #define S3_MEMBASE sreg
84 #define S3_NEWMMIO_REGBASE 0x1000000 /* 16MB */
85 #define S3_NEWMMIO_REGSIZE 0x10000 /* 64KB */
86 #define S3V_MMIO_REGSIZE 0x8000 /* 32KB */
87 #define S3_NEWMMIO_VGABASE (S3_NEWMMIO_REGBASE + 0x8000)
89 #define OUTREG(mmreg, value) *(unsigned int *)(&v->mmio[mmreg]) = value
91 int readcrtc(int reg) {
92 outb(reg, 0x3d4);
93 return inb(0x3d5);
96 void writecrtc(int reg, int value) {
97 outb(reg, 0x3d4);
98 outb(value, 0x3d5);
101 // enable S3 registers
102 int enable() {
103 int fd;
105 if (v)
106 return 1;
107 errno = 0;
108 v = malloc(sizeof(vga_t));
109 if (v) {
110 if (ioperm(0x3d4, 2, 1) == 0) {
111 fd = open("/dev/mem", O_RDWR);
112 if (fd != -1) {
113 v->mmio = mmap(0, S3_NEWMMIO_REGSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
114 S3_MEMBASE + S3_NEWMMIO_REGBASE);
115 close(fd);
116 if (v->mmio != MAP_FAILED) {
117 v->cr38 = readcrtc(0x38);
118 v->cr39 = readcrtc(0x39);
119 v->cr53 = readcrtc(0x53);
120 writecrtc(0x38, 0x48);
121 writecrtc(0x39, 0xa5);
122 writecrtc(0x53, 0x08);
123 return 1;
126 iopl(0);
128 free(v);
129 v = NULL;
131 return 0;
134 void disable() {
135 if (v) {
136 writecrtc(0x53, v->cr53);
137 writecrtc(0x39, v->cr39);
138 writecrtc(0x38, v->cr38);
139 ioperm(0x3d4, 2, 0);
140 munmap(v->mmio, S3_NEWMMIO_REGSIZE);
141 free(v);
142 v = NULL;
146 int yuv_on(int format, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int crop, int xres, int yres, int line_length, int offset) {
147 int tmp, pitch, start, src_wc, src_hc, bpp;
149 if (format == 0 || format == 7)
150 bpp = 4;
151 else if (format == 6)
152 bpp = 3;
153 else
154 bpp = 2;
156 src_wc = src_w - crop * 2;
157 src_hc = src_h - crop * 2;
158 pitch = src_w * bpp;
160 // video card memory layout:
161 // 0-n: visible screen memory, n = width * height * bytes per pixel
162 // n-m: scaler source memory, n is aligned to a page boundary
163 // m+: scaler source memory for multiple buffers
165 // offset is the first aligned byte after the screen memory, where the scaler input buffer is
166 tmp = (yres * line_length + 4095) & ~4095;
167 offset += tmp;
169 // start is the top left viewable scaler input pixel
170 start = offset + crop * pitch + crop * bpp;
172 OUTREG(COL_CHROMA_KEY_CONTROL_REG, 0x47000000);
173 OUTREG(CHROMA_KEY_UPPER_BOUND_REG, 0x0);
174 OUTREG(BLEND_CONTROL_REG, 0x00000020);
175 OUTREG(DOUBLE_BUFFER_REG, 0x0); /* Choose fbaddr0 as stream source. */
176 OUTREG(OPAQUE_OVERLAY_CONTROL_REG, 0x0);
178 OUTREG(PSTREAM_CONTROL_REG, 0x06000000);
179 OUTREG(PSTREAM_FBADDR0_REG, 0x0);
180 OUTREG(PSTREAM_FBADDR1_REG, 0x0);
181 OUTREG(PSTREAM_STRIDE_REG, line_length);
182 OUTREG(PSTREAM_START_REG, 0x00010001);
183 OUTREG(PSTREAM_WINDOW_SIZE_REG, 0x00010001);
184 //OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((xres-1) << 16) | yres) & 0x7ff07ff);
186 if (dst_w == src_w)
187 tmp = 0;
188 else
189 tmp = 2;
190 /* format 1=YCbCr-16 2=YUV-16 3=BGR15 4=YUV-16/32(mixed 2/4byte stride) 5=BGR16 6=BGR24 0,7=BGR32 */
191 /* 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. */
192 OUTREG(SSTREAM_CONTROL_REG, tmp << 28 | (format << 24) |
193 ((((src_wc-1)<<1)-(dst_w-1)) & 0xfff));
194 OUTREG(SSTREAM_STRETCH_REG,
195 ((src_wc - 1) & 0x7ff) | (((src_wc - dst_w-1) & 0x7ff) << 16));
196 OUTREG(SSTREAM_FBADDR0_REG, start & 0x3fffff );
197 OUTREG(SSTREAM_STRIDE_REG, pitch & 0xfff );
198 OUTREG(SSTREAM_START_REG, ((dst_x + 1) << 16) | (dst_y + 1));
199 OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((dst_w-1) << 16) | (dst_h ) ) & 0x7ff07ff);
200 OUTREG(K1_VSCALE_REG, src_hc - 1 );
201 OUTREG(K2_VSCALE_REG, (src_hc - dst_h) & 0x7ff );
202 /* 0xc000 = bw & vert interp */
203 /* 0x8000 = no bw save */
204 OUTREG(DDA_VERT_REG, (((~dst_h)-1) & 0xfff ) | 0xc000);
205 writecrtc(0x92, (((pitch + 7) / 8) >> 8) | 0x80);
206 writecrtc(0x93, (pitch + 7) / 8);
208 writecrtc(0x67, readcrtc(0x67) | 0x4);
210 return offset;
213 void yuv_off() {
214 writecrtc(0x67, readcrtc(0x67) & ~0xc);
215 memset(v->mmio + 0x8180, 0, 0x80);
216 OUTREG(0x81b8, 0x900);
217 OUTREG(0x81bc, 0x900);
218 OUTREG(0x81c8, 0x900);
219 OUTREG(0x81cc, 0x900);
220 OUTREG(0x81d8, 0x1);
221 OUTREG(0x81f8, 0x07ff07ff);
222 OUTREG(0x81fc, 0x00010001);
223 writecrtc(0x92, 0);
224 writecrtc(0x93, 0);
227 static int preinit(const char *arg)
229 char *name;
231 if(arg)
232 name = (char*)arg;
233 else if(!(name = getenv("FRAMEBUFFER")))
234 name = "/dev/fb0";
236 if((fd = open(name, O_RDWR)) == -1) {
237 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: can't open %s: %s\n", name, strerror(errno));
238 return -1;
241 if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
242 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: problem with FBITGET_FSCREENINFO ioctl: %s\n",
243 strerror(errno));
244 close(fd);
245 fd = -1;
246 return -1;
249 if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
250 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: problem with FBITGET_VSCREENINFO ioctl: %s\n",
251 strerror(errno));
252 close(fd);
253 fd = -1;
254 return -1;
257 // Check the depth now as config() musn't fail
258 switch(fb_vinfo.bits_per_pixel) {
259 case 16:
260 case 24:
261 case 32:
262 break; // Ok
263 default:
264 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: %d bpp output is not supported\n", fb_vinfo.bits_per_pixel);
265 close(fd);
266 fd = -1;
267 return -1;
270 /* Open up a window to the hardware */
271 smem = mmap(0, fb_finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
272 sreg = fb_finfo.smem_start;
274 if(smem == (void *)-1) {
275 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Couldn't map memory areas: %s\n", strerror(errno));
276 smem = NULL;
277 close(fd);
278 fd = -1;
279 return -1;
282 if (!enable()) {
283 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Couldn't map S3 registers: %s\n", strerror(errno));
284 close(fd);
285 fd = -1;
286 return -1;
289 return 0; // Success
292 /* And close our mess */
293 static void uninit(void)
295 if (inpage0) {
296 clear_screen();
297 yuv_off();
298 inpage0 = NULL;
301 if(smem) {
302 munmap(smem, fb_finfo.smem_len);
303 smem = NULL;
306 disable();
308 if(fd != -1) {
309 close(fd);
310 fd = -1;
314 static void clear_screen()
316 if (inpage0) {
317 int n;
319 memset(smem, 0, screenheight * screenstride);
321 if (in_format == IMGFMT_YUY2) {
322 unsigned short *ptr;
323 int i;
325 ptr = (unsigned short *)inpage0;
326 n = in_width * in_height;
327 if (vo_doublebuffering)
328 n *= 2;
329 for(i=0; i<n; i++)
330 *ptr++ = 0x8000;
332 } else {
333 n = in_depth * in_width * in_height;
334 if (vo_doublebuffering)
335 n *= 2;
336 memset(inpage0, 0, n);
341 /* Setup output screen dimensions etc */
342 static void setup_screen(uint32_t full)
344 int inpageoffset;
346 aspect(&vidwidth, &vidheight, full ? A_ZOOM : A_NOZOOM);
348 // center picture
349 vidx = (screenwidth - vidwidth) / 2;
350 vidy = (screenheight - vidheight) / 2;
352 geometry(&vidx, &vidy, &vidwidth, &vidheight, screenwidth, screenheight);
353 vo_fs = full;
355 inpageoffset = yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, 0);
356 inpage0 = smem + inpageoffset;
357 inpage = inpage0;
358 mp_msg(MSGT_VO, MSGL_INFO, "s3fb: output is at %dx%d +%dx%d\n", vidx, vidy, vidwidth, vidheight);
360 clear_screen();
363 static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height,
364 uint32_t flags, char *title, uint32_t format)
366 screenwidth = fb_vinfo.xres;
367 screenheight = fb_vinfo.yres;
368 screenstride = fb_finfo.line_length;
369 aspect_save_screenres(fb_vinfo.xres,fb_vinfo.yres);
371 in_width = width;
372 in_height = height;
373 in_format = format;
374 aspect_save_orig(width,height);
376 aspect_save_prescale(d_width,d_height);
378 /* Setup the screen for rendering to */
379 screendepth = fb_vinfo.bits_per_pixel / 8;
381 switch(in_format) {
383 case IMGFMT_YUY2:
384 in_depth = 2;
385 in_s3_format = 1;
386 alpha_func = vo_draw_alpha_yuy2;
387 break;
389 case IMGFMT_BGR15:
390 in_depth = 2;
391 in_s3_format = 3;
392 alpha_func = vo_draw_alpha_rgb16;
393 break;
395 case IMGFMT_BGR16:
396 in_depth = 2;
397 in_s3_format = 5;
398 alpha_func = vo_draw_alpha_rgb16;
399 break;
401 case IMGFMT_BGR24:
402 in_depth = 3;
403 in_s3_format = 6;
404 alpha_func = vo_draw_alpha_rgb24;
405 break;
407 case IMGFMT_BGR32:
408 in_depth = 4;
409 in_s3_format = 7;
410 alpha_func = vo_draw_alpha_rgb32;
411 break;
413 default:
414 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Eik! Something's wrong with control().\n");
415 return -1;
418 offset = in_width * in_depth * in_height;
419 if (vo_doublebuffering)
420 page = offset;
421 else
422 page = 0;
424 if(screenheight * screenstride + page + offset > fb_finfo.smem_len) {
425 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Not enough video memory to play this movie. Try at a lower resolution\n");
426 return -1;
429 setup_screen(flags & VOFLAG_FULLSCREEN);
430 if (vo_doublebuffering)
431 inpage = inpage0 + page;
433 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",
434 screenwidth, screenheight, screendepth * 8,
435 in_width, in_height, in_depth * 8,
436 d_width, d_height);
438 return 0;
441 static void draw_alpha(int x, int y, int w, int h, unsigned char *src,
442 unsigned char *srca, int stride)
444 char *dst = inpage + (y * in_width + x) * in_depth;
445 alpha_func(w, h, src, srca, stride, dst, in_width * in_depth);
448 static void draw_osd(void)
450 if (!vo_doublebuffering)
451 vo_draw_text(in_width, in_height, draw_alpha);
454 /* Render onto the screen */
455 static void flip_page(void)
457 if(vo_doublebuffering) {
458 vo_draw_text(in_width, in_height, draw_alpha);
459 yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, page);
460 page ^= offset;
461 inpage = inpage0 + page;
465 static int draw_frame(uint8_t *src[])
467 mem2agpcpy(inpage, src[0], in_width * in_depth * in_height);
468 return 0;
471 static int draw_slice(uint8_t *i[], int s[], int w, int h, int x, int y)
473 return 1;
476 /* Attempt to start doing DR */
477 static uint32_t get_image(mp_image_t *mpi)
480 if(mpi->flags & MP_IMGFLAG_READABLE)
481 return VO_FALSE;
482 if(mpi->type == MP_IMGTYPE_STATIC && vo_doublebuffering)
483 return VO_FALSE;
484 if(mpi->type > MP_IMGTYPE_TEMP)
485 return VO_FALSE; // TODO ??
487 switch(in_format) {
488 case IMGFMT_BGR15:
489 case IMGFMT_BGR16:
490 case IMGFMT_BGR24:
491 case IMGFMT_BGR32:
492 case IMGFMT_YUY2:
493 mpi->planes[0] = inpage;
494 mpi->stride[0] = in_width * in_depth;
495 break;
497 default:
498 return VO_FALSE;
501 mpi->width = in_width;
502 mpi->flags |= MP_IMGFLAG_DIRECT;
504 return VO_TRUE;
507 static int control(uint32_t request, void *data)
509 switch(request) {
510 case VOCTRL_GET_IMAGE:
511 return get_image(data);
513 case VOCTRL_QUERY_FORMAT:
514 switch(*((uint32_t*)data)) {
515 case IMGFMT_BGR15:
516 case IMGFMT_BGR16:
517 case IMGFMT_BGR24:
518 case IMGFMT_BGR32:
519 case IMGFMT_YUY2:
520 return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW |
521 VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
524 return 0; /* Not supported */
526 case VOCTRL_FULLSCREEN:
527 setup_screen(!vo_fs);
528 return 0;
531 return VO_NOTIMPL;
534 /* Dummy funcs */
535 static void check_events(void) {}