/* Copyright (c) 2008 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * Neither the name of the Advanced Micro Devices, Inc. nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "xf86.h" #include "geode.h" #include "xf86Crtc.h" #include "cim/cim_defs.h" #include "cim/cim_regs.h" typedef struct _LXOutputPrivateRec { int video_enable; unsigned long video_flags; GeodeMemPtr rotate_mem; } LXCrtcPrivateRec, *LXCrtcPrivatePtr; void lx_enable_dac_power(ScrnInfoPtr pScrni, int option) { GeodeRec *pGeode = GEODEPTR(pScrni); df_set_crt_enable(DF_CRT_ENABLE); /* Turn off the DAC if we don't need the CRT */ if (option && (!(pGeode->Output & OUTPUT_CRT))) { unsigned int misc = READ_VID32(DF_VID_MISC); misc |= DF_DAC_POWER_DOWN; WRITE_VID32(DF_VID_MISC, misc); } if (pGeode->Output & OUTPUT_PANEL) df_set_panel_enable(1); } void lx_disable_dac_power(ScrnInfoPtr pScrni, int option) { GeodeRec *pGeode = GEODEPTR(pScrni); if (pGeode->Output & OUTPUT_PANEL) df_set_panel_enable(0); if (pGeode->Output & OUTPUT_CRT) { /* Wait for the panel to finish its procedure */ if (pGeode->Output & OUTPUT_PANEL) while ((READ_VID32(DF_POWER_MANAGEMENT) & 2) == 0); df_set_crt_enable(option); } } static void lx_set_panel_mode(VG_DISPLAY_MODE * mode, DisplayModePtr pMode) { int hsync, vsync; mode->mode_width = mode->panel_width = pMode->HDisplay; mode->mode_height = mode->panel_height = pMode->VDisplay; mode->hactive = pMode->HDisplay; mode->hblankstart = pMode->HDisplay; mode->hsyncstart = pMode->HSyncStart; mode->hsyncend = pMode->HSyncEnd; mode->hblankend = pMode->HTotal; mode->htotal = pMode->HTotal; mode->vactive = pMode->VDisplay; mode->vblankstart = pMode->VDisplay; mode->vsyncstart = pMode->VSyncStart; mode->vsyncend = pMode->VSyncEnd; mode->vblankend = pMode->VTotal; mode->vtotal = pMode->VTotal; mode->vactive_even = pMode->VDisplay; mode->vblankstart_even = pMode->VDisplay; mode->vsyncstart_even = pMode->VSyncStart; mode->vsyncend_even = pMode->VSyncEnd; mode->vblankend_even = pMode->VTotal; mode->vtotal_even = pMode->VTotal; mode->frequency = (int) ((pMode->Clock / 1000.0) * 0x10000); /* In panel mode, Cimarron purposely swizzles these, * so we swizzle them first */ hsync = (pMode->Flags & V_NHSYNC) ? 0 : 1; vsync = (pMode->Flags & V_NVSYNC) ? 0 : 1; mode->flags |= (hsync) ? VG_MODEFLAG_NEG_HSYNC : 0; mode->flags |= (vsync) ? VG_MODEFLAG_NEG_VSYNC : 0; } static void lx_set_crt_mode(VG_DISPLAY_MODE * mode, DisplayModePtr pMode) { int hsync, vsync; mode->mode_width = mode->panel_width = pMode->HDisplay; mode->mode_height = mode->panel_height = pMode->VDisplay; mode->hactive = pMode->CrtcHDisplay; mode->hblankstart = pMode->CrtcHBlankStart; mode->hsyncstart = pMode->CrtcHSyncStart; mode->hsyncend = pMode->CrtcHSyncEnd; mode->hblankend = pMode->CrtcHBlankEnd; mode->htotal = pMode->CrtcHTotal; mode->vactive = pMode->CrtcVDisplay; mode->vblankstart = pMode->CrtcVBlankStart; mode->vsyncstart = pMode->CrtcVSyncStart; mode->vsyncend = pMode->CrtcVSyncEnd; mode->vblankend = pMode->CrtcVBlankEnd; mode->vtotal = pMode->CrtcVTotal; mode->vactive_even = pMode->CrtcVDisplay; mode->vblankstart_even = pMode->CrtcVBlankStart; mode->vsyncstart_even = pMode->CrtcVSyncStart; mode->vsyncend_even = pMode->CrtcVSyncEnd; mode->vblankend_even = pMode->CrtcVBlankEnd; mode->vtotal_even = pMode->CrtcVTotal; mode->frequency = (int) ((pMode->Clock / 1000.0) * 0x10000); hsync = (pMode->Flags & V_NHSYNC) ? 1 : 0; vsync = (pMode->Flags & V_NVSYNC) ? 1 : 0; mode->flags |= (hsync) ? VG_MODEFLAG_NEG_HSYNC : 0; mode->flags |= (vsync) ? VG_MODEFLAG_NEG_VSYNC : 0; } static int lx_set_mode(ScrnInfoPtr pScrni, DisplayModePtr pMode, int bpp) { GeodeRec *pGeode = GEODEPTR(pScrni); VG_DISPLAY_MODE mode; int ret; memset(&mode, 0, sizeof(mode)); mode.flags |= pGeode->Output & OUTPUT_CRT ? VG_MODEFLAG_CRT_AND_FP : 0; if (pGeode->Output & OUTPUT_PANEL) { mode.flags |= VG_MODEFLAG_PANELOUT; if (pGeode->Output & OUTPUT_CRT) mode.flags |= VG_MODEFLAG_CRT_AND_FP; } if (pGeode->Output & OUTPUT_PANEL && pGeode->Scale) lx_set_panel_mode(&mode, pGeode->panelMode); else lx_set_crt_mode(&mode, pMode); mode.src_width = pMode->HDisplay; mode.src_height = pMode->VDisplay; /* Set the filter coefficients to the default values */ vg_set_scaler_filter_coefficients(NULL, NULL); ret = vg_set_custom_mode(&mode, bpp); return (ret == CIM_STATUS_OK) ? 0 : -1; } static void lx_crtc_dpms(xf86CrtcPtr crtc, int mode) { ScrnInfoPtr pScrni = crtc->scrn; GeodeRec *pGeode = GEODEPTR(pScrni); if (pGeode->Output & OUTPUT_DCON) DCONDPMSSet(pScrni, mode); switch (mode) { case DPMSModeOn: lx_enable_dac_power(pScrni, 1); break; case DPMSModeStandby: lx_disable_dac_power(pScrni, DF_CRT_STANDBY); break; case DPMSModeSuspend: lx_disable_dac_power(pScrni, DF_CRT_SUSPEND); break; case DPMSModeOff: lx_disable_dac_power(pScrni, DF_CRT_DISABLE); break; } } static Bool lx_crtc_lock(xf86CrtcPtr crtc) { /* Wait until the GPU is idle */ gp_wait_until_idle(); return TRUE; } static void lx_crtc_unlock(xf86CrtcPtr crtc) { /* Nothing to do here */ } static void lx_crtc_prepare(xf86CrtcPtr crtc) { LXCrtcPrivatePtr lx_crtc = crtc->driver_private; /* Disable the video */ df_get_video_enable(&lx_crtc->video_enable, &lx_crtc->video_flags); if (lx_crtc->video_enable) df_set_video_enable(0, 0); /* Turn off compression */ vg_set_compression_enable(0); /* Hide the cursor */ crtc->funcs->hide_cursor(crtc); /* Turn off the display */ crtc->funcs->dpms(crtc, DPMSModeOff); } static Bool lx_crtc_mode_fixup(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode) { return TRUE; } static void lx_crtc_mode_set(xf86CrtcPtr crtc, DisplayModePtr mode, DisplayModePtr adjusted_mode, int x, int y) { ScrnInfoPtr pScrni = crtc->scrn; GeodeRec *pGeode = GEODEPTR(pScrni); DF_VIDEO_SOURCE_PARAMS vs_odd, vs_even; unsigned int rpitch; df_get_video_source_configuration(&vs_odd, &vs_even); /* Note - the memory gets adjusted when virtualX/virtualY * gets changed - so we don't need to worry about it here */ if (lx_set_mode(pScrni, adjusted_mode, pScrni->bitsPerPixel)) ErrorF("ERROR! Unable to set the mode!\n"); /* The output gets turned in in the output code as * per convention */ /* For rotation, any write to the frame buffer region marks * the retire frame as dirty. */ if (crtc->rotatedData != NULL) { rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8); vg_set_display_pitch(rpitch); } else vg_set_display_pitch(pGeode->Pitch); gp_set_bpp(pScrni->bitsPerPixel); /* Set the acceleration offset if we are drawing to a shadow */ if (crtc->rotatedData != NULL) vg_set_display_offset((unsigned int) ((char *) crtc->rotatedData - (char *) pGeode->FBBase)); else vg_set_display_offset(0); /* FIXME: What's up with X and Y? Does that come into play * here? */ df_configure_video_source(&vs_odd, &vs_even); vg_wait_vertical_blank(); } static void lx_crtc_commit(xf86CrtcPtr crtc) { LXCrtcPrivatePtr lx_crtc = crtc->driver_private; ScrnInfoPtr pScrni = crtc->scrn; GeodeRec *pGeode = GEODEPTR(pScrni); /* Turn back on the sreen */ crtc->funcs->dpms(crtc, DPMSModeOn); /* Turn on compression */ if (pGeode->Compression) { vg_configure_compression(&(pGeode->CBData)); vg_set_compression_enable(1); } /* Load the cursor */ if (crtc->scrn->pScreen != NULL) { xf86_reload_cursors(crtc->scrn->pScreen); crtc->funcs->hide_cursor(crtc); crtc->cursor_shown = FALSE; } /* Renable the video */ if (lx_crtc->video_enable) df_set_video_enable(lx_crtc->video_enable, lx_crtc->video_flags); lx_crtc->video_enable = 0; lx_crtc->video_flags = 0; } static void lx_crtc_gamma_set(xf86CrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue, int size) { unsigned int dcfg; int i; assert(size == 256); /* We need the Gamma Correction for video - fading operation, * the values address should be plused for every cycle. * Special for Screensaver Operation. */ for (i = 0; i < 256; i++) { unsigned int val; (*red) &= 0xff00; (*green) &= 0xff00; (*blue) &= 0xff00; val = (*(red++) << 8) | *(green++) | (*(blue++) >> 8); df_set_video_palette_entry(i, val); } /* df_set_video_palette_entry automatically turns on * gamma for video - if this gets called, we assume that * RandR wants it set for graphics, so reverse cimarron */ dcfg = READ_VID32(DF_DISPLAY_CONFIG); dcfg &= ~DF_DCFG_GV_PAL_BYP; WRITE_VID32(DF_DISPLAY_CONFIG, dcfg); } /* The Xserver has a scratch pixmap allocation routine that will * try to use the existing scratch pixmap if possible. When the driver * or any other user stop using it, it need to clear out any pixmap * state (private data etc) otherwise the next user may get stale data. */ /* Use our own wrapper to allocate a pixmap for wrapping a buffer object * It removes using scratch pixmaps for rotate. */ static PixmapPtr lx_create_bo_pixmap(ScreenPtr pScreen, int width, int height, int depth, int bpp, int pitch, pointer pPixData) { PixmapPtr pixmap; pixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0); if (!pixmap) return NULL; if (!(*pScreen->ModifyPixmapHeader) (pixmap, width, height, depth, bpp, pitch, pPixData)) { /* ModifyPixmapHeader failed, so we can't use it as scratch pixmap */ (*pScreen->DestroyPixmap) (pixmap); return NULL; } return pixmap; } static void lx_destory_bo_pixmap(PixmapPtr pixmap) { ScreenPtr pScreen = pixmap->drawable.pScreen; (*pScreen->DestroyPixmap) (pixmap); } /* Allocates shadow memory, and allocating a new space for Rotation. * The size is measured in bytes, and the offset from the beginning * of card space is returned. */ static Bool LXAllocShadow(ScrnInfoPtr pScrni, int size) { GeodeRec *pGeode = GEODEPTR(pScrni); if (pGeode->shadowArea) { if (pGeode->shadowArea->size != size) { exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea); pGeode->shadowArea = NULL; } } if (pGeode->shadowArea == NULL) { pGeode->shadowArea = exaOffscreenAlloc(pScrni->pScreen, size, 4, TRUE, NULL, NULL); if (pGeode->shadowArea == NULL) return FALSE; } pScrni->fbOffset = pGeode->shadowArea->offset; return TRUE; } static void * lx_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height) { ScrnInfoPtr pScrni = crtc->scrn; GeodePtr pGeode = GEODEPTR(pScrni); unsigned int rpitch, size; rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8); size = rpitch * height; /* Allocate shadow memory */ if (LXAllocShadow(pScrni, size) == FALSE) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Couldn't allocate the shadow memory for rotation\n"); xf86DrvMsg(pScrni->scrnIndex, X_ERROR, " You need 0x%x bytes, but only 0x%x bytes are available\n", size, GeodeOffscreenFreeSize(pGeode)); return NULL; } memset(pGeode->FBBase + pGeode->shadowArea->offset, 0, size); return pGeode->FBBase + pGeode->shadowArea->offset; } static PixmapPtr lx_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) { ScrnInfoPtr pScrni = crtc->scrn; PixmapPtr rpixmap; unsigned int rpitch; rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8); if (!data) data = lx_crtc_shadow_allocate(crtc, width, height); rpixmap = lx_create_bo_pixmap(pScrni->pScreen, width, height, pScrni->depth, pScrni->bitsPerPixel, rpitch, data); if (rpixmap == NULL) { xf86DrvMsg(pScrni->scrnIndex, X_ERROR, "Couldn't allocate shadow pixmap for rotated CRTC\n"); } return rpixmap; } static void lx_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rpixmap, void *data) { ScrnInfoPtr pScrni = crtc->scrn; GeodeRec *pGeode = GEODEPTR(pScrni); if (rpixmap) lx_destory_bo_pixmap(rpixmap); /* Free shadow memory */ if (data) { gp_wait_until_idle(); if (pGeode->shadowArea != NULL) { exaOffscreenFree(pScrni->pScreen, pGeode->shadowArea); pGeode->shadowArea = NULL; } } } static void lx_crtc_set_cursor_colors(xf86CrtcPtr crtc, int bg, int fg) { vg_set_mono_cursor_colors(bg, fg); } static void lx_crtc_set_cursor_position(xf86CrtcPtr crtc, int x, int y) { VG_PANNING_COORDINATES panning; vg_set_cursor_position(x, y, &panning); } static void lx_crtc_show_cursor(xf86CrtcPtr crtc) { vg_set_cursor_enable(1); } static void lx_crtc_hide_cursor(xf86CrtcPtr crtc) { vg_set_cursor_enable(0); } static void lx_crtc_load_cursor_argb(xf86CrtcPtr crtc, CARD32 *image) { LXLoadARGBCursorImage(crtc->scrn, (unsigned char *) image); } static const xf86CrtcFuncsRec lx_crtc_funcs = { .dpms = lx_crtc_dpms, .lock = lx_crtc_lock, .unlock = lx_crtc_unlock, .mode_fixup = lx_crtc_mode_fixup, .prepare = lx_crtc_prepare, .mode_set = lx_crtc_mode_set, .commit = lx_crtc_commit, .gamma_set = lx_crtc_gamma_set, .shadow_create = lx_crtc_shadow_create, .shadow_allocate = lx_crtc_shadow_allocate, .shadow_destroy = lx_crtc_shadow_destroy, .set_cursor_colors = lx_crtc_set_cursor_colors, .set_cursor_position = lx_crtc_set_cursor_position, .show_cursor = lx_crtc_show_cursor, .hide_cursor = lx_crtc_hide_cursor, .load_cursor_argb = lx_crtc_load_cursor_argb, }; void LXSetupCrtc(ScrnInfoPtr pScrni) { xf86CrtcPtr crtc; LXCrtcPrivatePtr lxpriv; crtc = xf86CrtcCreate(pScrni, &lx_crtc_funcs); if (crtc == NULL) { ErrorF("ERROR - failed to create a CRTC\n"); return; } lxpriv = XNFcallocarray(1, sizeof(LXCrtcPrivateRec)); if (!lxpriv) { xf86CrtcDestroy(crtc); ErrorF("unable to allocate memory for lxpriv\n"); return; } crtc->driver_private = lxpriv; }