/* (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 Denis Oliver Kropp , Andreas Hundt , Sven Neumann , Ville Syrjälä 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 "regs.h" #include "mmio.h" #include "matrox.h" #include "matrox_maven.h" typedef struct { CoreLayerRegionConfig config; DFBColorAdjustment adj; int field; /* Stored registers */ struct { /* CRTC2 */ u32 c2CTL; u32 c2DATACTL; u32 c2MISC; u32 c2OFFSET; u32 c2HPARAM; u32 c2VPARAM; u32 c2STARTADD0; u32 c2STARTADD1; u32 c2PL2STARTADD0; u32 c2PL2STARTADD1; u32 c2PL3STARTADD0; u32 c2PL3STARTADD1; } regs; MatroxMavenData mav; } MatroxCrtc2LayerData; static void crtc2_set_regs ( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ); static void crtc2_calc_regs ( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2, CoreLayerRegionConfig *config, CoreSurface *surface, CoreSurfaceBufferLock *lock ); static void crtc2_calc_buffer ( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2, CoreSurface *surface, CoreSurfaceBufferLock *lock ); static void crtc2_set_buffer ( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ); static DFBResult crtc2_disable_output( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ); static DFBResult crtc2_enable_output ( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ); #define CRTC2_SUPPORTED_OPTIONS (DLOP_FIELD_PARITY) /**********************/ static int crtc2LayerDataSize( void ) { return sizeof(MatroxCrtc2LayerData); } static const DFBColorAdjustment adjustments[2][2] = { /* G400 */ { /* PAL / PAL-60 */ { .flags = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_HUE | DCAF_SATURATION, .brightness = 0xA800, .saturation = 0x9500, .contrast = 0xFF00, .hue = 0x0000, }, /* NTSC */ { .flags = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_HUE | DCAF_SATURATION, .brightness = 0xB500, .saturation = 0x8E00, .contrast = 0xEA00, .hue = 0x0000, } }, /* G450 / G550 */ { /* PAL / PAL-60 */ { .flags = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_HUE | DCAF_SATURATION, .brightness = 0x9E00, .saturation = 0xBB00, .contrast = 0xFF00, .hue = 0x0000, }, /* NTSC */ { .flags = DCAF_BRIGHTNESS | DCAF_CONTRAST | DCAF_HUE | DCAF_SATURATION, .brightness = 0xAA00, .saturation = 0xAE00, .contrast = 0xEA00, .hue = 0x0000, } } }; static DFBResult crtc2InitLayer( CoreLayer *layer, void *driver_data, void *layer_data, DFBDisplayLayerDescription *description, DFBDisplayLayerConfig *config, DFBColorAdjustment *adjustment ) { MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; MatroxCrtc2LayerData *mcrtc2 = (MatroxCrtc2LayerData*) layer_data; MatroxDeviceData *mdev = mdrv->device_data; MatroxMavenData *mav = &mcrtc2->mav; DFBResult res; if ((res = maven_init( mav, mdrv )) != DFB_OK) return res; /* set capabilities and type */ description->caps = DLCAPS_SURFACE | DLCAPS_FIELD_PARITY | DLCAPS_BRIGHTNESS | DLCAPS_CONTRAST | DLCAPS_HUE | DLCAPS_SATURATION | DLCAPS_ALPHA_RAMP; description->type = DLTF_GRAPHICS | DLTF_VIDEO | DLTF_STILL_PICTURE; /* set name */ snprintf( description->name, DFB_DISPLAY_LAYER_DESC_NAME_LENGTH, "Matrox CRTC2 Layer" ); /* fill out the default configuration */ config->flags = DLCONF_WIDTH | DLCONF_HEIGHT | DLCONF_PIXELFORMAT | DLCONF_BUFFERMODE | DLCONF_OPTIONS | DLCONF_SURFACE_CAPS; config->width = 720; config->height = (dfb_config->matrox_tv_std != DSETV_PAL) ? 480 : 576; config->pixelformat = DSPF_YUY2; config->buffermode = DLBM_FRONTONLY; config->options = DLOP_NONE; config->surface_caps = DSCAPS_INTERLACED; /* fill out default color adjustment, only fields set in flags will be accepted from applications */ *adjustment = adjustments[mdev->g450_matrox][dfb_config->matrox_tv_std == DSETV_NTSC]; /* remember color adjustment */ mcrtc2->adj = *adjustment; return DFB_OK; } static DFBResult crtc2TestRegion( CoreLayer *layer, void *driver_data, void *layer_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags *failed ) { CoreLayerRegionConfigFlags fail = 0; if (config->options & ~CRTC2_SUPPORTED_OPTIONS) fail |= CLRCF_OPTIONS; if (config->surface_caps & ~(DSCAPS_INTERLACED | DSCAPS_SEPARATED)) fail |= CLRCF_SURFACE_CAPS; switch (config->format) { case DSPF_ARGB: case DSPF_RGB32: case DSPF_RGB555: case DSPF_ARGB1555: case DSPF_RGB16: case DSPF_YUY2: case DSPF_UYVY: case DSPF_I420: case DSPF_YV12: break; default: fail |= CLRCF_FORMAT; } if (config->width != 720) fail |= CLRCF_WIDTH; if (config->height != ((dfb_config->matrox_tv_std != DSETV_PAL) ? 480 : 576)) fail |= CLRCF_HEIGHT; if (failed) *failed = fail; if (fail) return DFB_UNSUPPORTED; return DFB_OK; } static DFBResult crtc2AddRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreLayerRegionConfig *config ) { return DFB_OK; } static DFBResult crtc2SetRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreLayerRegionConfig *config, CoreLayerRegionConfigFlags updated, CoreSurface *surface, CorePalette *palette, CoreSurfaceBufferLock *lock ) { DFBResult ret; MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; MatroxCrtc2LayerData *mcrtc2 = (MatroxCrtc2LayerData*) layer_data; MatroxDeviceData *mdev = mdrv->device_data; /* remember configuration */ mcrtc2->config = *config; if (updated & CLRCF_PARITY) mcrtc2->field = !config->parity; if (updated & (CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_FORMAT | CLRCF_SURFACE_CAPS | CLRCF_ALPHA_RAMP | CLRCF_SURFACE)) { crtc2_calc_regs( mdrv, mcrtc2, config, surface, lock ); crtc2_calc_buffer( mdrv, mcrtc2, surface, lock ); ret = crtc2_enable_output( mdrv, mcrtc2 ); if (ret) return ret; mdev->crtc2_separated = !!(surface->config.caps & DSCAPS_SEPARATED); } return DFB_OK; } static DFBResult crtc2RemoveRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data ) { MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; MatroxCrtc2LayerData *mcrtc2 = (MatroxCrtc2LayerData*) layer_data; crtc2_disable_output( mdrv, mcrtc2 ); return DFB_OK; } static DFBResult crtc2FlipRegion( CoreLayer *layer, void *driver_data, void *layer_data, void *region_data, CoreSurface *surface, DFBSurfaceFlipFlags flags, CoreSurfaceBufferLock *lock ) { MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; MatroxCrtc2LayerData *mcrtc2 = (MatroxCrtc2LayerData*) layer_data; volatile u8 *mmio = mdrv->mmio_base; crtc2_calc_buffer( mdrv, mcrtc2, surface, lock ); if (mcrtc2->config.options & DLOP_FIELD_PARITY) { int field = (mga_in32( mmio, C2VCOUNT ) & C2FIELD) ? 1 : 0; while (field == mcrtc2->field) { dfb_screen_wait_vsync( mdrv->secondary ); field = (mga_in32( mmio, C2VCOUNT ) & C2FIELD) ? 1 : 0; } } crtc2_set_buffer( mdrv, mcrtc2 ); dfb_surface_flip( surface, false ); if (flags & DSFLIP_WAIT) dfb_screen_wait_vsync( mdrv->secondary ); return DFB_OK; } static DFBResult crtc2SetColorAdjustment( CoreLayer *layer, void *driver_data, void *layer_data, DFBColorAdjustment *adj ) { MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; MatroxCrtc2LayerData *mcrtc2 = (MatroxCrtc2LayerData*) layer_data; MatroxMavenData *mav = &mcrtc2->mav; DFBResult res; if ((res = maven_open( mav, mdrv )) != DFB_OK) return res; if (adj->flags & DCAF_HUE) maven_set_hue( mav, mdrv, adj->hue >> 8 ); if (adj->flags & DCAF_SATURATION) maven_set_saturation( mav, mdrv, adj->saturation >> 8 ); if (adj->flags & DCAF_BRIGHTNESS || adj->flags & DCAF_CONTRAST) maven_set_bwlevel( mav, mdrv, adj->brightness >> 8, adj->contrast >> 8 ); maven_close( mav, mdrv ); /* remember color adjustment */ mcrtc2->adj = *adj; return DFB_OK; } static DFBResult crtc2GetCurrentOutputField( CoreLayer *layer, void *driver_data, void *layer_data, int *field ) { MatroxDriverData *mdrv = (MatroxDriverData*) driver_data; if (!field) return DFB_INVARG; *field = (mga_in32( mdrv->mmio_base, C2VCOUNT ) & C2FIELD) ? 1 : 0; return DFB_OK; } DisplayLayerFuncs matroxCrtc2Funcs = { .LayerDataSize = crtc2LayerDataSize, .InitLayer = crtc2InitLayer, .TestRegion = crtc2TestRegion, .AddRegion = crtc2AddRegion, .SetRegion = crtc2SetRegion, .RemoveRegion = crtc2RemoveRegion, .FlipRegion = crtc2FlipRegion, .SetColorAdjustment = crtc2SetColorAdjustment, .GetCurrentOutputField = crtc2GetCurrentOutputField, }; /* internal */ static void crtc2_set_regs( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ) { volatile u8 *mmio = mdrv->mmio_base; mga_out32( mmio, mcrtc2->regs.c2CTL, C2CTL ); mga_out32( mmio, mcrtc2->regs.c2DATACTL, C2DATACTL ); mga_out32( mmio, mcrtc2->regs.c2HPARAM, C2HPARAM ); mga_out32( mmio, 0, C2HSYNC ); mga_out32( mmio, mcrtc2->regs.c2VPARAM, C2VPARAM ); mga_out32( mmio, 0, C2VSYNC ); mga_out32( mmio, mcrtc2->regs.c2OFFSET, C2OFFSET ); mga_out32( mmio, mcrtc2->regs.c2MISC, C2MISC ); mga_out32( mmio, 0, C2PRELOAD ); } static void crtc2_calc_regs( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2, CoreLayerRegionConfig *config, CoreSurface *surface, CoreSurfaceBufferLock *lock ) { MatroxDeviceData *mdev = mdrv->device_data; mcrtc2->regs.c2CTL = 0; /* Don't touch sub-picture bits. */ mcrtc2->regs.c2DATACTL = mga_in32( mdrv->mmio_base, C2DATACTL ); mcrtc2->regs.c2DATACTL &= C2STATICKEY | C2OFFSETDIVEN | C2STATICKEYEN | C2SUBPICEN; if (mdev->g450_matrox) mcrtc2->regs.c2CTL |= C2PIXCLKSEL_CRISTAL; else mcrtc2->regs.c2CTL |= C2PIXCLKSEL_VDOCLK; /* * High priority request level. * According to G400 specs these values should * be fixed when CRTC2 is in YUV mode. */ /* c2hiprilvl */ mcrtc2->regs.c2CTL |= 2 << 4; /* c2maxhipri */ mcrtc2->regs.c2CTL |= 1 << 8; switch (surface->config.format) { case DSPF_RGB555: case DSPF_ARGB1555: case DSPF_RGB16: case DSPF_RGB32: case DSPF_ARGB: mcrtc2->regs.c2DATACTL |= C2DITHEN | C2YFILTEN | C2CBCRFILTEN; break; default: break; } if (dfb_config->matrox_tv_std != DSETV_PAL) mcrtc2->regs.c2DATACTL |= C2NTSCEN; /* pixel format settings */ switch (surface->config.format) { case DSPF_I420: case DSPF_YV12: mcrtc2->regs.c2CTL |= C2DEPTH_YCBCR420; break; case DSPF_UYVY: mcrtc2->regs.c2DATACTL |= C2UYVYFMT; /* fall through */ case DSPF_YUY2: mcrtc2->regs.c2CTL |= C2DEPTH_YCBCR422; break; case DSPF_RGB555: case DSPF_ARGB1555: mcrtc2->regs.c2CTL |= C2DEPTH_15BPP; break; case DSPF_RGB16: mcrtc2->regs.c2CTL |= C2DEPTH_16BPP; break; case DSPF_RGB32: case DSPF_ARGB: mcrtc2->regs.c2CTL |= C2DEPTH_32BPP; break; default: D_BUG( "unexpected pixelformat" ); return; } if (!(surface->config.caps & DSCAPS_INTERLACED)) mcrtc2->regs.c2CTL |= C2VCBCRSINGLE; mcrtc2->regs.c2OFFSET = lock->pitch; if (!(surface->config.caps & DSCAPS_SEPARATED)) mcrtc2->regs.c2OFFSET *= 2; { int hdisplay, htotal, vdisplay, vtotal; if (dfb_config->matrox_tv_std != DSETV_PAL) { hdisplay = 720; htotal = 858; vdisplay = 480 / 2; vtotal = 525 / 2; } else { hdisplay = 720; htotal = 864; vdisplay = 576 / 2; vtotal = 625 / 2; } mcrtc2->regs.c2HPARAM = ((hdisplay - 8) << 16) | (htotal - 8); mcrtc2->regs.c2VPARAM = ((vdisplay - 1) << 16) | (vtotal - 1); mcrtc2->regs.c2MISC = 0; /* c2vlinecomp */ mcrtc2->regs.c2MISC |= (vdisplay + 1) << 16; } /* c2bpp15halpha */ mcrtc2->regs.c2DATACTL |= config->alpha_ramp[3] << 8; /* c2bpp15lalpha */ mcrtc2->regs.c2DATACTL |= config->alpha_ramp[0] << 16; } static void crtc2_calc_buffer( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2, CoreSurface *surface, CoreSurfaceBufferLock *lock ) { unsigned int field_offset = lock->pitch; if (surface->config.caps & DSCAPS_SEPARATED) field_offset *= surface->config.size.h / 2; mcrtc2->regs.c2STARTADD1 = lock->offset; mcrtc2->regs.c2STARTADD0 = mcrtc2->regs.c2STARTADD1 + field_offset; if (surface->config.caps & DSCAPS_INTERLACED) field_offset = lock->pitch / 2; else field_offset = 0; if (surface->config.caps & DSCAPS_SEPARATED) field_offset *= surface->config.size.h / 4; switch (surface->config.format) { case DSPF_I420: mcrtc2->regs.c2PL2STARTADD1 = mcrtc2->regs.c2STARTADD1 + surface->config.size.h * lock->pitch; mcrtc2->regs.c2PL2STARTADD0 = mcrtc2->regs.c2PL2STARTADD1 + field_offset; mcrtc2->regs.c2PL3STARTADD1 = mcrtc2->regs.c2PL2STARTADD1 + surface->config.size.h/2 * lock->pitch/2; mcrtc2->regs.c2PL3STARTADD0 = mcrtc2->regs.c2PL3STARTADD1 + field_offset; break; case DSPF_YV12: mcrtc2->regs.c2PL3STARTADD1 = mcrtc2->regs.c2STARTADD1 + surface->config.size.h * lock->pitch; mcrtc2->regs.c2PL3STARTADD0 = mcrtc2->regs.c2PL3STARTADD1 + field_offset; mcrtc2->regs.c2PL2STARTADD1 = mcrtc2->regs.c2PL3STARTADD1 + surface->config.size.h/2 * lock->pitch/2; mcrtc2->regs.c2PL2STARTADD0 = mcrtc2->regs.c2PL2STARTADD1 + field_offset; break; default: break; } } static void crtc2_set_buffer( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ) { volatile u8 *mmio = mdrv->mmio_base; mga_out32( mmio, mcrtc2->regs.c2STARTADD0, C2STARTADD0 ); mga_out32( mmio, mcrtc2->regs.c2STARTADD1, C2STARTADD1 ); mga_out32( mmio, mcrtc2->regs.c2PL2STARTADD0, C2PL2STARTADD0 ); mga_out32( mmio, mcrtc2->regs.c2PL2STARTADD1, C2PL2STARTADD1 ); mga_out32( mmio, mcrtc2->regs.c2PL3STARTADD0, C2PL3STARTADD0 ); mga_out32( mmio, mcrtc2->regs.c2PL3STARTADD1, C2PL3STARTADD1 ); } static void crtc2OnOff( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2, int on ) { volatile u8 *mmio = mdrv->mmio_base; if (on) mcrtc2->regs.c2CTL |= C2EN; else mcrtc2->regs.c2CTL &= ~C2EN; mga_out32( mmio, mcrtc2->regs.c2CTL, C2CTL ); if (on) mcrtc2->regs.c2CTL &= ~C2PIXCLKDIS; else mcrtc2->regs.c2CTL |= C2PIXCLKDIS; mga_out32( mmio, mcrtc2->regs.c2CTL, C2CTL ); if (!on) { mcrtc2->regs.c2CTL &= ~C2INTERLACE; mga_out32( mmio, mcrtc2->regs.c2CTL, C2CTL ); } } static void crtc2_set_mafc( MatroxDriverData *mdrv, int on ) { volatile u8 *mmio = mdrv->mmio_base; u8 val; val = mga_in_dac( mmio, XMISCCTRL ); if (on) { val &= ~(MFCSEL_MASK | VDOUTSEL_MASK); val |= MFCSEL_MAFC | VDOUTSEL_CRTC2656; } else { val &= ~MFCSEL_MASK; val |= MFCSEL_DIS; } mga_out_dac( mmio, XMISCCTRL, val ); } static DFBResult crtc2_disable_output( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ) { MatroxDeviceData *mdev = mdrv->device_data; MatroxMavenData *mav = &mcrtc2->mav; DFBResult res; if ((res = maven_open( mav, mdrv )) != DFB_OK) return res; maven_disable( mav, mdrv ); if (!mdev->g450_matrox) crtc2_set_mafc( mdrv, 0 ); crtc2OnOff( mdrv, mcrtc2, 0 ); maven_close( mav, mdrv ); if (mdev->g450_matrox) { volatile u8 *mmio = mdrv->mmio_base; u8 val; /* Set Rset to 0.7 V */ val = mga_in_dac( mmio, XGENIOCTRL ); val &= ~0x40; mga_out_dac( mmio, XGENIOCTRL, val ); val = mga_in_dac( mmio, XGENIODATA ); val &= ~0x40; mga_out_dac( mmio, XGENIODATA, val ); val = mga_in_dac( mmio, XPWRCTRL ); val &= ~(DAC2PDN | CFIFOPDN); mga_out_dac( mmio, XPWRCTRL, val ); val = mga_in_dac( mmio, XDISPCTRL ); val &= ~DAC2OUTSEL_MASK; val |= DAC2OUTSEL_DIS; mga_out_dac( mmio, XDISPCTRL, val ); } return DFB_OK; } static DFBResult crtc2_enable_output( MatroxDriverData *mdrv, MatroxCrtc2LayerData *mcrtc2 ) { MatroxDeviceData *mdev = mdrv->device_data; MatroxMavenData *mav = &mcrtc2->mav; volatile u8 *mmio = mdrv->mmio_base; DFBResult res; if ((res = maven_open( mav, mdrv )) != DFB_OK) return res; if (mdev->g450_matrox) { volatile u8 *mmio = mdrv->mmio_base; u8 val; /* Set Rset to 1.0 V */ val = mga_in_dac( mmio, XGENIOCTRL ); val |= 0x40; mga_out_dac( mmio, XGENIOCTRL, val ); val = mga_in_dac( mmio, XGENIODATA ); val &= ~0x40; mga_out_dac( mmio, XGENIODATA, val ); val = mga_in_dac( mmio, XPWRCTRL ); val |= DAC2PDN | CFIFOPDN; mga_out_dac( mmio, XPWRCTRL, val ); val = mga_in_dac( mmio, XDISPCTRL ); val &= ~DAC2OUTSEL_MASK; val |= DAC2OUTSEL_TVE; mga_out_dac( mmio, XDISPCTRL, val ); if (dfb_config->matrox_cable == 1) { val = mga_in_dac( mmio, XSYNCCTRL ); val &= ~(DAC2HSOFF | DAC2VSOFF | DAC2HSPOL | DAC2VSPOL); mga_out_dac( mmio, XSYNCCTRL, val ); } } maven_disable( mav, mdrv ); if (!mdev->g450_matrox) crtc2_set_mafc( mdrv, 0 ); crtc2OnOff( mdrv, mcrtc2, 0 ); crtc2_set_regs( mdrv, mcrtc2 ); crtc2_set_buffer( mdrv, mcrtc2 ); if (!mdev->g450_matrox) crtc2_set_mafc( mdrv, 1 ); crtc2OnOff( mdrv, mcrtc2, 1 ); maven_set_regs( mav, mdrv, &mcrtc2->config, &mcrtc2->adj ); mcrtc2->regs.c2CTL |= C2INTERLACE; if (mdev->g450_matrox) mcrtc2->regs.c2CTL |= 0x1000; /* Undocumented bit */ while ((mga_in32( mmio, C2VCOUNT ) & 0x00000FFF) != 1) ; while ((mga_in32( mmio, C2VCOUNT ) & 0x00000FFF) != 0) ; mga_out32( mmio, mcrtc2->regs.c2CTL, C2CTL ); maven_enable( mav, mdrv ); if (!mdev->g450_matrox) { while ((mga_in32( mmio, C2VCOUNT ) & 0x00000FFF) != 1) ; while ((mga_in32( mmio, C2VCOUNT ) & 0x00000FFF) != 0) ; maven_sync( mav, mdrv ); } maven_close( mav, mdrv ); return DFB_OK; }