Mercurial > mplayer.hg
diff libvo/vo_s3fb.c @ 18535:8e92dd0ff93a
Add YUY2 and back end scaling on S3 Virge chips in combination with fbdev.
Patch by Mark Sanderson < mmp AH kiora POIS ath POIS cx>
author | gpoirier |
---|---|
date | Wed, 17 May 2006 20:56:49 +0000 |
parents | |
children | fb07cde79487 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libvo/vo_s3fb.c Wed May 17 20:56:49 2006 +0000 @@ -0,0 +1,515 @@ +/* Copyright (C) Mark Sanderson, 2006, <mmp@kiora.ath.cx>. + * Released under the terms and conditions of the GPL. + * + * 30-Mar-2006 Modified from tdfxfb.c by Mark Zealey + * + * Hints and tricks: + * - Use -dr to get direct rendering + * - Use -vf yuy2 to get yuy2 rendering, *MUCH* faster than yv12 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <linux/fb.h> +#include <asm/io.h> + +#include "config.h" +#include "fastmemcpy.h" +#include "video_out.h" +#include "video_out_internal.h" +#include "aspect.h" +#include "sub.h" + +static vo_info_t info = + { + "S3 Virge over fbdev", + "s3fb", + "Mark Sanderson <mmp@kiora.ath.cx>", + "" + }; + +LIBVO_EXTERN(s3fb) + + static int fd = -1; + static struct fb_fix_screeninfo fb_finfo; + static struct fb_var_screeninfo fb_vinfo; + static uint32_t in_width, in_height, in_format, in_depth, in_s3_format, + screenwidth, screenheight, screendepth, screenstride, + vidwidth, vidheight, vidx, vidy, page, offset, sreg; + static char *inpage, *inpage0, *smem = NULL; + static void (*alpha_func)(); + +static void clear_screen(); + +/* streams registers */ +#define PSTREAM_CONTROL_REG 0x8180 +#define COL_CHROMA_KEY_CONTROL_REG 0x8184 +#define SSTREAM_CONTROL_REG 0x8190 +#define CHROMA_KEY_UPPER_BOUND_REG 0x8194 +#define SSTREAM_STRETCH_REG 0x8198 +#define BLEND_CONTROL_REG 0x81A0 +#define PSTREAM_FBADDR0_REG 0x81C0 +#define PSTREAM_FBADDR1_REG 0x81C4 +#define PSTREAM_STRIDE_REG 0x81C8 +#define DOUBLE_BUFFER_REG 0x81CC +#define SSTREAM_FBADDR0_REG 0x81D0 +#define SSTREAM_FBADDR1_REG 0x81D4 +#define SSTREAM_STRIDE_REG 0x81D8 +#define OPAQUE_OVERLAY_CONTROL_REG 0x81DC +#define K1_VSCALE_REG 0x81E0 +#define K2_VSCALE_REG 0x81E4 +#define DDA_VERT_REG 0x81E8 +#define STREAMS_FIFO_REG 0x81EC +#define PSTREAM_START_REG 0x81F0 +#define PSTREAM_WINDOW_SIZE_REG 0x81F4 +#define SSTREAM_START_REG 0x81F8 +#define SSTREAM_WINDOW_SIZE_REG 0x81FC + +#define S3_MEMBASE sreg +#define S3_NEWMMIO_REGBASE 0x1000000 /* 16MB */ +#define S3_NEWMMIO_REGSIZE 0x10000 /* 64KB */ +#define S3V_MMIO_REGSIZE 0x8000 /* 32KB */ +#define S3_NEWMMIO_VGABASE (S3_NEWMMIO_REGBASE + 0x8000) + +#define OUTREG(mmreg, value) *(unsigned int *)(&v.mmio[mmreg]) = value + +typedef struct vga_type { + int cr38, cr39, cr53; + unsigned char *mmio; +} vga_t; + +int readcrtc(int reg) { + outb(reg, 0x3d4); + return inb(0x3d5); +} + +void writecrtc(int reg, int value) { + outb(reg, 0x3d4); + outb(value, 0x3d5); +} + +int enable(vga_t *v) { + int fd; + + // enable registers + if (iopl(3) != 0) + return 0; + v->cr38 = readcrtc(0x38); + v->cr39 = readcrtc(0x39); + v->cr53 = readcrtc(0x53); + writecrtc(0x38, 0x48); + writecrtc(0x39, 0xa5); + writecrtc(0x53, 0x08); + fd = open("/dev/mem", O_RDWR); + v->mmio = mmap(0, S3_NEWMMIO_REGSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, + S3_MEMBASE + S3_NEWMMIO_REGBASE); + close(fd); + return 1; +} + +void disable(vga_t *v) { + writecrtc(0x53, v->cr53); + writecrtc(0x39, v->cr39); + writecrtc(0x38, v->cr38); + iopl(0); + munmap(v->mmio, S3_NEWMMIO_REGSIZE); +} + +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) { + int tmp, pitch, start, src_wc, src_hc, bpp; + vga_t v; + + if (format == 0 || format == 7) + bpp = 4; + else if (format == 6) + bpp = 3; + else + bpp = 2; + + src_wc = src_w - crop * 2; + src_hc = src_h - crop * 2; + pitch = src_w * bpp; + + // video card memory layout: + // 0-n: visable screen memory, n = width * height * bytes per pixel + // n-m: scaler source memory, n is aligned to a page boundary + // m+: scaler source memory for multiple buffers + + // offset is the first aligned byte after the screen memory, where the scaler input buffer is + tmp = (yres * line_length + 4095) & ~4095; + offset += tmp; + + // start is the top left viewable scaler input pixel + start = offset + crop * pitch + crop * bpp; + + if (!enable(&v)) + return 0; + + OUTREG(COL_CHROMA_KEY_CONTROL_REG, 0x47000000); + OUTREG(CHROMA_KEY_UPPER_BOUND_REG, 0x0); + OUTREG(BLEND_CONTROL_REG, 0x00000020); + OUTREG(DOUBLE_BUFFER_REG, 0x0); /* Choose fbaddr0 as stream source. */ + OUTREG(OPAQUE_OVERLAY_CONTROL_REG, 0x0); + + OUTREG(PSTREAM_CONTROL_REG, 0x06000000); + OUTREG(PSTREAM_FBADDR0_REG, 0x0); + OUTREG(PSTREAM_FBADDR1_REG, 0x0); + OUTREG(PSTREAM_STRIDE_REG, line_length); + OUTREG(PSTREAM_START_REG, 0x00010001); + OUTREG(PSTREAM_WINDOW_SIZE_REG, 0x00010001); + //OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((xres-1) << 16) | yres) & 0x7ff07ff); + + if (dst_w == src_w) + tmp = 0; + else + tmp = 2; + /* format 1=YCbCr-16 2=YUV-16 3=BGR15 4=YUV-16/32(mixed 2/4byte stride) 5=BGR16 6=BGR24 0,7=BGR32 */ + /* 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. */ + OUTREG(SSTREAM_CONTROL_REG, tmp << 28 | (format << 24) | + ((((src_wc-1)<<1)-(dst_w-1)) & 0xfff)); + OUTREG(SSTREAM_STRETCH_REG, + ((src_wc - 1) & 0x7ff) | (((src_wc - dst_w-1) & 0x7ff) << 16)); + OUTREG(SSTREAM_FBADDR0_REG, start & 0x3fffff ); + OUTREG(SSTREAM_STRIDE_REG, pitch & 0xfff ); + OUTREG(SSTREAM_START_REG, ((dst_x + 1) << 16) | (dst_y + 1)); + OUTREG(SSTREAM_WINDOW_SIZE_REG, ( ((dst_w-1) << 16) | (dst_h ) ) & 0x7ff07ff); + OUTREG(K1_VSCALE_REG, src_hc - 1 ); + OUTREG(K2_VSCALE_REG, (src_hc - dst_h) & 0x7ff ); + /* 0xc000 = bw & vert interp */ + /* 0x8000 = no bw save */ + OUTREG(DDA_VERT_REG, (((~dst_h)-1) & 0xfff ) | 0xc000); + writecrtc(0x92, (((pitch + 7) / 8) >> 8) | 0x80); + writecrtc(0x93, (pitch + 7) / 8); + + writecrtc(0x67, readcrtc(0x67) | 0x4); + + disable(&v); + + return offset; +} + +void yuv_off() { + vga_t v; + + enable(&v); + + writecrtc(0x67, readcrtc(0x67) & ~0xc); + memset(v.mmio + 0x8180, 0, 0x80); + OUTREG(0x81b8, 0x900); + OUTREG(0x81bc, 0x900); + OUTREG(0x81c8, 0x900); + OUTREG(0x81cc, 0x900); + OUTREG(0x81d8, 0x1); + OUTREG(0x81f8, 0x07ff07ff); + OUTREG(0x81fc, 0x00010001); + writecrtc(0x92, 0); + writecrtc(0x93, 0); + disable(&v); +} + +static int preinit(const char *arg) +{ + char *name; + + if(arg) + name = (char*)arg; + else if(!(name = getenv("FRAMEBUFFER"))) + name = "/dev/fb0"; + + if((fd = open(name, O_RDWR)) == -1) { + printf("s3fb: can't open %s: %s\n", name, strerror(errno)); + return -1; + } + + if(ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) { + printf("s3fb: problem with FBITGET_FSCREENINFO ioctl: %s\n", + strerror(errno)); + close(fd); + fd = -1; + return -1; + } + + if(ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) { + printf("s3fb: problem with FBITGET_VSCREENINFO ioctl: %s\n", + strerror(errno)); + close(fd); + fd = -1; + return -1; + } + + // Check the depth now as config() musn't fail + switch(fb_vinfo.bits_per_pixel) { + case 16: + case 24: + case 32: + break; // Ok + default: + printf("s3fb: %d bpp output is not supported\n", fb_vinfo.bits_per_pixel); + close(fd); + fd = -1; + return -1; + } + + /* Open up a window to the hardware */ + smem = mmap(0, fb_finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + sreg = fb_finfo.smem_start; + + if((long)smem == -1) { + printf("s3fb: Couldn't map memory areas: %s\n", strerror(errno)); + if((long)smem != -1) + munmap(smem, fb_finfo.smem_len); + smem = NULL; + return -1; + } + + return 0; +} + +static void uninit(void) +{ + if (inpage0) { + clear_screen(); + yuv_off(); + inpage0 = NULL; + } + + /* And close our mess */ + if(smem) { + munmap(smem, fb_finfo.smem_len); + smem = NULL; + } + + if(fd != -1) { + close(fd); + fd = -1; + } +} + +static void clear_screen() +{ + if (inpage0) { + int n; + + memset(smem, 0, screenheight * screenstride); + + if (in_format == IMGFMT_YUY2) { + unsigned short *ptr; + int i; + + ptr = (unsigned short *)inpage0; + n = in_width * in_height; + if (vo_doublebuffering) + n *= 2; + for(i=0; i<n; i++) + *ptr++ = 0x8000; + + } else { + n = in_depth * in_width * in_height; + if (vo_doublebuffering) + n *= 2; + memset(inpage0, 0, n); + } + } +} + +/* Setup output screen dimensions etc */ +static void setup_screen(uint32_t full) +{ + int inpageoffset; + + aspect(&vidwidth, &vidheight, full ? A_ZOOM : A_NOZOOM); + + // center picture + vidx = (screenwidth - vidwidth) / 2; + vidy = (screenheight - vidheight) / 2; + + geometry(&vidx, &vidy, &vidwidth, &vidheight, screenwidth, screenheight); + vo_fs = full; + + inpageoffset = yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, 0); + inpage0 = smem + inpageoffset; + inpage = inpage0; + printf("s3fb: output is at %dx%d +%dx%d\n", vidx, vidy, vidwidth, vidheight); + + clear_screen(); +} + +static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, + uint32_t flags, char *title, uint32_t format) +{ + screenwidth = fb_vinfo.xres; + screenheight = fb_vinfo.yres; + screenstride = fb_finfo.line_length; + aspect_save_screenres(fb_vinfo.xres,fb_vinfo.yres); + + in_width = width; + in_height = height; + in_format = format; + aspect_save_orig(width,height); + + aspect_save_prescale(d_width,d_height); + + /* Setup the screen for rendering to */ + screendepth = fb_vinfo.bits_per_pixel / 8; + + switch(in_format) { + + case IMGFMT_YUY2: + in_depth = 2; + in_s3_format = 1; + alpha_func = vo_draw_alpha_yuy2; + break; + + case IMGFMT_BGR15: + in_depth = 2; + in_s3_format = 3; + alpha_func = vo_draw_alpha_rgb16; + break; + + case IMGFMT_BGR16: + in_depth = 2; + in_s3_format = 5; + alpha_func = vo_draw_alpha_rgb16; + break; + + case IMGFMT_BGR24: + in_depth = 3; + in_s3_format = 6; + alpha_func = vo_draw_alpha_rgb24; + break; + + case IMGFMT_BGR32: + in_depth = 4; + in_s3_format = 7; + alpha_func = vo_draw_alpha_rgb32; + break; + + default: + printf("s3fb: Eik! Something's wrong with control().\n"); + return -1; + } + + offset = in_width * in_depth * in_height; + if (vo_doublebuffering) + page = offset; + else + page = 0; + + if(screenheight * screenstride + page + offset > fb_finfo.smem_len) { + printf("s3fb: Not enough video memory to play this movie. Try at a lower resolution\n"); + return -1; + } + + setup_screen(flags & VOFLAG_FULLSCREEN); + if (vo_doublebuffering) + inpage = inpage0 + page; + + printf("s3fb: screen is %dx%d at %d bpp, in is %dx%d at %d bpp, norm is %dx%d\n", + screenwidth, screenheight, screendepth * 8, + in_width, in_height, in_depth * 8, + d_width, d_height); + + return 0; +} + +static void draw_alpha(int x, int y, int w, int h, unsigned char *src, + unsigned char *srca, int stride) +{ + char *dst = inpage + (y * in_width + x) * in_depth; + alpha_func(w, h, src, srca, stride, dst, in_width * in_depth); +} + +static void draw_osd(void) +{ + if (!vo_doublebuffering) + vo_draw_text(in_width, in_height, draw_alpha); +} + +/* Render onto the screen */ +static void flip_page(void) +{ + if(vo_doublebuffering) { + vo_draw_text(in_width, in_height, draw_alpha); + yuv_on(in_s3_format, in_width, in_height, vidx, vidy, vidwidth, vidheight, 0, screenwidth, screenheight, screenstride, page); + page ^= offset; + inpage = inpage0 + page; + } +} + +static int draw_frame(uint8_t *src[]) +{ + mem2agpcpy(inpage, src[0], in_width * in_depth * in_height); + return 0; +} + +static int draw_slice(uint8_t *i[], int s[], int w, int h, int x, int y) +{ + return 1; +} + +/* Attempt to start doing DR */ +static uint32_t get_image(mp_image_t *mpi) +{ + + if(mpi->flags & MP_IMGFLAG_READABLE) + return VO_FALSE; + if(mpi->type == MP_IMGTYPE_STATIC && vo_doublebuffering) + return VO_FALSE; + if(mpi->type > MP_IMGTYPE_TEMP) + return VO_FALSE; // TODO ?? + + switch(in_format) { + case IMGFMT_BGR15: + case IMGFMT_BGR16: + case IMGFMT_BGR24: + case IMGFMT_BGR32: + case IMGFMT_YUY2: + mpi->planes[0] = inpage; + mpi->stride[0] = in_width * in_depth; + break; + + default: + return VO_FALSE; + } + + mpi->width = in_width; + mpi->flags |= MP_IMGFLAG_DIRECT; + + return VO_TRUE; +} + +static int control(uint32_t request, void *data, ...) +{ + switch(request) { + case VOCTRL_GET_IMAGE: + return get_image(data); + + case VOCTRL_QUERY_FORMAT: + switch(*((uint32_t*)data)) { + case IMGFMT_BGR15: + case IMGFMT_BGR16: + case IMGFMT_BGR24: + case IMGFMT_BGR32: + case IMGFMT_YUY2: + return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | + VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN; + } + + return 0; /* Not supported */ + + case VOCTRL_FULLSCREEN: + setup_screen(!vo_fs); + return 0; + } + + return VO_NOTIMPL; +} + +/* Dummy funcs */ +static void check_events(void) {}