summaryrefslogtreecommitdiff
path: root/src/lx_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lx_display.c')
-rw-r--r--src/lx_display.c492
1 files changed, 492 insertions, 0 deletions
diff --git a/src/lx_display.c b/src/lx_display.c
new file mode 100644
index 0000000..c53a9f5
--- /dev/null
+++ b/src/lx_display.c
@@ -0,0 +1,492 @@
+/* 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;
+
+static 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);
+}
+
+static 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)
+{
+ 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);
+}
+
+static void
+lx_set_crt_mode(VG_DISPLAY_MODE * mode, DisplayModePtr pMode)
+{
+ 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);
+}
+
+static int
+lx_set_mode(ScrnInfoPtr pScrni, DisplayModePtr pMode, int bpp)
+{
+ GeodeRec *pGeode = GEODEPTR(pScrni);
+ VG_DISPLAY_MODE mode;
+ int hsync, vsync;
+ int ret;
+
+ memset(&mode, 0, sizeof(mode));
+
+ /* Cimarron purposely swaps the sync when panels are enabled -this is
+ * presumably to allow for "default" panels which are normally active
+ * low, so we need to swizzle the flags
+ */
+
+ hsync = (pMode->Flags & V_NHSYNC) ? 1 : 0;
+ vsync = (pMode->Flags & V_NVSYNC) ? 1 : 0;
+
+ if (pGeode->Output & OUTPUT_PANEL) {
+ hsync = !vsync;
+ vsync = !vsync;
+ }
+
+ mode.flags |= (hsync) ? VG_MODEFLAG_NEG_HSYNC : 0;
+ mode.flags |= (vsync) ? VG_MODEFLAG_NEG_VSYNC : 0;
+
+ 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;
+
+ 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) {
+ if (DCONDPMSSet(pScrni, mode))
+ return;
+ }
+
+ 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;
+
+ 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 */
+
+ vg_set_display_pitch(pGeode->Pitch);
+ gp_set_bpp(pScrni->bitsPerPixel);
+
+ /* FIXME: Whats up with X and Y? Does that come into play
+ * here? */
+
+ vg_set_display_offset(0);
+ 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);
+
+ /* 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);
+
+ for (i = 0; i < 256; i++) {
+ unsigned int 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);
+}
+
+static void *
+lx_crtc_shadow_allocate(xf86CrtcPtr crtc, int width, int height)
+{
+ ScrnInfoPtr pScrni = crtc->scrn;
+ GeodePtr pGeode = GEODEPTR(pScrni);
+ LXCrtcPrivatePtr lx_crtc = crtc->driver_private;
+ unsigned int rpitch, size;
+
+ rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
+ size = rpitch * height;
+
+ lx_crtc->rotate_mem = GeodeAllocOffscreen(pGeode, size, 4);
+
+ if (lx_crtc->rotate_mem == NULL) {
+ xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
+ "Couldn't allocate shadow memory for rotated CRTC\n");
+ return NULL;
+ }
+
+ memset(pGeode->FBBase + lx_crtc->rotate_mem->offset, 0, size);
+ return pGeode->FBBase + lx_crtc->rotate_mem->offset;
+}
+
+static PixmapPtr
+lx_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height)
+{
+ ScrnInfoPtr pScrni = crtc->scrn;
+ PixmapPtr rpixmap;
+ unsigned int rpitch;
+
+ if (!data)
+ data = lx_crtc_shadow_allocate(crtc, width, height);
+
+ rpitch = pScrni->displayWidth * (pScrni->bitsPerPixel / 8);
+
+ rpixmap = GetScratchPixmapHeader(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);
+ LXCrtcPrivatePtr lx_crtc = crtc->driver_private;
+
+ if (rpixmap)
+ FreeScratchPixmapHeader(rpixmap);
+
+ if (data) {
+ gp_wait_until_idle();
+ GeodeFreeOffscreen(pGeode, lx_crtc->rotate_mem);
+ lx_crtc->rotate_mem = 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;
+
+ /* FIXME: Do I need to worry about rotation adjustment here? */
+
+ switch (crtc->rotation) {
+ case RR_Rotate_0:
+ x += 31;
+ y += 31;
+ }
+
+ 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_image(xf86CrtcPtr crtc, unsigned char *src)
+{
+ ScrnInfoPtr pScrni = crtc->scrn;
+
+ LXLoadCursorImage(pScrni, src);
+}
+
+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_image = lx_crtc_load_cursor_image,
+};
+
+void
+LXSetupCrtc(ScrnInfoPtr pScrni)
+{
+ xf86CrtcPtr crtc;
+ LXCrtcPrivatePtr lxpriv;
+
+ crtc = xf86CrtcCreate(pScrni, &lx_crtc_funcs);
+
+ if (crtc == NULL) {
+ ErrorF("ERROR - xf86CrtcCreate() fail %x\n", crtc);
+ return;
+ }
+
+ lxpriv = xnfcalloc(sizeof(LXCrtcPrivateRec), 1);
+
+ if (!lxpriv) {
+ xf86CrtcDestroy(crtc);
+ ErrorF("unable to allocate memory for lxpriv\n");
+ return;
+ }
+
+ crtc->driver_private = lxpriv;
+}