af_scaletempo: fix crash after channel reconfiguration
[mplayer.git] / libvo / vo_s3fb.c
blobbd04ea29a7a5ecb36c2894b409e49728ac80d6cb
1 /*
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.
23 /* Hints and tricks:
24 * - Use -dr to get direct rendering
25 * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/ioctl.h>
34 #include <fcntl.h>
35 #include <linux/fb.h>
36 #include <sys/io.h>
38 #include "config.h"
39 #ifdef HAVE_SYS_MMAN_H
40 #include <sys/mman.h>
41 #endif
42 #include "mp_msg.h"
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
46 #include "aspect.h"
47 #include "sub/sub.h"
49 static const vo_info_t info =
51 "S3 Virge over fbdev",
52 "s3fb",
53 "Mark Sanderson <mmp@kiora.ath.cx>",
57 const LIBVO_EXTERN(s3fb)
59 typedef struct vga_type {
60 int cr38, cr39, cr53;
61 unsigned char *mmio;
62 } vga_t;
64 static vga_t *v = NULL;
65 static int fd = -1;
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)
110 outb(reg, 0x3d4);
111 return inb(0x3d5);
114 static void writecrtc(int reg, int value)
116 outb(reg, 0x3d4);
117 outb(value, 0x3d5);
120 // enable S3 registers
121 static int enable(void)
123 int fd;
125 if (v)
126 return 1;
127 errno = 0;
128 v = malloc(sizeof(vga_t));
129 if (v) {
130 if (ioperm(0x3d4, 2, 1) == 0) {
131 fd = open("/dev/mem", O_RDWR);
132 if (fd != -1) {
133 v->mmio = mmap(0, S3_NEWMMIO_REGSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd,
134 S3_MEMBASE + S3_NEWMMIO_REGBASE);
135 close(fd);
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);
143 return 1;
146 iopl(0);
148 free(v);
149 v = NULL;
151 return 0;
154 static void disable(void)
156 if (v) {
157 writecrtc(0x53, v->cr53);
158 writecrtc(0x39, v->cr39);
159 writecrtc(0x38, v->cr38);
160 ioperm(0x3d4, 2, 0);
161 munmap(v->mmio, S3_NEWMMIO_REGSIZE);
162 free(v);
163 v = NULL;
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)
174 bpp = 4;
175 else if (format == 6)
176 bpp = 3;
177 else
178 bpp = 2;
180 src_wc = src_w - crop * 2;
181 src_hc = src_h - crop * 2;
182 pitch = src_w * bpp;
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;
191 offset += tmp;
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);
210 if (dst_w == src_w)
211 tmp = 0;
212 else
213 tmp = 2;
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);
234 return offset;
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);
245 OUTREG(0x81d8, 0x1);
246 OUTREG(0x81f8, 0x07ff07ff);
247 OUTREG(0x81fc, 0x00010001);
248 writecrtc(0x92, 0);
249 writecrtc(0x93, 0);
252 static int preinit(const char *arg)
254 char *name;
256 if(arg)
257 name = (char*)arg;
258 else if(!(name = getenv("FRAMEBUFFER")))
259 name = "/dev/fb0";
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));
263 return -1;
266 if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
267 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: problem with FBITGET_FSCREENINFO ioctl: %s\n",
268 strerror(errno));
269 close(fd);
270 fd = -1;
271 return -1;
274 if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
275 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: problem with FBITGET_VSCREENINFO ioctl: %s\n",
276 strerror(errno));
277 close(fd);
278 fd = -1;
279 return -1;
282 // Check the depth now as config() musn't fail
283 switch(fb_vinfo.bits_per_pixel) {
284 case 16:
285 case 24:
286 case 32:
287 break; // Ok
288 default:
289 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: %d bpp output is not supported\n", fb_vinfo.bits_per_pixel);
290 close(fd);
291 fd = -1;
292 return -1;
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));
301 smem = NULL;
302 close(fd);
303 fd = -1;
304 return -1;
307 if (!enable()) {
308 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Couldn't map S3 registers: %s\n", strerror(errno));
309 close(fd);
310 fd = -1;
311 return -1;
314 return 0; // Success
317 /* And close our mess */
318 static void uninit(void)
320 if (inpage0) {
321 clear_screen();
322 yuv_off();
323 inpage0 = NULL;
326 if(smem) {
327 munmap(smem, fb_finfo.smem_len);
328 smem = NULL;
331 disable();
333 if(fd != -1) {
334 close(fd);
335 fd = -1;
339 static void clear_screen(void)
341 if (inpage0) {
342 int n;
344 memset(smem, 0, screenheight * screenstride);
346 if (in_format == IMGFMT_YUY2) {
347 unsigned short *ptr;
348 int i;
350 ptr = (unsigned short *)inpage0;
351 n = in_width * in_height;
352 if (vo_doublebuffering)
353 n *= 2;
354 for(i=0; i<n; i++)
355 *ptr++ = 0x8000;
357 } else {
358 n = in_depth * in_width * in_height;
359 if (vo_doublebuffering)
360 n *= 2;
361 memset(inpage0, 0, n);
366 /* Setup output screen dimensions etc */
367 static void setup_screen(uint32_t full)
369 int inpageoffset;
371 aspect(&vidwidth, &vidheight, full ? A_ZOOM : A_NOZOOM);
373 // center picture
374 vidx = (screenwidth - vidwidth) / 2;
375 vidy = (screenheight - vidheight) / 2;
377 geometry(&vidx, &vidy, &vidwidth, &vidheight, screenwidth, screenheight);
378 vo_fs = full;
380 inpageoffset = yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, 0);
381 inpage0 = smem + inpageoffset;
382 inpage = inpage0;
383 mp_msg(MSGT_VO, MSGL_INFO, "s3fb: output is at %dx%d +%dx%d\n", vidx, vidy, vidwidth, vidheight);
385 clear_screen();
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);
396 in_width = width;
397 in_height = height;
398 in_format = format;
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;
406 switch(in_format) {
408 case IMGFMT_YUY2:
409 in_depth = 2;
410 in_s3_format = 1;
411 alpha_func = vo_draw_alpha_yuy2;
412 break;
414 case IMGFMT_BGR15:
415 in_depth = 2;
416 in_s3_format = 3;
417 alpha_func = vo_draw_alpha_rgb16;
418 break;
420 case IMGFMT_BGR16:
421 in_depth = 2;
422 in_s3_format = 5;
423 alpha_func = vo_draw_alpha_rgb16;
424 break;
426 case IMGFMT_BGR24:
427 in_depth = 3;
428 in_s3_format = 6;
429 alpha_func = vo_draw_alpha_rgb24;
430 break;
432 case IMGFMT_BGR32:
433 in_depth = 4;
434 in_s3_format = 7;
435 alpha_func = vo_draw_alpha_rgb32;
436 break;
438 default:
439 mp_msg(MSGT_VO, MSGL_FATAL, "s3fb: Eik! Something's wrong with control().\n");
440 return -1;
443 offset = in_width * in_depth * in_height;
444 if (vo_doublebuffering)
445 page = offset;
446 else
447 page = 0;
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");
451 return -1;
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,
461 d_width, d_height);
463 return 0;
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);
485 page ^= offset;
486 inpage = inpage0 + page;
490 static int draw_frame(uint8_t *src[])
492 mem2agpcpy(inpage, src[0], in_width * in_depth * in_height);
493 return 0;
496 static int draw_slice(uint8_t *i[], int s[], int w, int h, int x, int y)
498 return 1;
501 /* Attempt to start doing DR */
502 static uint32_t get_image(mp_image_t *mpi)
505 if(mpi->flags & MP_IMGFLAG_READABLE)
506 return VO_FALSE;
507 if(mpi->type == MP_IMGTYPE_STATIC && vo_doublebuffering)
508 return VO_FALSE;
509 if(mpi->type > MP_IMGTYPE_TEMP)
510 return VO_FALSE; // TODO ??
512 switch(in_format) {
513 case IMGFMT_BGR15:
514 case IMGFMT_BGR16:
515 case IMGFMT_BGR24:
516 case IMGFMT_BGR32:
517 case IMGFMT_YUY2:
518 mpi->planes[0] = inpage;
519 mpi->stride[0] = in_width * in_depth;
520 break;
522 default:
523 return VO_FALSE;
526 mpi->width = in_width;
527 mpi->flags |= MP_IMGFLAG_DIRECT;
529 return VO_TRUE;
532 static int control(uint32_t request, void *data)
534 switch(request) {
535 case VOCTRL_GET_IMAGE:
536 return get_image(data);
538 case VOCTRL_QUERY_FORMAT:
539 switch(*((uint32_t*)data)) {
540 case IMGFMT_BGR15:
541 case IMGFMT_BGR16:
542 case IMGFMT_BGR24:
543 case IMGFMT_BGR32:
544 case IMGFMT_YUY2:
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);
553 return 0;
556 return VO_NOTIMPL;
559 /* Dummy funcs */
560 static void check_events(void) {}