/* (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 Oliver Schwartz and Claudio Ciccani . 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "nvidia.h" #include "nvidia_regs.h" #include "nvidia_accel.h" typedef struct { CoreLayerRegionConfig config; CoreSurface *videoSurface; CoreSurfaceBufferLock *lock; short brightness; short contrast; short hue; short saturation; int field; struct { u32 BUFFER; u32 STOP; u32 UVBASE_0; u32 UVBASE_1; u32 UVOFFSET_0; u32 UVOFFSET_1; u32 BASE_0; u32 BASE_1; u32 OFFSET_0; u32 OFFSET_1; u32 SIZE_IN_0; u32 SIZE_IN_1; u32 POINT_IN_0; u32 POINT_IN_1; u32 DS_DX_0; u32 DS_DX_1; u32 DT_DY_0; u32 DT_DY_1; u32 POINT_OUT_0; u32 POINT_OUT_1; u32 SIZE_OUT_0; u32 SIZE_OUT_1; u32 FORMAT_0; u32 FORMAT_1; } regs; } NVidiaOverlayLayerData; static void ov0_set_regs ( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfigFlags flags ); static void ov0_calc_regs ( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags flags ); static void ov0_set_colorkey( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfig *config ); static void ov0_set_csc ( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0 ); #define OV0_SUPPORTED_OPTIONS \ ( DLOP_DST_COLORKEY | DLOP_DEINTERLACING ) /**********************/ static int ov0LayerDataSize( void ) { return sizeof(NVidiaOverlayLayerData); } static DFBResult ov0InitLayer( CoreLayer *layer, void *driver_data, void *layer_data, DFBDisplayLayerDescription *description, DFBDisplayLayerConfig *config, DFBColorAdjustment *adjustment ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; NVidiaOverlayLayerData *nvov0 = (NVidiaOverlayLayerData*) layer_data; /* set capabilities and type */ description->caps = DLCAPS_SURFACE | DLCAPS_SCREEN_LOCATION | DLCAPS_BRIGHTNESS | DLCAPS_CONTRAST | DLCAPS_SATURATION | DLCAPS_HUE | DLCAPS_DST_COLORKEY | DLCAPS_DEINTERLACING; description->type = DLTF_VIDEO | DLTF_STILL_PICTURE; /* set name */ snprintf( description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "NVidia 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 | DCAF_HUE; adjustment->brightness = 0x8000; adjustment->contrast = 0x8000; adjustment->saturation = 0x8000; adjustment->hue = 0x8000; /* reset overlay */ nvov0->brightness = 0; nvov0->contrast = 4096; nvov0->hue = 0; nvov0->saturation = 4096; ov0_set_csc( nvdrv, nvov0 ); return DFB_OK; } static DFBResult ov0Remove( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; volatile u8 *mmio = nvdrv->mmio_base; /* disable overlay */ nv_out32( mmio, PVIDEO_STOP, PVIDEO_STOP_OVERLAY_ACTIVE | PVIDEO_STOP_METHOD_IMMEDIATELY ); nv_out32( mmio, PVIDEO_BUFFER, 0 ); return DFB_OK; } static DFBResult ov0TestRegion(CoreLayer *layer, void *driver_data, void *layer_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags *failed ) { NVidiaDriverData *nvdrv = driver_data; NVidiaDeviceData *nvdev = nvdrv->device_data; CoreLayerRegionConfigFlags fail = CLRCF_NONE; /* check for unsupported options */ if (config->options & ~OV0_SUPPORTED_OPTIONS) fail |= CLRCF_OPTIONS; /* check buffermode */ switch (config->buffermode) { case DLBM_FRONTONLY: case DLBM_BACKSYSTEM: case DLBM_BACKVIDEO: case DLBM_TRIPLE: break; default: fail |= CLRCF_BUFFERMODE; break; } /* check pixel format */ switch (config->format) { case DSPF_YUY2: case DSPF_UYVY: break; case DSPF_NV12: /*case DSPF_NV21:*/ if (nvdev->arch < NV_ARCH_30) fail |= CLRCF_FORMAT; break; default: fail |= CLRCF_FORMAT; break; } /* check width */ if (config->width > 2046 || config->width < 1) fail |= CLRCF_WIDTH; /* check height */ if (config->height > 2046 || config->height < 1) fail |= CLRCF_HEIGHT; /* write back failing fields */ if (failed) *failed = fail; /* return failure if any field failed */ if (fail) return DFB_UNSUPPORTED; return DFB_OK; } static DFBResult ov0SetRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags updated, CoreSurface *surface, CorePalette *palette, CoreSurfaceBufferLock *lock ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; NVidiaOverlayLayerData *nvov0 = (NVidiaOverlayLayerData*) layer_data; /* remember configuration */ nvov0->config = *config; nvov0->videoSurface = surface; nvov0->lock = lock; /* set configuration */ if (updated & (CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_FORMAT | CLRCF_SOURCE | CLRCF_DEST | CLRCF_OPTIONS | CLRCF_OPACITY)) { ov0_calc_regs( nvdrv, nvov0, config, updated ); ov0_set_regs( nvdrv, nvov0, updated ); } /* set destination colorkey */ if (updated & CLRCF_DSTKEY) ov0_set_colorkey( nvdrv, nvov0, config ); return DFB_OK; } static DFBResult ov0FlipRegion ( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, DFBSurfaceFlipFlags flags, CoreSurfaceBufferLock *lock ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; NVidiaOverlayLayerData *nvov0 = (NVidiaOverlayLayerData*) layer_data; nvov0->videoSurface = surface; nvov0->lock = lock; dfb_surface_flip( nvov0->videoSurface, false ); ov0_calc_regs( nvdrv, nvov0, &nvov0->config, CLRCF_SURFACE ); ov0_set_regs( nvdrv, nvov0, CLRCF_SURFACE ); if (flags & DSFLIP_WAIT) dfb_layer_wait_vsync( layer ); return DFB_OK; } static DFBResult ov0SetColorAdjustment( CoreLayer *layer, void *driver_data, void *layer_data, DFBColorAdjustment *adj ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; NVidiaOverlayLayerData *nvov0 = (NVidiaOverlayLayerData*) layer_data; if (adj->flags & DCAF_BRIGHTNESS) { nvov0->brightness = (adj->brightness >> 8) - 128; D_DEBUG( "DirectFB/NVidia/Overlay: brightness=%i\n", nvov0->brightness ); } if (adj->flags & DCAF_CONTRAST) { nvov0->contrast = 8191 - (adj->contrast >> 3); /* contrast inverted ?! */ D_DEBUG( "DirectFB/NVidia/Overlay: contrast=%i\n", nvov0->contrast ); } if (adj->flags & DCAF_SATURATION) { nvov0->saturation = adj->saturation >> 3; D_DEBUG( "DirectFB/NVidia/Overlay: saturation=%i\n", nvov0->saturation ); } if (adj->flags & DCAF_HUE) { nvov0->hue = (adj->hue / 182 - 180) % 360; D_DEBUG( "DirectFB/NVidia/Overlay: hue=%i\n", nvov0->hue ); } ov0_set_csc( nvdrv, nvov0 ); return DFB_OK; } static DFBResult ov0SetInputField( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, int field ) { NVidiaDriverData *nvdrv = (NVidiaDriverData*) driver_data; NVidiaOverlayLayerData *nvov0 = (NVidiaOverlayLayerData*) layer_data; nvov0->field = field; nvov0->regs.BUFFER = 1 << (field << 2); nv_out32( nvdrv->mmio_base, PVIDEO_BUFFER, nvov0->regs.BUFFER ); return DFB_OK; } DisplayLayerFuncs nvidiaOverlayFuncs = { .LayerDataSize = ov0LayerDataSize, .InitLayer = ov0InitLayer, .SetRegion = ov0SetRegion, .RemoveRegion = ov0Remove, .TestRegion = ov0TestRegion, .FlipRegion = ov0FlipRegion, .SetColorAdjustment = ov0SetColorAdjustment, .SetInputField = ov0SetInputField, }; /* internal */ static void ov0_set_regs( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfigFlags flags ) { volatile u8 *mmio = nvdrv->mmio_base; if (flags & CLRCF_SURFACE) { if (DFB_PLANAR_PIXELFORMAT(nvov0->config.format)) { nv_out32( mmio, PVIDEO_UVBASE_0, nvov0->regs.UVBASE_0 ); nv_out32( mmio, PVIDEO_UVBASE_1, nvov0->regs.UVBASE_1 ); nv_out32( mmio, PVIDEO_UVOFFSET_0, nvov0->regs.UVOFFSET_0 ); nv_out32( mmio, PVIDEO_UVOFFSET_1, nvov0->regs.UVOFFSET_1 ); } nv_out32( mmio, PVIDEO_BASE_0, nvov0->regs.BASE_0 ); nv_out32( mmio, PVIDEO_BASE_1, nvov0->regs.BASE_1 ); nv_out32( mmio, PVIDEO_OFFSET_0, nvov0->regs.OFFSET_0 ); nv_out32( mmio, PVIDEO_OFFSET_1, nvov0->regs.OFFSET_1 ); } if (flags & (CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_OPTIONS)) { nv_out32( mmio, PVIDEO_SIZE_IN_0, nvov0->regs.SIZE_IN_0 ); nv_out32( mmio, PVIDEO_SIZE_IN_1, nvov0->regs.SIZE_IN_1 ); } if (flags & (CLRCF_SOURCE | CLRCF_DEST | CLRCF_OPTIONS)) { nv_out32( mmio, PVIDEO_POINT_IN_0, nvov0->regs.POINT_IN_0 ); nv_out32( mmio, PVIDEO_POINT_IN_1, nvov0->regs.POINT_IN_1 ); nv_out32( mmio, PVIDEO_DS_DX_0, nvov0->regs.DS_DX_0 ); nv_out32( mmio, PVIDEO_DS_DX_1, nvov0->regs.DS_DX_1 ); nv_out32( mmio, PVIDEO_DT_DY_0, nvov0->regs.DT_DY_0 ); nv_out32( mmio, PVIDEO_DT_DY_1, nvov0->regs.DT_DY_1 ); } if (flags & CLRCF_DEST) { nv_out32( mmio, PVIDEO_POINT_OUT_0, nvov0->regs.POINT_OUT_0 ); nv_out32( mmio, PVIDEO_POINT_OUT_1, nvov0->regs.POINT_OUT_1 ); nv_out32( mmio, PVIDEO_SIZE_OUT_0, nvov0->regs.SIZE_OUT_0 ); nv_out32( mmio, PVIDEO_SIZE_OUT_1, nvov0->regs.SIZE_OUT_1 ); } if (flags & (CLRCF_FORMAT | CLRCF_SURFACE | CLRCF_OPTIONS)) { nv_out32( mmio, PVIDEO_FORMAT_0, nvov0->regs.FORMAT_0 ); nv_out32( mmio, PVIDEO_FORMAT_1, nvov0->regs.FORMAT_1 ); } nv_out32( mmio, PVIDEO_BUFFER, nvov0->regs.BUFFER ); nv_out32( mmio, PVIDEO_STOP, nvov0->regs.STOP ); } static void ov0_calc_regs( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags flags ) { NVidiaDeviceData *nvdev = nvdrv->device_data; if (flags & (CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_SOURCE | CLRCF_DEST | CLRCF_OPTIONS)) { int width = config->width; int height = config->height; DFBRectangle source = config->source; DFBRectangle dest = config->dest; source.x <<= 4; source.y <<= 4; if (dest.x < 0) { source.x -= (dest.x * source.w << 4) / dest.w; source.w += dest.x * source.w / dest.w; dest.w += dest.x; dest.x = 0; } if (dest.y < 0) { source.y -= (dest.y * source.h << 4) / dest.h; source.h += dest.y * source.h / dest.h; dest.h += dest.y; dest.y = 0; } if (config->options & DLOP_DEINTERLACING) { height /= 2; source.y /= 2; source.h /= 2; } if (source.w < 1 || source.h < 1 || dest.w < 1 || dest.h < 1) { nvov0->regs.STOP = PVIDEO_STOP_OVERLAY_ACTIVE | PVIDEO_STOP_METHOD_NORMALLY; return; } nvov0->regs.SIZE_IN_0 = nvov0->regs.SIZE_IN_1 = ((height << 16) & PVIDEO_SIZE_IN_HEIGHT_MSK) | ( width & PVIDEO_SIZE_IN_WIDTH_MSK); nvov0->regs.POINT_IN_0 = nvov0->regs.POINT_IN_1 = ((source.y << 16) & PVIDEO_POINT_IN_T_MSK) | ( source.x & PVIDEO_POINT_IN_S_MSK); nvov0->regs.DS_DX_0 = nvov0->regs.DS_DX_1 = (source.w << 20) / dest.w; nvov0->regs.DT_DY_0 = nvov0->regs.DT_DY_1 = (source.h << 20) / dest.h; nvov0->regs.POINT_OUT_0 = nvov0->regs.POINT_OUT_1 = ((dest.y << 16) & PVIDEO_POINT_OUT_Y_MSK) | ( dest.x & PVIDEO_POINT_OUT_X_MSK); nvov0->regs.SIZE_OUT_0 = nvov0->regs.SIZE_OUT_1 = ((dest.h << 16) & PVIDEO_SIZE_OUT_HEIGHT_MSK) | ( dest.w & PVIDEO_SIZE_OUT_WIDTH_MSK); } if (flags & (CLRCF_SURFACE | CLRCF_FORMAT | CLRCF_OPTIONS)) { CoreSurfaceBufferLock *lock = nvov0->lock; u32 format; if (config->options & DLOP_DEINTERLACING) format = (lock->pitch*2) & PVIDEO_FORMAT_PITCH_MSK; else format = lock->pitch & PVIDEO_FORMAT_PITCH_MSK; if (DFB_PLANAR_PIXELFORMAT(config->format)) format |= PVIDEO_FORMAT_PLANAR_NV; if (config->format == DSPF_UYVY) format |= PVIDEO_FORMAT_COLOR_YB8CR8YA8CB8; else format |= PVIDEO_FORMAT_COLOR_CR8YB8CB8YA8; if (config->options & DLOP_DST_COLORKEY) format |= PVIDEO_FORMAT_DISPLAY_COLOR_KEY_EQUAL; /* Use Buffer 0 for Odd field */ nvov0->regs.OFFSET_0 = (nvdev->fb_offset + lock->offset) & PVIDEO_OFFSET_MSK; /* Use Buffer 1 for Even field */ nvov0->regs.OFFSET_1 = nvov0->regs.OFFSET_0 + lock->pitch; if (DFB_PLANAR_PIXELFORMAT(config->format)) { CoreSurface *surface = nvov0->videoSurface; nvov0->regs.UVOFFSET_0 = (nvov0->regs.OFFSET_0 + lock->pitch * surface->config.size.h) & PVIDEO_UVOFFSET_MSK; nvov0->regs.UVOFFSET_1 = nvov0->regs.UVOFFSET_0 + lock->pitch; } nvov0->regs.FORMAT_0 = nvov0->regs.FORMAT_1 = format; } nvov0->regs.BUFFER = 1 << (nvov0->field << 2); nvov0->regs.STOP = (config->opacity) ? PVIDEO_STOP_OVERLAY_INACTIVE : PVIDEO_STOP_OVERLAY_ACTIVE; nvov0->regs.STOP |= PVIDEO_STOP_METHOD_NORMALLY; } static void ov0_set_colorkey( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0, CoreLayerRegionConfig *config ) { u32 key; key = dfb_color_to_pixel( dfb_primary_layer_pixelformat(), config->dst_key.r, config->dst_key.g, config->dst_key.b ); nv_out32( nvdrv->mmio_base, PVIDEO_COLOR_KEY, key ); } static void ov0_set_csc( NVidiaDriverData *nvdrv, NVidiaOverlayLayerData *nvov0 ) { volatile u8 *mmio = nvdrv->mmio_base; s32 satSine; s32 satCosine; double angle; angle = (double) nvov0->hue * M_PI / 180.0; satSine = nvov0->saturation * sin(angle); if (satSine < -1024) satSine = -1024; satCosine = nvov0->saturation * cos(angle); if (satCosine < -1024) satCosine = -1024; nv_out32( mmio, PVIDEO_LUMINANCE_0, (nvov0->brightness << 16) | (nvov0->contrast & 0xffff) ); nv_out32( mmio, PVIDEO_LUMINANCE_1, (nvov0->brightness << 16) | (nvov0->contrast & 0xffff) ); nv_out32( mmio, PVIDEO_CHROMINANCE_0, (satSine << 16) | (satCosine & 0xffff) ); nv_out32( mmio, PVIDEO_CHROMINANCE_1, (satSine << 16) | (satCosine & 0xffff) ); }