From 8903c510f8340f9862dc4631e7f62d10b73f7d8b Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 11 Aug 2008 23:10:03 +0000 Subject: Add VIDIX driver for SuperH Mobile VEU hardware block. Patch by Magnus Damm . git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@27452 b3059339-0415-0410-9bf9-f77b7e298cf2 --- Makefile | 1 + configure | 9 +- vidix/drivers.c | 4 + vidix/sh_veu_vid.c | 594 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 606 insertions(+), 2 deletions(-) create mode 100644 vidix/sh_veu_vid.c diff --git a/Makefile b/Makefile index 0eaa22098b..b0e6a0e53d 100644 --- a/Makefile +++ b/Makefile @@ -595,6 +595,7 @@ SRCS_MPLAYER-$(VIDIX_PM3) += vidix/pm3_vid.c SRCS_MPLAYER-$(VIDIX_RADEON) += vidix/radeon_vid.c SRCS_MPLAYER-$(VIDIX_RAGE128) += vidix/rage128_vid.c SRCS_MPLAYER-$(VIDIX_S3) += vidix/s3_vid.c +SRCS_MPLAYER-$(VIDIX_SH_VEU) += vidix/sh_veu_vid.c SRCS_MPLAYER-$(VIDIX_SIS) += vidix/sis_vid.c vidix/sis_bridge.c SRCS_MPLAYER-$(VIDIX_UNICHROME) += vidix/unichrome_vid.c diff --git a/configure b/configure index 0a9a0373df..c905117915 100755 --- a/configure +++ b/configure @@ -4287,6 +4287,8 @@ _def_vidix_drv_rage128='#undef CONFIG_VIDIX_DRV_RAGE128' _vidix_drv_rage128=no _def_vidix_drv_s3='#undef CONFIG_VIDIX_DRV_S3' _vidix_drv_s3=no +_def_vidix_drv_sh_veu='#undef CONFIG_VIDIX_DRV_SH_VEU' +_vidix_drv_sh_veu=no _def_vidix_drv_sis='#undef CONFIG_VIDIX_DRV_SIS' _vidix_drv_sis=no _def_vidix_drv_unichrome='#undef CONFIG_VIDIX_DRV_UNICHROME' @@ -4303,11 +4305,12 @@ if test "$_vidix" = yes ; then _def_vidix='#define CONFIG_VIDIX 1' _vosrc="$_vosrc vo_cvidix.c" _vomodules="cvidix $_vomodules" - test "$_vidix_drivers" || _vidix_drivers="cyberblade ivtv mach64 mga mga_crtc2 nvidia pm2 pm3 radeon rage128 s3 sis unichrome" + test "$_vidix_drivers" || _vidix_drivers="cyberblade ivtv mach64 mga mga_crtc2 nvidia pm2 pm3 radeon rage128 s3 sh_veu sis unichrome" test $_ivtv = "yes" || _vidix_drivers=`echo $_vidix_drivers | sed s/ivtv//` - # some vidix drivers are meant to work on x86 only, discard them elsewhere + # some vidix drivers are architecture and os specific, discard them elsewhere x86 || _vidix_drivers=`echo $_vidix_drivers | sed -e s/cyberblade// -e s/sis// -e s/unichrome// -e s/s3//` + (test $host_arch = "sh" && linux) || _vidix_drivers=`echo $_vidix_drivers | sed s/sh_veu//` for driver in $_vidix_drivers ; do uc_driver=`echo $driver | tr '[a-z]' '[A-Z]'` @@ -7894,6 +7897,7 @@ VIDIX_PM3=$_vidix_drv_pm3 VIDIX_RADEON=$_vidix_drv_radeon VIDIX_RAGE128=$_vidix_drv_rage128 VIDIX_S3=$_vidix_drv_s3 +VIDIX_SH_VEU=$_vidix_drv_sh_veu VIDIX_SIS=$_vidix_drv_sis VIDIX_UNICHROME=$_vidix_drv_unichrome VSTREAM = $_vstream @@ -8621,6 +8625,7 @@ $_def_vidix_drv_pm3 $_def_vidix_drv_radeon $_def_vidix_drv_rage128 $_def_vidix_drv_s3 +$_def_vidix_drv_sh_veu $_def_vidix_drv_sis $_def_vidix_drv_unichrome $_def_vidix_pfx diff --git a/vidix/drivers.c b/vidix/drivers.c index 483428ff3b..2311d94800 100644 --- a/vidix/drivers.c +++ b/vidix/drivers.c @@ -43,6 +43,7 @@ extern VDXDriver pm3_drv; extern VDXDriver radeon_drv; extern VDXDriver rage128_drv; extern VDXDriver s3_drv; +extern VDXDriver sh_veu_drv; extern VDXDriver sis_drv; extern VDXDriver unichrome_drv; @@ -92,6 +93,9 @@ void vidix_register_all_drivers (void) #ifdef CONFIG_VIDIX_DRV_S3 vidix_register_driver (&s3_drv); #endif +#ifdef CONFIG_VIDIX_DRV_SH_VEU + vidix_register_driver (&sh_veu_drv); +#endif #ifdef CONFIG_VIDIX_DRV_SIS vidix_register_driver (&sis_drv); #endif diff --git a/vidix/sh_veu_vid.c b/vidix/sh_veu_vid.c new file mode 100644 index 0000000000..1d683e38c6 --- /dev/null +++ b/vidix/sh_veu_vid.c @@ -0,0 +1,594 @@ +/* + * VIDIX driver for SuperH Mobile VEU hardware block. + * Copyright (C) 2008 Magnus Damm + * + * Requires a kernel that exposes the VEU hardware block to user space + * using UIO. Available in upstream linux-2.6.27 or later. + * + * Tested using WVGA and QVGA panels with sh7722 VEU and sh7723 VEU2H. + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MPlayer; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "vidix.h" +#include "fourcc.h" + +#include "dha.h" + +static int fgets_with_openclose(char *fname, char *buf, size_t maxlen) +{ + FILE *fp; + + fp = fopen(fname, "r"); + if (!fp) + return -1; + + fgets(buf, maxlen, fp); + fclose(fp); + return strlen(buf); +} + +struct uio_device { + char *name; + char *path; + int fd; +}; + +#define MAXNAMELEN 256 + +static int locate_uio_device(char *name, struct uio_device *udp) +{ + char fname[MAXNAMELEN], buf[MAXNAMELEN]; + char *tmp; + int uio_id; + + uio_id = -1; + do { + uio_id++; + snprintf(fname, MAXNAMELEN, "/sys/class/uio/uio%d/name", uio_id); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) < 0) + return -1; + + } while (strncmp(name, buf, strlen(name))); + + tmp = strchr(buf, '\n'); + if (tmp) + *tmp = '\0'; + + udp->name = strdup(buf); + udp->path = strdup(fname); + udp->path[strlen(udp->path) - 5] = '\0'; + + snprintf(buf, MAXNAMELEN, "/dev/uio%d", uio_id); + udp->fd = open(buf, O_RDWR | O_SYNC); + + if (udp->fd < 0) + return -1; + + return 0; +} + +struct uio_map { + unsigned long address; + unsigned long size; + void *iomem; +}; + +static int setup_uio_map(struct uio_device *udp, int nr, struct uio_map *ump) +{ + char fname[MAXNAMELEN], buf[MAXNAMELEN]; + + snprintf(fname, MAXNAMELEN, "%s/maps/map%d/addr", udp->path, nr); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) + return -1; + + ump->address = strtoul(buf, NULL, 0); + + snprintf(fname, MAXNAMELEN, "%s/maps/map%d/size", udp->path, nr); + if (fgets_with_openclose(fname, buf, MAXNAMELEN) <= 0) + return -1; + + ump->size = strtoul(buf, NULL, 0); + ump->iomem = mmap(0, ump->size, + PROT_READ|PROT_WRITE, MAP_SHARED, + udp->fd, nr * getpagesize()); + + if (ump->iomem == MAP_FAILED) + return -1; + + return 0; +} + +struct fb_info { + unsigned long width; + unsigned long height; + unsigned long bpp; + unsigned long line_length; + + unsigned long address; + unsigned long size; +}; + +static int get_fb_info(char *device, struct fb_info *fip) +{ + struct fb_var_screeninfo vinfo; + struct fb_fix_screeninfo finfo; + void *iomem; + int fd; + + fd = open(device, O_RDWR); + if (fd < 0) { + perror("open"); + return -1; + } + + if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo) == -1) { + perror("ioctl(FBIOGET_VSCREENINFO)"); + return -1; + } + + fip->width = vinfo.xres; + fip->height = vinfo.yres; + fip->bpp = vinfo.bits_per_pixel; + + if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) { + perror("ioctl(FBIOGET_FSCREENINFO)"); + return -1; + } + + fip->address = finfo.smem_start; + fip->size = finfo.smem_len; + fip->line_length = finfo.line_length; + + iomem = mmap(0, fip->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); + + if (iomem == MAP_FAILED) { + perror("mmap"); + return -1; + } + + /* clear framebuffer */ + memset(iomem, 0, fip->line_length * fip->height); + munmap(iomem, fip->size); + + close(fd); + return 0; +} + +#define VESTR 0x00 /* start register */ +#define VESWR 0x10 /* src: line length */ +#define VESSR 0x14 /* src: image size */ +#define VSAYR 0x18 /* src: y/rgb plane address */ +#define VSACR 0x1c /* src: c plane address */ +#define VBSSR 0x20 /* bundle mode register */ +#define VEDWR 0x30 /* dst: line length */ +#define VDAYR 0x34 /* dst: y/rgb plane address */ +#define VDACR 0x38 /* dst: c plane address */ +#define VTRCR 0x50 /* transform control */ +#define VRFCR 0x54 /* resize scale */ +#define VRFSR 0x58 /* resize clip */ +#define VENHR 0x5c /* enhance */ +#define VFMCR 0x70 /* filter mode */ +#define VVTCR 0x74 /* lowpass vertical */ +#define VHTCR 0x78 /* lowpass horizontal */ +#define VAPCR 0x80 /* color match */ +#define VECCR 0x84 /* color replace */ +#define VAFXR 0x90 /* fixed mode */ +#define VSWPR 0x94 /* swap */ +#define VEIER 0xa0 /* interrupt mask */ +#define VEVTR 0xa4 /* interrupt event */ +#define VSTAR 0xb0 /* status */ +#define VBSRR 0xb4 /* reset */ + +#define VMCR00 0x200 /* color conversion matrix coefficient 00 */ +#define VMCR01 0x204 /* color conversion matrix coefficient 01 */ +#define VMCR02 0x208 /* color conversion matrix coefficient 02 */ +#define VMCR10 0x20c /* color conversion matrix coefficient 10 */ +#define VMCR11 0x210 /* color conversion matrix coefficient 11 */ +#define VMCR12 0x214 /* color conversion matrix coefficient 12 */ +#define VMCR20 0x218 /* color conversion matrix coefficient 20 */ +#define VMCR21 0x21c /* color conversion matrix coefficient 21 */ +#define VMCR22 0x220 /* color conversion matrix coefficient 22 */ +#define VCOFFR 0x224 /* color conversion offset */ +#define VCBR 0x228 /* color conversion clip */ + +/* Helper functions for reading registers. */ + +static unsigned long read_reg(struct uio_map *ump, int reg_offs) +{ + volatile unsigned long *reg = ump->iomem; + + return reg[reg_offs / 4]; +} + +static void write_reg(struct uio_map *ump, unsigned long value, int reg_offs) +{ + volatile unsigned long *reg = ump->iomem; + + reg[reg_offs / 4] = value; +} + +static vidix_capability_t sh_veu_cap = { + "SuperH VEU driver", + "Magnus Damm", + TYPE_OUTPUT, + { 0, 0, 0, 0 }, + 2560, + 1920, + 16, + 16, + -1, + FLAG_UPSCALER|FLAG_DOWNSCALER, + -1, + -1, + { 0, 0, 0, 0 } +}; + +/* global variables yuck */ + +static struct fb_info fbi; +static struct uio_device uio_dev; +static struct uio_map uio_mmio, uio_mem; + +struct sh_veu_plane { + unsigned long width; + unsigned long height; + unsigned long stride; + unsigned long pos_x; + unsigned long pos_y; +}; + +static struct sh_veu_plane _src, _dst; +static vidix_playback_t my_info; + +static int sh_veu_probe(int verbose, int force) +{ + int ret; + + ret = get_fb_info("/dev/fb0", &fbi); + if (ret < 0) + return ret; + + if (fbi.bpp != 16) { + printf("sh_veu: only 16bpp supported\n"); + return -1; + } + + ret = locate_uio_device("VEU", &uio_dev); + if (ret < 0) { + printf("sh_veu: unable to locate matching UIO device\n"); + return ret; + } + + ret = setup_uio_map(&uio_dev, 0, &uio_mmio); + if (ret < 0) { + printf("sh_veu: cannot setup MMIO\n"); + return ret; + } + + ret = setup_uio_map(&uio_dev, 1, &uio_mem); + if (ret < 0) { + printf("sh_veu: cannot setup contiguous memory\n"); + return ret; + } + + printf("sh_veu: Using %s at %s on %lux%lu %ldbpp /dev/fb0\n", + uio_dev.name, uio_dev.path, + fbi.width, fbi.height, fbi.bpp); + + return ret; +} + +static void sh_veu_wait_irq(vidix_playback_t *info) +{ + unsigned long n_pending; + + /* Wait for an interrupt */ + read(uio_dev.fd, &n_pending, sizeof(u_long)); + + write_reg(&uio_mmio, 0x100, VEVTR); /* ack int, write 0 to bit 0 */ +} + +static int sh_veu_is_veu2h(void) +{ + return uio_mmio.size > 0xb8; +} + +static unsigned long sh_veu_do_scale(struct uio_map *ump, + int vertical, int size_in, + int size_out, int crop_out) +{ + unsigned long fixpoint, mant, frac, value, rep; + + /* calculate FRAC and MANT */ + do { + rep = mant = frac = 0; + + if (size_in == size_out) { + if (crop_out != size_out) + mant = 1; /* needed for cropping */ + break; + } + + /* VEU2H special upscale */ + if (sh_veu_is_veu2h() && size_out > size_in) { + fixpoint = (4096 * size_in) / size_out; + + mant = fixpoint / 4096; + frac = fixpoint - (mant * 4096); + frac &= ~0x07; + + switch (frac) { + case 0x800: + rep = 1; + break; + case 0x400: + rep = 3; + break; + case 0x200: + rep = 7; + break; + } + + if (rep) + break; + } + + fixpoint = (4096 * (size_in - 1)) / (size_out + 1); + mant = fixpoint / 4096; + frac = fixpoint - (mant * 4096); + + if (frac & 0x07) { + frac &= ~0x07; + if (size_out > size_in) + frac -= 8; /* round down if scaling up */ + else + frac += 8; /* round up if scaling down */ + } + + } while (0); + + /* set scale */ + value = read_reg(ump, VRFCR); + if (vertical) { + value &= ~0xffff0000; + value |= ((mant << 12) | frac) << 16; + } else { + value &= ~0xffff; + value |= (mant << 12) | frac; + } + write_reg(ump, value, VRFCR); + + /* set clip */ + value = read_reg(ump, VRFSR); + if (vertical) { + value &= ~0xffff0000; + value |= ((rep << 12) | crop_out) << 16; + } else { + value &= ~0xffff; + value |= (rep << 12) | crop_out; + } + write_reg(ump, value, VRFSR); + + return (((size_in * crop_out) / size_out) + 0x03) & ~0x03; +} + +static void sh_veu_setup_planes(vidix_playback_t *info, + struct sh_veu_plane *src, + struct sh_veu_plane *dst) +{ + unsigned long addr, real_w, real_h; + + src->width = info->src.w; + src->height = info->src.h; + src->stride = (info->src.w+15) & ~15; + + dst->width = real_w = info->dest.w; + dst->height = real_h = info->dest.h; + dst->stride = fbi.line_length; + dst->pos_x = info->dest.x & ~0x03; + dst->pos_y = info->dest.y; + + if ((dst->width + dst->pos_x) > fbi.width) + dst->width = fbi.width - dst->pos_x; + + if ((dst->height + dst->pos_y) > fbi.height) + dst->height = fbi.height - dst->pos_y; + + addr = fbi.address; + addr += dst->pos_x * (fbi.bpp / 8); + addr += dst->pos_y * dst->stride; + + src->width = sh_veu_do_scale(&uio_mmio, 0, src->width, + real_w, dst->width); + src->height = sh_veu_do_scale(&uio_mmio, 1, src->height, + real_h, dst->height); + + write_reg(&uio_mmio, src->stride, VESWR); + write_reg(&uio_mmio, src->width | (src->height << 16), VESSR); + write_reg(&uio_mmio, 0, VBSSR); /* not using bundle mode */ + + write_reg(&uio_mmio, dst->stride, VEDWR); + write_reg(&uio_mmio, addr, VDAYR); + write_reg(&uio_mmio, 0, VDACR); /* unused for RGB */ + + write_reg(&uio_mmio, 0x67, VSWPR); + write_reg(&uio_mmio, (6 << 16) | (0 << 14) | 2 | 4, VTRCR); + + if (sh_veu_is_veu2h()) { + write_reg(&uio_mmio, 0x0cc5, VMCR00); + write_reg(&uio_mmio, 0x0950, VMCR01); + write_reg(&uio_mmio, 0x0000, VMCR02); + + write_reg(&uio_mmio, 0x397f, VMCR10); + write_reg(&uio_mmio, 0x0950, VMCR11); + write_reg(&uio_mmio, 0x3ccd, VMCR12); + + write_reg(&uio_mmio, 0x0000, VMCR20); + write_reg(&uio_mmio, 0x0950, VMCR21); + write_reg(&uio_mmio, 0x1023, VMCR22); + + write_reg(&uio_mmio, 0x00800010, VCOFFR); + } + + write_reg(&uio_mmio, 1, VEIER); /* enable interrupt in VEU */ +} + +static void sh_veu_blit(vidix_playback_t *info, int frame) +{ + unsigned long enable = 1; + unsigned long addr; + + addr = uio_mem.address + info->offsets[frame]; + + write_reg(&uio_mmio, addr + info->offset.y, VSAYR); + write_reg(&uio_mmio, addr + info->offset.u, VSACR); + + /* Enable interrupt in UIO driver */ + write(uio_dev.fd, &enable, sizeof(u_long)); + + write_reg(&uio_mmio, 1, VESTR); /* start operation */ +} + +static int sh_veu_init(void) +{ + write_reg(&uio_mmio, 0x100, VBSRR); /* reset VEU */ + return 0; +} + +static void sh_veu_destroy(void) +{ +} + +static int sh_veu_get_caps(vidix_capability_t *to) +{ + memcpy(to, &sh_veu_cap, sizeof(vidix_capability_t)); + return 0; +} + +static int sh_veu_query_fourcc(vidix_fourcc_t *to) +{ + if (to->fourcc == IMGFMT_NV12) { + to->depth = VID_DEPTH_ALL; + to->flags = VID_CAP_EXPAND | VID_CAP_SHRINK; + return 0; + } + to->depth = to->flags = 0; + return ENOSYS; +} + + +static int sh_veu_config_playback(vidix_playback_t *info) +{ + unsigned int i, y_pitch; + + switch (info->fourcc) { + case IMGFMT_NV12: + y_pitch = (info->src.w + 15) & ~15; + + info->offset.y = 0; + info->offset.u = y_pitch * info->src.h; + info->frame_size = info->offset.u + info->offset.u / 2; + break; + default: + return ENOTSUP; + } + + info->num_frames = uio_mem.size / info->frame_size; + if (info->num_frames > VID_PLAY_MAXFRAMES) + info->num_frames = VID_PLAY_MAXFRAMES; + + if (!info->num_frames) { + printf("sh_veu: %d is not enough memory for %d bytes frame\n", + (int)uio_mem.size, (int)info->frame_size); + return ENOMEM; + } + + info->dga_addr = uio_mem.iomem; + info->dest.pitch.y = info->dest.pitch.u = info->dest.pitch.v = 16; + + for (i = 0; i < info->num_frames; i++) + info->offsets[i] = info->frame_size * i; + + my_info = *info; + + printf("sh_veu: %d frames * %d bytes, total size = %d\n", + (int)info->num_frames, (int)info->frame_size, + (int)uio_mem.size); + + sh_veu_setup_planes(info, &_src, &_dst); + + printf("sh_veu: %dx%d->%dx%d@%dx%d -> %dx%d->%dx%d@%dx%d \n", + (int)info->src.w, (int)info->src.h, + (int)info->dest.w, (int)info->dest.h, + (int)info->dest.x, (int)info->dest.y, + (int)_src.width, (int)_src.height, + (int)_dst.width, (int)_dst.height, + (int)_dst.pos_x, (int)_dst.pos_y); + return 0; +} + + +static int sh_veu_playback_on(void) +{ + return 0; +} + +static int sh_veu_playback_off(void) +{ + return 0; +} + +static int sh_veu_first_frame = 1; + +static int sh_veu_frame_sel(unsigned int frame) +{ + if (!sh_veu_first_frame) + sh_veu_wait_irq(&my_info); + + sh_veu_blit(&my_info, frame); + sh_veu_first_frame = 0; + return 0; +} + +VDXDriver sh_veu_drv = { + "sh_veu", + NULL, + .probe = sh_veu_probe, + .get_caps = sh_veu_get_caps, + .query_fourcc = sh_veu_query_fourcc, + .init = sh_veu_init, + .destroy = sh_veu_destroy, + .config_playback = sh_veu_config_playback, + .playback_on = sh_veu_playback_on, + .playback_off = sh_veu_playback_off, + .frame_sel = sh_veu_frame_sel, +}; -- cgit v1.2.3