/* i810_overlay.c -- Video Overlay Support (based partly from XFree86 i810_video.c) (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. Written by Antonino Daplas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include /* FIXME: Needs to be included before dfb_types.h to work around a type clash with asm/types.h */ #include #include #include #include #include #include #include #include #include "i810.h" /* * OV0CMD - Overlay Command Register */ #define VERTICAL_CHROMINANCE_FILTER 0x70000000 #define VC_SCALING_OFF 0x00000000 #define VC_LINE_REPLICATION 0x10000000 #define VC_UP_INTERPOLATION 0x20000000 #define VC_PIXEL_DROPPING 0x50000000 #define VC_DOWN_INTERPOLATION 0x60000000 #define VERTICAL_LUMINANCE_FILTER 0x0E000000 #define VL_SCALING_OFF 0x00000000 #define VL_LINE_REPLICATION 0x02000000 #define VL_UP_INTERPOLATION 0x04000000 #define VL_PIXEL_DROPPING 0x0A000000 #define VL_DOWN_INTERPOLATION 0x0C000000 #define HORIZONTAL_CHROMINANCE_FILTER 0x01C00000 #define HC_SCALING_OFF 0x00000000 #define HC_LINE_REPLICATION 0x00400000 #define HC_UP_INTERPOLATION 0x00800000 #define HC_PIXEL_DROPPING 0x01400000 #define HC_DOWN_INTERPOLATION 0x01800000 #define HORIZONTAL_LUMINANCE_FILTER 0x00380000 #define HL_SCALING_OFF 0x00000000 #define HL_LINE_REPLICATION 0x00080000 #define HL_UP_INTERPOLATION 0x00100000 #define HL_PIXEL_DROPPING 0x00280000 #define HL_DOWN_INTERPOLATION 0x00300000 #define Y_ADJUST 0x00010000 #define OV_BYTE_ORDER 0x0000C000 #define UV_SWAP 0x00004000 #define Y_SWAP 0x00008000 #define Y_AND_UV_SWAP 0x0000C000 #define SOURCE_FORMAT 0x00003C00 #define RGB_555 0x00000800 #define RGB_565 0x00000C00 #define YUV_422 0x00002000 #define YUV_411 0x00002400 #define YUV_420 0x00003000 #define YUV_410 0x00003800 #define FIELD_MODE 0x00000020 #define FRAME_MODE 0x00000000 #define BUFFER_AND_FIELD 0x00000006 #define BUFFER0_FIELD0 0x00000000 #define BUFFER1_FIELD0 0x00000004 #define OVERLAY_ENABLE 0x00000001 #define UV_VERT_BUF1 0x02 #define UV_VERT_BUF0 0x04 #define SRC_CONSTANT_ALPHA_BLEND 1 << 31 #define MINUV_SCALE 0x1 #define I810FB_IOC_UPDATEOVERLAY _IOW ('F', 0xF7, struct i810_ovl_regs) #define I810FB_IOC_UPDATEOVERLAYCMD _IOW ('F', 0xF6, int) extern u32 i810_wait_for_space(I810DriverData *i810drv, I810DeviceData *i810dev, u32 space ); #define I810_OVERLAY_SUPPORTED_OPTIONS (DLOP_DST_COLORKEY | DLOP_DEINTERLACING) static void ovl_calc_regs (I810DriverData *i810drv, I810OverlayLayerData *i810ovl, CoreLayer *layer, CoreSurface *surface, CoreLayerRegionConfig *config, CoreSurfaceBufferLock *lock ); static void update_overlay(I810DriverData *i810drv, I810DeviceData *i810dev) { i810_writel(i810drv->mmio_base, OV0ADDR, i810dev->ovl_mem.physical); } void i810ovlOnOff( I810DriverData *idrv, I810DeviceData *idev, bool on ) { if (on) idrv->oregs->ov0cmd |= 1; else idrv->oregs->ov0cmd &= ~1; update_overlay( idrv, idev ); } static int ovlLayerDataSize( void ) { return sizeof(I810OverlayLayerData); } static DFBResult ovlInitLayer( CoreLayer *layer, void *driver_data, void *layer_data, DFBDisplayLayerDescription *description, DFBDisplayLayerConfig *config, DFBColorAdjustment *adjustment ) { I810OverlayLayerData *i810ovl = (I810OverlayLayerData *) layer_data; I810DriverData *idrv = driver_data; I810DeviceData *idev = idrv->idev; idev->iovl = i810ovl; idrv->oregs = (volatile struct i810_ovl_regs*) idrv->ovl_base; memset( (void*) idrv->oregs, 0, sizeof(struct i810_ovl_regs) ); /* set_capabilities */ description->caps = DLCAPS_SURFACE | DLCAPS_SCREEN_LOCATION | DLCAPS_BRIGHTNESS | DLCAPS_CONTRAST | DLCAPS_SATURATION | DLCAPS_DST_COLORKEY | DLCAPS_OPACITY | DLCAPS_DEINTERLACING; description->type = DLTF_GRAPHICS | DLTF_VIDEO | DLTF_STILL_PICTURE; /* set name */ snprintf( description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "Intel 810/815 Overlay" ); /* fill out the default configuration */ config->flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE | DLCONF_OPTIONS; config->width = 640; config->height = 480; config->pixelformat = DSPF_YUY2; config->buffermode = DLBM_FRONTONLY; config->options = DLOP_NONE; /* fill out default color adjustment, only fields set in flags will be accepted from applications */ adjustment->flags = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_SATURATION; adjustment->brightness = 0x8000; adjustment->contrast = 0x8000; adjustment->saturation = 0x8000; idrv->oregs->yrgb_vph = 0; idrv->oregs->uv_vph = 0; idrv->oregs->horz_ph = 0; idrv->oregs->init_ph = 0; idrv->oregs->dwinpos = 0; idrv->oregs->dwinsz = (640 << 16) | 480; idrv->oregs->swid = 640 | (640 << 15); idrv->oregs->swidqw = (640 >> 3) | (640 << 12); idrv->oregs->sheight = 480 | (480 << 15); idrv->oregs->yrgbscale = 0x80004000; /* scale factor 1 */ idrv->oregs->uvscale = 0x80004000; /* scale factor 1 */ idrv->oregs->ov0clrc0 = 0x4000; /* brightness: 0 contrast: 1.0 */ idrv->oregs->ov0clrc1 = 0x80; /* saturation: bypass */ idrv->oregs->sclrkvh = 0; idrv->oregs->sclrkvl = 0; idrv->oregs->sclrkm = 0; /* source color key disable */ idrv->oregs->ov0conf = 0; /* two 720 pixel line buffers */ idrv->oregs->ov0cmd = VC_UP_INTERPOLATION | HC_UP_INTERPOLATION | Y_ADJUST | YUV_420; update_overlay( idrv, idev ); /* * FIXME: If the fence registers are enabled, then the buffer pointers * require specific alignment. This is a problem with planar formats * which have separate pointers for each of the U and V planes. Packed * formats should not be a problem. */ i810ovl->planar_bug = 0; if (i810_readl(idrv->mmio_base, FENCE) & 1) i810ovl->planar_bug = 1; return DFB_OK; } static DFBResult ovlTestRegion( CoreLayer *layer, void *driver_data, void *layer_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags *failed ) { DFBDisplayLayerConfigFlags fail = 0; I810OverlayLayerData *i810ovl = (I810OverlayLayerData *) layer_data; if (config->options & ~I810_OVERLAY_SUPPORTED_OPTIONS) fail |= DLCONF_OPTIONS; switch (config->format) { case DSPF_I420: case DSPF_YV12: case DSPF_YUY2: case DSPF_UYVY: break; default: fail |= DLCONF_PIXELFORMAT; } if (i810ovl->planar_bug && (config->format == DSPF_I420 || config->format == DSPF_YV12 )) { D_DEBUG("Sorry, planar formats will not work when memory tiling " "is enabled\n"); fail |= DLCONF_PIXELFORMAT; } if (config->width > 1440 || config->width < 1) fail |= DLCONF_WIDTH; if (config->height > 1023 || config->height < 1) fail |= DLCONF_HEIGHT; if (failed) *failed = fail; if (fail) return DFB_UNSUPPORTED; return DFB_OK; } static DFBResult ovlSetRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags updated, CoreSurface *surface, CorePalette *palette, CoreSurfaceBufferLock *lock) { I810DriverData *i810drv = (I810DriverData *) driver_data; I810OverlayLayerData *i810ovl = (I810OverlayLayerData *) layer_data; i810ovl->config = *config; ovl_calc_regs (i810drv, i810ovl, layer, surface, config, lock); update_overlay(i810drv, i810drv->idev); i810ovlOnOff(i810drv, i810drv->idev, 1); return DFB_OK; } static DFBResult ovlRemoveRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data ) { I810DriverData *i810drv = (I810DriverData *) driver_data; /* disable overlay */ i810ovlOnOff( i810drv, i810drv->idev, 0 ); return DFB_OK; } static DFBResult ovlFlipRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, DFBSurfaceFlipFlags flags, CoreSurfaceBufferLock *lock ) { I810DriverData *i810drv = (I810DriverData *) driver_data; I810OverlayLayerData *i810ovl = (I810OverlayLayerData *) layer_data; u32 current_buffer; dfb_surface_flip( surface, false ); /* select buffer */ current_buffer = (i810drv->oregs->ov0cmd & 4) >> 2; if (current_buffer) { i810drv->oregs->ov0cmd &= ~4; } else { i810drv->oregs->ov0cmd |= 4; } ovl_calc_regs (i810drv, i810ovl, layer, surface, &i810ovl->config, lock); update_overlay(i810drv, i810drv->idev); if (flags & DSFLIP_WAIT) dfb_screen_wait_vsync( dfb_screens_at( DSCID_PRIMARY ) ); return DFB_OK; } static DFBResult ovlSetColorAdjustment( CoreLayer *layer, void *driver_data, void *layer_data, DFBColorAdjustment *adj ) { I810DriverData *i810drv = (I810DriverData*) driver_data; i810drv->oregs->ov0clrc0 = (((adj->brightness >> 8) - 128) & 0xFF) | ((adj->contrast >> 9) << 8); i810drv->oregs->ov0clrc1 = (adj->saturation >> 8) & 0xFF; update_overlay(i810drv, i810drv->idev); return DFB_OK; } static DFBResult ovlSetInputField( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, int field ) { I810DriverData *i810drv = (I810DriverData*) driver_data; i810drv->oregs->ov0cmd &= ~2; i810drv->oregs->ov0cmd |= (field) ? 2 : 0; update_overlay(i810drv, i810drv->idev); return DFB_OK; } DisplayLayerFuncs i810OverlayFuncs = { .LayerDataSize = ovlLayerDataSize, .InitLayer = ovlInitLayer, .TestRegion = ovlTestRegion, .SetRegion = ovlSetRegion, .RemoveRegion = ovlRemoveRegion, .FlipRegion = ovlFlipRegion, .SetColorAdjustment = ovlSetColorAdjustment, .SetInputField = ovlSetInputField, }; static void ovl_calc_regs (I810DriverData *i810drv, I810OverlayLayerData *i810ovl, CoreLayer *layer, CoreSurface *surface, CoreLayerRegionConfig *config, CoreSurfaceBufferLock *lock ) { u32 swidth = 0, y_offset, v_offset = 0, u_offset = 0; u32 drw_w, src_w, drw_h, src_h, xscaleInt, xscaleFract, yscaleInt; u32 xscaleFractUV = 0, xscaleIntUV, yscaleIntUV = 0, yscaleFract, yscaleFractUV = 0; DFBSurfacePixelFormat primary_format; drw_w = config->dest.w; drw_h = config->dest.h; src_w = surface->config.size.w; src_h = surface->config.size.h; if (config->options & DLOP_DEINTERLACING) src_h >>= 1; /* reset command register except the enable bit and buffer select bits */ i810drv->oregs->ov0cmd &= 7; /* Set source dimension in bytes */ switch (surface->config.format) { case DSPF_I420: case DSPF_YV12: swidth = (src_w + 7) & ~7; i810drv->oregs->swid = (swidth << 15) | swidth; i810drv->oregs->swidqw = (swidth << 12) | (swidth >> 3); break; case DSPF_UYVY: case DSPF_YUY2: swidth = ((src_w + 3) & ~3) << 1; i810drv->oregs->swid = swidth; i810drv->oregs->swidqw = swidth >> 3; break; default: break; } i810drv->oregs->sheight = src_h | (src_h << 15); /* select buffer size */ if (swidth > 720) i810drv->oregs->ov0conf = 1; else i810drv->oregs->ov0conf = 0; /* set dest window position and dimension */ i810drv->oregs->dwinpos = (config->dest.y << 16) | config->dest.x; i810drv->oregs->dwinsz = (drw_h << 16) | drw_w; /* Set buffer pointers */ y_offset = (dfb_gfxcard_memory_physical(NULL, lock->offset)); switch (surface->config.format) { case DSPF_I420: u_offset = y_offset + surface->config.size.h * lock->pitch; v_offset = u_offset + ((surface->config.size.h >> 1) * (lock->pitch >> 1)); break; case DSPF_YV12: v_offset = y_offset + surface->config.size.h * lock->pitch; u_offset = v_offset + ((surface->config.size.h >> 1) * (lock->pitch >> 1)); break; default: break; } if (i810drv->oregs->ov0cmd & 4) { i810drv->oregs->obuf_1y = y_offset; i810drv->oregs->obuf_1v = v_offset; i810drv->oregs->obuf_1u = u_offset; } else { i810drv->oregs->obuf_0y = y_offset; i810drv->oregs->obuf_0v = v_offset; i810drv->oregs->obuf_0u = u_offset; } /* set scaling */ i810drv->oregs->yrgbscale = 0x80004000; i810drv->oregs->uvscale = 0x80004000; i810drv->oregs->ov0cmd |= VC_UP_INTERPOLATION | HC_UP_INTERPOLATION | Y_ADJUST | FRAME_MODE; if (config->options & DLOP_DEINTERLACING) i810drv->oregs->ov0cmd |= FIELD_MODE; if ((drw_w != src_w) || (drw_h != src_h)) { xscaleInt = (src_w / drw_w) & 0x3; xscaleFract = (src_w << 12) / drw_w; yscaleInt = (src_h / drw_h) & 0x3; yscaleFract = (src_h << 12) / drw_h; i810drv->oregs->yrgbscale = (xscaleInt << 15) | ((xscaleFract & 0xFFF) << 3) | (yscaleInt) | ((yscaleFract & 0xFFF) << 20); if (drw_w > src_w) { i810drv->oregs->ov0cmd &= ~HORIZONTAL_CHROMINANCE_FILTER; i810drv->oregs->ov0cmd &= ~HORIZONTAL_LUMINANCE_FILTER; i810drv->oregs->ov0cmd |= (HC_UP_INTERPOLATION | HL_UP_INTERPOLATION); } if (drw_h > src_h) { i810drv->oregs->ov0cmd &= ~VERTICAL_CHROMINANCE_FILTER; i810drv->oregs->ov0cmd &= ~VERTICAL_LUMINANCE_FILTER; i810drv->oregs->ov0cmd |= (VC_UP_INTERPOLATION | VL_UP_INTERPOLATION); } if (drw_w < src_w) { i810drv->oregs->ov0cmd &= ~HORIZONTAL_CHROMINANCE_FILTER; i810drv->oregs->ov0cmd &= ~HORIZONTAL_LUMINANCE_FILTER; i810drv->oregs->ov0cmd |= (HC_DOWN_INTERPOLATION | HL_DOWN_INTERPOLATION); } if (drw_h < src_h) { i810drv->oregs->ov0cmd &= ~VERTICAL_CHROMINANCE_FILTER; i810drv->oregs->ov0cmd &= ~VERTICAL_LUMINANCE_FILTER; i810drv->oregs->ov0cmd |= (VC_DOWN_INTERPOLATION | VL_DOWN_INTERPOLATION); } if (xscaleFract) { xscaleFractUV = xscaleFract >> MINUV_SCALE; i810drv->oregs->ov0cmd &= ~HC_DOWN_INTERPOLATION; i810drv->oregs->ov0cmd |= HC_UP_INTERPOLATION; } if (xscaleInt) { xscaleIntUV = xscaleInt >> MINUV_SCALE; if (xscaleIntUV) { i810drv->oregs->ov0cmd &= ~HC_UP_INTERPOLATION; } } if (yscaleFract) { yscaleFractUV = yscaleFract >> MINUV_SCALE; i810drv->oregs->ov0cmd &= ~VC_DOWN_INTERPOLATION; i810drv->oregs->ov0cmd |= VC_UP_INTERPOLATION; } if (yscaleInt) { yscaleIntUV = yscaleInt >> MINUV_SCALE; if (yscaleIntUV) { i810drv->oregs->ov0cmd &= ~VC_UP_INTERPOLATION; i810drv->oregs->ov0cmd |= VC_DOWN_INTERPOLATION; } } i810drv->oregs->uvscale = yscaleIntUV | ((xscaleFractUV & 0xFFF) << 3) | ((yscaleFractUV & 0xFFF) << 20); } switch(surface->config.format) { case DSPF_YV12: case DSPF_I420: /* set UV vertical phase to -0.25 */ i810drv->oregs->uv_vph = 0x30003000; i810drv->oregs->init_ph = UV_VERT_BUF0 | UV_VERT_BUF1; i810drv->oregs->ov0stride = (lock->pitch) | (lock->pitch << 15); i810drv->oregs->ov0cmd &= ~SOURCE_FORMAT; i810drv->oregs->ov0cmd |= YUV_420; break; case DSPF_UYVY: case DSPF_YUY2: i810drv->oregs->uv_vph = 0; i810drv->oregs->init_ph = 0; i810drv->oregs->ov0stride = lock->pitch; i810drv->oregs->ov0cmd &= ~SOURCE_FORMAT; i810drv->oregs->ov0cmd |= YUV_422; i810drv->oregs->ov0cmd &= ~OV_BYTE_ORDER; if (surface->config.format == DSPF_UYVY) i810drv->oregs->ov0cmd |= Y_SWAP; break; default: D_BUG("unexpected pixelformat"); break; } /* Set alpha window */ i810drv->oregs->awinpos = i810drv->oregs->dwinpos; i810drv->oregs->awinsz = i810drv->oregs->dwinsz; /* * Destination color keying. */ primary_format = dfb_primary_layer_pixelformat(); i810drv->oregs->dclrkv = dfb_color_to_pixel( primary_format, config->dst_key.r, config->dst_key.g, config->dst_key.b ); i810drv->oregs->dclrkm = (1 << DFB_COLOR_BITS_PER_PIXEL( primary_format )) - 1; if (config->options & DLOP_DST_COLORKEY) i810drv->oregs->dclrkm |= 0x80000000; }