/* TI Davinci driver - Video Layer (c) Copyright 2007 Telio AG Written by Denis Oliver Kropp Code is derived from VMWare driver. (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) (c) Copyright 2000-2004 Convergence (integrated media) GmbH All rights reserved. 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. */ //#define DIRECT_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include "davincifb.h" #include "davinci_gfxdriver.h" #include "davinci_video.h" #define D_VIDERROR(x...) do {} while (0) D_DEBUG_DOMAIN( Davinci_Video, "Davinci/Video", "TI Davinci Video" ); /**********************************************************************************************************************/ static DFBResult ShowBuffer( DavinciDriverData *ddrv, DavinciVideoLayerData *dvid, CoreSurfaceBufferLock *lock, const DFBRectangle *area, DFBSurfaceFlipFlags flags ); static void SetupResizerParams( vpfe_resizer_params_t *params, int srcWidth, int srcHeight, int outWidth, int outHeight, int *ret_outWidth, int *ret_outHeight ); /**********************************************************************************************************************/ static int videoLayerDataSize( void ) { return sizeof(DavinciVideoLayerData); } static DFBResult videoInitLayer( CoreLayer *layer, void *driver_data, void *layer_data, DFBDisplayLayerDescription *description, DFBDisplayLayerConfig *config, DFBColorAdjustment *adjustment ) { int ret; DavinciDriverData *ddrv = driver_data; DavinciVideoLayerData *dvid = layer_data; D_DEBUG_AT( Davinci_Video, "%s()\n", __FUNCTION__ ); /* Initialize with configuration from VID0 to start with a fullscreen (unscaled) layer */ ret = ioctl( ddrv->fb[VID0].fd, FBIOGET_VSCREENINFO, &dvid->var ); if (ret) { D_PERROR( "Davinci/Video: FBIOGET_VSCREENINFO (fb%d) failed!\n", VID0 ); return DFB_INIT; } /* Disable VID0 (unused) */ ret = ioctl( ddrv->fb[VID0].fd, FBIO_ENABLE_DISABLE_WIN, 0 ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_ENABLE_DISABLE_WIN (fb%d - %d)!\n", VID0, 0 ); /* Disable VID1 (our layer) */ ret = ioctl( ddrv->fb[VID1].fd, FBIO_ENABLE_DISABLE_WIN, 0 ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_ENABLE_DISABLE_WIN (fb%d - %d)!\n", VID1, 0 ); /* set capabilities and type */ description->caps = DLCAPS_SURFACE | DLCAPS_SCREEN_LOCATION; description->type = DLTF_VIDEO | DLTF_STILL_PICTURE; /* set name */ snprintf( description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "TI Davinci Video" ); /* fill out the default configuration */ config->flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE | DLCONF_OPTIONS; config->width = dvid->var.xres; config->height = dvid->var.yres; config->pixelformat = DSPF_UYVY; config->buffermode = DLBM_FRONTONLY; config->options = DLOP_NONE; return DFB_OK; } static DFBResult videoTestRegion( CoreLayer *layer, void *driver_data, void *layer_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags *failed ) { CoreLayerRegionConfigFlags fail = 0; D_DEBUG_AT( Davinci_Video, "%s()\n", __FUNCTION__ ); DFB_CORE_LAYER_REGION_CONFIG_DEBUG_AT( Davinci_Video, config ); if (config->options & ~DAVINCI_VIDEO_SUPPORTED_OPTIONS) fail |= CLRCF_OPTIONS; switch (config->format) { case DSPF_UYVY: break; default: fail |= CLRCF_FORMAT; } if (config->width < 8 || config->width > 1920) fail |= CLRCF_WIDTH; if (config->height < 8 || config->height > 1080) fail |= CLRCF_HEIGHT; if (config->dest.x < 0 || config->dest.y < 0) fail |= CLRCF_DEST; if (config->dest.x + config->dest.w > 1920) fail |= CLRCF_DEST; if (config->dest.y + config->dest.h > 1080) fail |= CLRCF_DEST; if (failed) *failed = fail; if (fail) { D_DEBUG_AT( Davinci_Video, " -> FAILED (0x%08x)\n", fail ); return DFB_UNSUPPORTED; } D_DEBUG_AT( Davinci_Video, " -> OK\n" ); return DFB_OK; } static DFBResult videoSetRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags updated, CoreSurface *surface, CorePalette *palette, CoreSurfaceBufferLock *lock ) { int ret; DavinciDriverData *ddrv = driver_data; DavinciDeviceData *ddev = ddrv->ddev; DavinciVideoLayerData *dvid = layer_data; CoreLayerRegionConfig *old = &dvid->config; D_DEBUG_AT( Davinci_Video, "%s( updated 0x%08x, surface %p )\n", __FUNCTION__, updated, surface ); DFB_CORE_LAYER_REGION_CONFIG_DEBUG_AT( Davinci_Video, config ); D_ASSERT( ddrv != NULL ); D_ASSERT( ddev != NULL ); D_ASSERT( dvid != NULL ); /* Update output size? */ if ((updated & CLRCF_DEST) && (config->dest.w != old->dest.w || config->dest.h != old->dest.h)) { vpbe_window_position_t win_pos; D_DEBUG_AT( Davinci_Video, " => dest %4dx%4d\n", config->dest.w, config->dest.h ); ret = ioctl( ddrv->fb[VID1].fd, FBIO_ENABLE_DISABLE_WIN, 0 ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_ENABLE_DISABLE_WIN (fb%d - %d)!\n", VID1, 0 ); dvid->enabled = false; /*********************************** Start workaround ***********************************/ win_pos.xpos = 0; win_pos.ypos = 0; ret = ioctl( ddrv->fb[VID1].fd, FBIO_SETPOS, &win_pos ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_SETPOS (fb%d - %d,%d) failed!\n", VID1, win_pos.xpos, win_pos.ypos ); dvid->var.yoffset = 0; /*********************************** End workaround ***********************************/ /* Set output width and height. */ dvid->var.xres = config->dest.w; dvid->var.yres = config->dest.h; dvid->var.yres_virtual = ddrv->fb[VID1].size / lock->pitch; ret = ioctl( ddrv->fb[VID1].fd, FBIOPUT_VSCREENINFO, &dvid->var ); if (ret) D_PERROR( "Davinci/Video: FBIOPUT_VSCREENINFO (fb%d) failed!\n", VID1 ); /* Read back new pitch etc. */ ret = ioctl( ddrv->fb[VID1].fd, FBIOGET_FSCREENINFO, &ddev->fix[VID1] ); if (ret) D_PERROR( "Davinci/Video: FBIOGET_FSCREENINFO (fb%d) failed!\n", VID1 ); } /* Update output position? */ if (updated & CLRCF_DEST) { vpbe_window_position_t win_pos; D_DEBUG_AT( Davinci_Video, " => dest %4d,%4d\n", config->dest.x, config->dest.y ); if (dvid->enabled) ioctl( ddrv->fb[VID1].fd, FBIO_WAITFORVSYNC ); /* Set horizontal and vertical offset. */ win_pos.xpos = config->dest.x; win_pos.ypos = config->dest.y; ret = ioctl( ddrv->fb[VID1].fd, FBIO_SETPOS, &win_pos ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_SETPOS (fb%d - %d,%d) failed!\n", VID1, config->dest.x, config->dest.y ); } /* Update format? */ if (updated & CLRCF_FORMAT) { vpbe_video_config_params_t params; params.cb_cr_order = (config->format == DSPF_YUY2) ? 1 : 0; params.exp_info.horizontal = VPBE_DISABLE; params.exp_info.vertical = VPBE_DISABLE; ret = ioctl( ddrv->fb[VID1].fd, FBIO_SET_VIDEO_CONFIG_PARAMS, ¶ms ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_SET_VIDEO_CONFIG_PARAMS (fb%d - %s) failed!\n", VID1, params.cb_cr_order ? "CrCb" : "CbCr" ); } /* Update scaling parameters? */ if ((updated & (CLRCF_SOURCE | CLRCF_DEST)) && (config->source.w != old->source.w || config->source.h != old->source.h || config->dest.w != old->dest.w || config->dest.h != old->dest.h) && (config->dest.w != config->source.w || config->dest.h != config->source.h)) { D_DEBUG_AT( Davinci_Video, " => scaling %4dx%4d -> %4dx%4d\n", config->source.w, config->source.h, config->dest.w, config->dest.h ); SetupResizerParams( &dvid->resizer, config->source.w, config->source.h, config->dest.w, config->dest.h, &dvid->resized.w, &dvid->resized.h ); dvid->offset.x = (config->dest.w - dvid->resized.w) / 2; dvid->offset.y = (config->dest.h - dvid->resized.h) / 2; D_DEBUG_AT( Davinci_Video, " => resized %4dx%4d, centered %d,%d\n", dvid->resized.w, dvid->resized.h, dvid->offset.x, dvid->offset.y ); dvid->offset.x += dvid->offset.x & 1; /* Round up to multiple of two */ D_DEBUG_AT( Davinci_Video, " => offset %4d,%4d\n", dvid->offset.x, dvid->offset.y ); davincifb_pan_display( &ddrv->fb[VID1], &dvid->var, NULL, DSFLIP_NONE, 0, 0 ); } dvid->enable = true; dvid->config = *config; return DFB_OK; } static DFBResult videoRemoveRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data ) { int ret; DavinciDriverData *ddrv = driver_data; DavinciVideoLayerData *dvid = layer_data; D_DEBUG_AT( Davinci_Video, "%s()\n", __FUNCTION__ ); D_ASSERT( ddrv != NULL ); ret = ioctl( ddrv->fb[VID1].fd, FBIO_ENABLE_DISABLE_WIN, 0 ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_ENABLE_DISABLE_WIN (fb%d - %d)!\n", VID1, 0 ); dvid->enabled = false; dvid->enable = false; return DFB_OK; } static DFBResult videoFlipRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, DFBSurfaceFlipFlags flags, CoreSurfaceBufferLock *lock ) { DFBResult ret; DavinciDriverData *ddrv = driver_data; DavinciVideoLayerData *dvid = layer_data; D_ASSERT( surface != NULL ); D_ASSERT( lock != NULL ); D_ASSERT( ddrv != NULL ); D_ASSERT( dvid != NULL ); D_DEBUG_AT( Davinci_Video, "%s( 0x%08lx [%d] 0x%04x [%4dx%4d] )\n", __FUNCTION__, lock->phys, lock->pitch, flags, dvid->config.width, dvid->config.height ); ret = ShowBuffer( ddrv, dvid, lock, NULL, flags ); if (ret) return ret; dfb_surface_flip( surface, false ); return DFB_OK; } static DFBResult videoUpdateRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, const DFBRegion *update, CoreSurfaceBufferLock *lock ) { DavinciDriverData *ddrv = driver_data; DavinciVideoLayerData *dvid = layer_data; D_ASSERT( surface != NULL ); D_ASSERT( lock != NULL ); D_ASSERT( ddrv != NULL ); D_ASSERT( dvid != NULL ); if (update) { DFBRectangle area = DFB_RECTANGLE_INIT_FROM_REGION( update ); D_DEBUG_AT( Davinci_Video, "%s( 0x%08lx [%d], %4d,%4d-%4dx%4d )\n", __FUNCTION__, lock->phys, lock->pitch, DFB_RECTANGLE_VALS( &area ) ); if (!dfb_rectangle_intersect( &area, &dvid->config.source )) { D_DEBUG_AT( Davinci_Video, " -> NO INTERSECTION with %4d,%4d-%4dx%4d\n", DFB_RECTANGLE_VALS( &dvid->config.source ) ); return DFB_OK; } if (!DFB_RECTANGLE_EQUAL( area, dvid->config.source )) return ShowBuffer( ddrv, dvid, lock, &area, DSFLIP_NONE ); } else D_DEBUG_AT( Davinci_Video, "%s( 0x%08lx [%d], %4dx%4d )\n", __FUNCTION__, lock->phys, lock->pitch, dvid->config.width, dvid->config.height ); return ShowBuffer( ddrv, dvid, lock, NULL, DSFLIP_NONE ); } const DisplayLayerFuncs davinciVideoLayerFuncs = { .LayerDataSize = videoLayerDataSize, .InitLayer = videoInitLayer, .TestRegion = videoTestRegion, .SetRegion = videoSetRegion, .RemoveRegion = videoRemoveRegion, .FlipRegion = videoFlipRegion, .UpdateRegion = videoUpdateRegion, }; /*********************************************************************************************************************** ** Frame Output */ static void enable_video( DavinciDriverData *ddrv, DavinciVideoLayerData *dvid ) { if (dvid->enable && !dvid->enabled) { ioctl( ddrv->fb[VID1].fd, FBIO_WAITFORVSYNC ); if (ioctl( ddrv->fb[VID1].fd, FBIO_ENABLE_DISABLE_WIN, 1 )) D_VIDERROR( "Davinci/Video: FBIO_ENABLE_DISABLE_WIN (fb%d - %d)!\n", VID1, 1 ); dvid->enabled = true; } } static DFBResult ShowBuffer( DavinciDriverData *ddrv, DavinciVideoLayerData *dvid, CoreSurfaceBufferLock *lock, const DFBRectangle *area, DFBSurfaceFlipFlags flags ) { const CoreLayerRegionConfig *config = &dvid->config; if (area) D_DEBUG_AT( Davinci_Video, "%s( 0x%08lx [%d], %4d,%4d-%4dx%4d )\n", __FUNCTION__, lock->phys, lock->pitch, DFB_RECTANGLE_VALS( area ) ); else D_DEBUG_AT( Davinci_Video, "%s( 0x%08lx [%d] )\n", __FUNCTION__, lock->phys, lock->pitch ); if (config->dest.w == config->source.w && config->dest.h == config->source.h) { /* * Unscaled video, buffer displayed directly */ D_DEBUG_AT( Davinci_Video, " -> unscaled %4dx%4d <- %4d,%4d [%4dx%4d]\n", config->source.w, config->source.h, config->source.x, config->source.y, config->width, config->height ); /* Partial update, assuming proper buffer is shown, saving system calls */ if (area && dvid->enabled) return DFB_OK; davincifb_pan_display( &ddrv->fb[VID1], &dvid->var, lock, flags, config->source.x, config->source.y ); } else { int ret; DavinciDeviceData *ddev = ddrv->ddev; CoreSurfaceBuffer *buffer = lock->buffer; vpfe_resizer_params_t *params = &dvid->resizer; /* * Scaled video, buffer scaled to output buffer by resizer */ D_DEBUG_AT( Davinci_Video, " -> scaled %4dx%4d -> %4dx%4d <- %4d,%4d [%4dx%4d]\n", config->source.w, config->source.h, config->dest.w, config->dest.h, config->source.x, config->source.y, config->width, config->height ); /* FIXME: Implement scaled partial updates! */ if (area) D_UNIMPLEMENTED(); params->sdr_inoff = lock->pitch; params->sdr_inadd = lock->phys + DFB_BYTES_PER_LINE( buffer->format, config->source.x ) + config->source.y * params->sdr_inoff; params->sdr_outoff = ddev->fix[VID1].line_length; params->sdr_outadd = ddev->fix[VID1].smem_start + dvid->offset.x * 2 + dvid->offset.y * params->sdr_outoff; params->in_start = (params->sdr_outadd & 0x1f) / 2; params->sdr_outadd &= ~0x1f; D_DEBUG_AT( Davinci_Video, " -> FBIO_RESIZER running...\n" ); ret = ioctl( ddrv->fb[VID1].fd, FBIO_RESIZER, params ); if (ret) D_VIDERROR( "Davinci/Video: FBIO_RESIZER (fb%d)!\n", VID1 ); D_DEBUG_AT( Davinci_Video, " => FBIO_RESIZER returned %d\n", ret ); } enable_video( ddrv, dvid ); return DFB_OK; } /*********************************************************************************************************************** ** Scaling Setup */ static int limitInput(int rsz,int inSize,int outSize,int* pInSize) { int phases; int phaseShift; int taps; int phaseMask; int coarseShift; int halfCoarse; int tmp; do { if (rsz<=512) { //1/2x to 4x resize uses 8 phase, 4 taps phaseShift = 3; taps = 4; } else { //4-phase, 7 taps phaseShift = 2; taps = 7; } phases = 1<>8) + taps; if (tmp <= inSize) break; rsz--; } while (1); *pInSize = tmp; return rsz; } static void SetupCoef(unsigned int* pCoef,int rsz) { int startCoef; int highCoef; int c; int phases; int taps; if (rsz<=512) { //1/2x to 4x resize uses 8 phase, 4 taps highCoef = 0x100; c=1; phases=8; taps=4; } else { //4-phase, 7 taps if (rsz<=(256*3)) { highCoef = 0x100/2; c=2; } else { highCoef = 0x100/4; c=1; } phases=4; taps=7; } startCoef = highCoef>>1; while (phases) { int prev = startCoef; int tapNum=0; int rem=256 - startCoef; while ( tapNum < (c-1)) { *pCoef++ = 0; tapNum+=2; } if (c&1) { *pCoef++ = prev<<16; tapNum+=2; } else { tapNum++; } while ( tapNum < taps) { int min = (rem (highCoef>>3)) startCoef -= (highCoef>>3); else { startCoef = highCoef; c++; } phases--; } } #define SDRAM_SRC (1<<28) #define BILINEAR (1<<29) static void SetupResizerParams( vpfe_resizer_params_t *params, int srcWidth, int srcHeight, int outWidth, int outHeight, int *ret_outWidth, int *ret_outHeight ) { int rsz; int hrsz; int vrsz; int tmp; D_DEBUG_AT( Davinci_Video, "%s( %4dx%4d->%4dx%4d )\n", __FUNCTION__, srcWidth, srcHeight, outWidth, outHeight ); params->sdr_inadd = 0; params->sdr_inoff = 0; params->sdr_outadd = 0; params->sdr_outoff = 0; params->in_start = (0<<16)|(0); params->yenh = 0; params->rsz_cnt = SDRAM_SRC; //find scale factor rsz = (srcWidth<<8)/outWidth; if (rsz<64) { //too much upscaling, reduce destination size rsz = 64; } else if (rsz>1024) { //too much down scaling, reduce source size rsz=1024; srcWidth = (outWidth * rsz)>>8; } tmp = ((srcWidth<<8)+255)/rsz; if (tmp > outWidth) tmp = outWidth; tmp &= ~1; //force even if (rsz>256) { //upsize in vertical direction requires a multiple of 16 bytes (8 pixels) tmp &= ~0x7; } do { int t; hrsz = limitInput(rsz,srcWidth,tmp,&t); if (hrsz>=64) { srcWidth = t; break; } tmp-=2; } while (1); outWidth = tmp; if (srcWidth==outWidth) { int i=0; params->rsz_cnt |= ((256-1)<<0); //1 to 1 params->in_size = (srcWidth+3); //4 taps while (i<16) { params->hfilt[i] = i? 0 : 0x100; //2 coefficient written at a time i++; } } else { SetupCoef(¶ms->hfilt[0],hrsz); params->rsz_cnt |= ((hrsz-1)<<0) | ((hrsz<256)? BILINEAR : 0); params->in_size = (srcWidth); } //find scale factor rsz = (srcHeight<<8)/outHeight; if (rsz<64) { //too much upscaling, reduce destination size rsz = 64; } else if (rsz>1024) { //too much down scaling, reduce source size rsz=1024; srcHeight = (outHeight * rsz)>>8; } tmp = ((srcHeight<<8)+255)/rsz; if (tmp > outHeight) tmp = outHeight; do { int t; vrsz = limitInput(rsz,srcHeight,tmp,&t); if (vrsz>=64) { srcHeight = t; break; } tmp--; } while (1); outHeight = tmp; if (srcHeight==outHeight) { int i=0; params->rsz_cnt |= ((256-1)<<10); //1 to 1 params->in_size |= ((srcHeight+3)<<16); //4 taps while (i<16) { params->vfilt[i] = i? 0 : 0x100; //2 coefficient written at a time i++; } } else { SetupCoef(¶ms->vfilt[0],vrsz); params->rsz_cnt |= ((vrsz-1)<<10); params->in_size |= (srcHeight<<16); } params->out_size = (outHeight<<16)|(outWidth); D_DEBUG_AT( Davinci_Video, " => %4dx%4d->%4dx%4d\n", srcWidth, srcHeight, outWidth, outHeight ); if (ret_outWidth) *ret_outWidth = outWidth; if (ret_outHeight) *ret_outHeight = outHeight; }