summaryrefslogtreecommitdiff
path: root/src/cim/cim_vg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cim/cim_vg.c')
-rw-r--r--src/cim/cim_vg.c3711
1 files changed, 3711 insertions, 0 deletions
diff --git a/src/cim/cim_vg.c b/src/cim/cim_vg.c
new file mode 100644
index 0000000..6a8ea49
--- /dev/null
+++ b/src/cim/cim_vg.c
@@ -0,0 +1,3711 @@
+/*
+ * Copyright (c) 2006 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.
+ */
+
+ /*
+ * Cimarron display controller routines. These routines program the display
+ * mode and configure the hardware cursor and video buffers.
+ */
+
+/*---------------------*/
+/* CIMARRON VG GLOBALS */
+/*---------------------*/
+
+CIMARRON_STATIC unsigned long vg3_x_hotspot = 0;
+CIMARRON_STATIC unsigned long vg3_y_hotspot = 0;
+CIMARRON_STATIC unsigned long vg3_cursor_offset = 0;
+CIMARRON_STATIC unsigned long vg3_mode_width = 0;
+CIMARRON_STATIC unsigned long vg3_mode_height = 0;
+CIMARRON_STATIC unsigned long vg3_panel_width = 0;
+CIMARRON_STATIC unsigned long vg3_panel_height = 0;
+CIMARRON_STATIC unsigned long vg3_delta_x = 0;
+CIMARRON_STATIC unsigned long vg3_delta_y = 0;
+CIMARRON_STATIC unsigned long vg3_bpp = 0;
+
+CIMARRON_STATIC unsigned long vg3_color_cursor = 0;
+CIMARRON_STATIC unsigned long vg3_panel_enable = 0;
+
+/*---------------------------------------------------------------------------
+ * vg_delay_milliseconds
+ *
+ * This routine delays for a number of milliseconds based on a crude
+ * delay loop.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_delay_milliseconds(unsigned long ms)
+{
+ /* ASSUME 500 MHZ 20 CLOCKS PER READ */
+
+ unsigned long loop = ms * 25000;
+
+ while (loop-- > 0) {
+ READ_REG32(DC3_UNLOCK);
+ }
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_display_mode
+ *
+ * This routine sets a CRT display mode using predefined Cimarron timings.
+ * The source width and height are specified to allow scaling.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_mode(unsigned long src_width, unsigned long src_height,
+ unsigned long dst_width, unsigned long dst_height,
+ int bpp, int hz, unsigned long flags)
+{
+ VG_QUERY_MODE crt_query;
+ VG_DISPLAY_MODE crt_mode;
+ int mode;
+
+ crt_query.active_width = dst_width;
+ crt_query.active_height = dst_height;
+ crt_query.bpp = bpp;
+ crt_query.hz = hz;
+ crt_query.query_flags = VG_QUERYFLAG_ACTIVEWIDTH |
+ VG_QUERYFLAG_ACTIVEHEIGHT | VG_QUERYFLAG_BPP | VG_QUERYFLAG_REFRESH;
+
+ mode = vg_get_display_mode_index(&crt_query);
+ if (mode >= 0) {
+ crt_mode = CimarronDisplayModes[mode];
+ crt_mode.src_width = src_width;
+ crt_mode.src_height = src_height;
+
+ /* ADD USER-REQUESTED FLAGS */
+
+ crt_mode.flags |= (flags & VG_MODEFLAG_VALIDUSERFLAGS);
+
+ if (flags & VG_MODEFLAG_OVERRIDE_BAND) {
+ crt_mode.flags &= ~VG_MODEFLAG_BANDWIDTHMASK;
+ crt_mode.flags |= (flags & VG_MODEFLAG_BANDWIDTHMASK);
+ }
+ if (flags & VG_MODEFLAG_INT_OVERRIDE) {
+ crt_mode.flags &= ~VG_MODEFLAG_INT_MASK;
+ crt_mode.flags |= (flags & VG_MODEFLAG_INT_MASK);
+ }
+
+ return vg_set_custom_mode(&crt_mode, bpp);
+ }
+ return CIM_STATUS_ERROR;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_panel_mode
+ *
+ * This routine sets a panel mode using predefined Cimarron fixed timings.
+ * The source width and height specify the width and height of the data in
+ * the frame buffer. The destination width and height specify the width and
+ * height of the active data to be displayed. The panel width and height
+ * specify the dimensions of the panel. This interface allows the user to
+ * scale or center graphics data or both. To perform scaling, the src width
+ * or height should be different than the destination width or height. To
+ * perform centering or panning, the destination width and height should be
+ * different than the panel resolution.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_panel_mode(unsigned long src_width, unsigned long src_height,
+ unsigned long dst_width, unsigned long dst_height,
+ unsigned long panel_width, unsigned long panel_height,
+ int bpp, unsigned long flags)
+{
+ unsigned long sync_width;
+ unsigned long sync_offset;
+ VG_QUERY_MODE panel_query;
+ VG_DISPLAY_MODE panel_mode;
+ int mode;
+
+ /* SEARCH CIMARRON'S TABLE OF PREDEFINED PANEL MODES */
+ /* If the destination resolution is larger than the panel resolution, */
+ /* panning will be performed. However, the timings for a panned mode */
+ /* are identical to the timings without panning. To save space in the */
+ /* mode tables, there are no additional table entries for modes with */
+ /* panning. Instead, we read the timings for a mode without panning */
+ /* and override the structure entries that specify the width and */
+ /* height of the mode. We perform a similar procedure for centered */
+ /* modes, except that certain timing parameters are dynamically */
+ /* calculated. */
+
+ panel_query.active_width = panel_width;
+ panel_query.active_height = panel_height;
+ panel_query.panel_width = panel_width;
+ panel_query.panel_height = panel_height;
+ panel_query.bpp = bpp;
+ panel_query.query_flags = VG_QUERYFLAG_ACTIVEWIDTH |
+ VG_QUERYFLAG_ACTIVEHEIGHT |
+ VG_QUERYFLAG_PANELWIDTH |
+ VG_QUERYFLAG_PANELHEIGHT | VG_QUERYFLAG_PANEL | VG_QUERYFLAG_BPP;
+
+ mode = vg_get_display_mode_index(&panel_query);
+
+ /* COPY THE DATA FROM THE MODE TABLE TO A TEMPORARY STRUCTURE */
+
+ if (mode >= 0) {
+ panel_mode = CimarronDisplayModes[mode];
+ panel_mode.mode_width = dst_width;
+ panel_mode.mode_height = dst_height;
+ panel_mode.src_width = src_width;
+ panel_mode.src_height = src_height;
+
+ /* ADD USER-REQUESTED FLAGS */
+
+ panel_mode.flags |= (flags & VG_MODEFLAG_VALIDUSERFLAGS);
+
+ if (flags & VG_MODEFLAG_OVERRIDE_BAND) {
+ panel_mode.flags &= ~VG_MODEFLAG_BANDWIDTHMASK;
+ panel_mode.flags |= (flags & VG_MODEFLAG_BANDWIDTHMASK);
+ }
+ if (flags & VG_MODEFLAG_INT_OVERRIDE) {
+ panel_mode.flags &= ~VG_MODEFLAG_INT_MASK;
+ panel_mode.flags |= (flags & VG_MODEFLAG_INT_MASK);
+ }
+
+ /* DYNAMICALLY CALCULATE CENTERED TIMINGS */
+ /* For centered timings the blank start and blank end are set to */
+ /* half the difference between the mode dimension and the panel */
+ /* dimension. The sync pulse preserves the width and offset from */
+ /* blanking whenever possible. */
+
+ if (dst_width < panel_width) {
+ sync_width = panel_mode.hsyncend - panel_mode.hsyncstart;
+ sync_offset = panel_mode.hsyncstart - panel_mode.hblankstart;
+
+ panel_mode.hactive = dst_width;
+ panel_mode.hblankstart =
+ panel_mode.hactive + ((panel_width - dst_width) >> 1);
+ panel_mode.hblankend =
+ panel_mode.htotal - ((panel_width - dst_width) >> 1);
+ panel_mode.hsyncstart = panel_mode.hblankstart + sync_offset;
+ panel_mode.hsyncend = panel_mode.hsyncstart + sync_width;
+
+ panel_mode.flags |= VG_MODEFLAG_CENTERED;
+ }
+ if (dst_height < panel_height) {
+ sync_width = panel_mode.vsyncend - panel_mode.vsyncstart;
+ sync_offset = panel_mode.vsyncstart - panel_mode.vblankstart;
+
+ panel_mode.vactive = dst_height;
+ panel_mode.vblankstart =
+ panel_mode.vactive + ((panel_height - dst_height) >> 1);
+ panel_mode.vblankend =
+ panel_mode.vtotal - ((panel_height - dst_height) >> 1);
+ panel_mode.vsyncstart = panel_mode.vblankstart + sync_offset;
+ panel_mode.vsyncend = panel_mode.vsyncstart + sync_width;
+
+ panel_mode.flags |= VG_MODEFLAG_CENTERED;
+ }
+ return vg_set_custom_mode(&panel_mode, bpp);
+ }
+ return CIM_STATUS_ERROR;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_tv_mode
+ *
+ * This routine sets a TV display mode using predefined Cimarron timings. The
+ * source width and height are specified to allow scaling.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_tv_mode(unsigned long *src_width, unsigned long *src_height,
+ unsigned long encoder, unsigned long tvres, int bpp,
+ unsigned long flags, unsigned long h_overscan, unsigned long v_overscan)
+{
+ unsigned long sync_width;
+ unsigned long sync_offset;
+ VG_QUERY_MODE tv_query;
+ VG_DISPLAY_MODE tv_mode;
+ int mode;
+
+ if (!src_width || !src_height)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ tv_query.bpp = bpp;
+ tv_query.encoder = encoder;
+ tv_query.tvmode = tvres;
+ tv_query.query_flags = VG_QUERYFLAG_BPP | VG_QUERYFLAG_TVOUT |
+ VG_QUERYFLAG_ENCODER | VG_QUERYFLAG_TVMODE;
+
+ mode = vg_get_display_mode_index(&tv_query);
+ if (mode >= 0) {
+ /* RETRIEVE THE UNSCALED RESOLUTION
+ * As we are indexing here simply by a mode and encoder, the actual
+ * timings may vary. A 0 value for source or height will thus query
+ * the unscaled resolution.
+ */
+
+ if (!(*src_width) || !(*src_height)) {
+ *src_width = CimarronDisplayModes[mode].hactive -
+ (h_overscan << 1);
+ *src_height = CimarronDisplayModes[mode].vactive;
+
+ if (CimarronDisplayModes[mode].flags & VG_MODEFLAG_INTERLACED) {
+ if (((flags & VG_MODEFLAG_INT_OVERRIDE) &&
+ (flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_LINEDOUBLE)
+ || (!(flags & VG_MODEFLAG_INT_OVERRIDE)
+ && (CimarronDisplayModes[mode].
+ flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_LINEDOUBLE)) {
+ if (CimarronDisplayModes[mode].vactive_even >
+ CimarronDisplayModes[mode].vactive)
+ *src_height = CimarronDisplayModes[mode].vactive_even;
+
+ /* ONLY 1/2 THE OVERSCAN FOR LINE DOUBLED MODES */
+
+ *src_height -= v_overscan;
+ } else {
+ *src_height += CimarronDisplayModes[mode].vactive_even;
+ *src_height -= v_overscan << 1;
+ }
+ } else {
+ *src_height -= v_overscan << 1;
+ }
+
+ return CIM_STATUS_OK;
+ }
+
+ tv_mode = CimarronDisplayModes[mode];
+ tv_mode.src_width = *src_width;
+ tv_mode.src_height = *src_height;
+
+ /* ADD USER-REQUESTED FLAGS */
+
+ tv_mode.flags |= (flags & VG_MODEFLAG_VALIDUSERFLAGS);
+
+ if (flags & VG_MODEFLAG_OVERRIDE_BAND) {
+ tv_mode.flags &= ~VG_MODEFLAG_BANDWIDTHMASK;
+ tv_mode.flags |= (flags & VG_MODEFLAG_BANDWIDTHMASK);
+ }
+ if (flags & VG_MODEFLAG_INT_OVERRIDE) {
+ tv_mode.flags &= ~VG_MODEFLAG_INT_MASK;
+ tv_mode.flags |= (flags & VG_MODEFLAG_INT_MASK);
+ }
+
+ /* ADJUST FOR OVERSCAN */
+
+ if (h_overscan) {
+ sync_width = tv_mode.hsyncend - tv_mode.hsyncstart;
+ sync_offset = tv_mode.hsyncstart - tv_mode.hblankstart;
+
+ tv_mode.hactive -= h_overscan << 1;
+ tv_mode.hblankstart = tv_mode.hactive + h_overscan;
+ tv_mode.hblankend = tv_mode.htotal - h_overscan;
+ tv_mode.hsyncstart = tv_mode.hblankstart + sync_offset;
+ tv_mode.hsyncend = tv_mode.hsyncstart + sync_width;
+
+ tv_mode.flags |= VG_MODEFLAG_CENTERED;
+ }
+ if (v_overscan) {
+ sync_width = tv_mode.vsyncend - tv_mode.vsyncstart;
+ sync_offset = tv_mode.vsyncstart - tv_mode.vblankstart;
+
+ if (tv_mode.flags & VG_MODEFLAG_INTERLACED) {
+ tv_mode.vactive -= v_overscan;
+ tv_mode.vblankstart = tv_mode.vactive + (v_overscan >> 1);
+ tv_mode.vblankend = tv_mode.vtotal - (v_overscan >> 1);
+ tv_mode.vsyncstart = tv_mode.vblankstart + sync_offset;
+ tv_mode.vsyncend = tv_mode.vsyncstart + sync_width;
+
+ sync_width = tv_mode.vsyncend_even - tv_mode.vsyncstart_even;
+ sync_offset = tv_mode.vsyncstart_even -
+ tv_mode.vblankstart_even;
+
+ tv_mode.vactive_even -= v_overscan;
+ tv_mode.vblankstart_even =
+ tv_mode.vactive_even + (v_overscan >> 1);
+ tv_mode.vblankend_even =
+ tv_mode.vtotal_even - (v_overscan >> 1);
+ tv_mode.vsyncstart_even =
+ tv_mode.vblankstart_even + sync_offset;
+ tv_mode.vsyncend_even = tv_mode.vsyncstart_even + sync_width;
+ } else {
+ tv_mode.vactive -= v_overscan << 1;
+ tv_mode.vblankstart = tv_mode.vactive + v_overscan;
+ tv_mode.vblankend = tv_mode.vtotal - v_overscan;
+ tv_mode.vsyncstart = tv_mode.vblankstart + sync_offset;
+ tv_mode.vsyncend = tv_mode.vsyncstart + sync_width;
+ }
+
+ tv_mode.flags |= VG_MODEFLAG_CENTERED;
+ }
+
+ /* TV MODES WILL NEVER ALLOW PANNING */
+
+ tv_mode.panel_width = tv_mode.hactive;
+ tv_mode.panel_height = tv_mode.vactive;
+ tv_mode.mode_width = tv_mode.hactive;
+ tv_mode.mode_height = tv_mode.vactive;
+
+ return vg_set_custom_mode(&tv_mode, bpp);
+ }
+ return CIM_STATUS_ERROR;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_custom_mode
+ *
+ * This routine sets a display mode. The API is structured such that this
+ * routine can be called from four sources:
+ * - vg_set_display_mode
+ * - vg_set_panel_mode
+ * - vg_set_tv_mode
+ * - directly by the user for a custom mode.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_custom_mode(VG_DISPLAY_MODE * mode_params, int bpp)
+{
+ unsigned long config, misc, temp;
+ unsigned long irq_ctl, genlk_ctl;
+ unsigned long unlock, flags;
+ unsigned long acfg, gcfg, dcfg;
+ unsigned long size, line_size, pitch;
+ unsigned long bpp_mask, dv_size;
+ unsigned long hscale, vscale, starting_width;
+ unsigned long starting_height, output_height;
+ Q_WORD msr_value;
+
+ /* DETERMINE DIMENSIONS FOR SCALING */
+ /* Scaling is performed before flicker filtering and interlacing */
+
+ output_height = mode_params->vactive;
+
+ if (mode_params->flags & VG_MODEFLAG_INTERLACED) {
+ /* EVEN AND ODD FIELDS ARE SEPARATE
+ * The composite image height is the sum of the height of both
+ * fields
+ */
+
+ if ((mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_FLICKER
+ || (mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_ADDRESS) {
+ output_height += mode_params->vactive_even;
+ }
+
+ /* LINE DOUBLING
+ * The composite image height is the greater of the two field
+ * heights.
+ */
+
+ else if (mode_params->vactive_even > output_height)
+ output_height = mode_params->vactive_even;
+ }
+
+ /* CHECK FOR VALID SCALING FACTOR
+ * GeodeLX supports only 2:1 vertical downscale (before interlacing) and
+ * 2:1 horizontal downscale. The source width when scaling must be
+ * less than or equal to 1024 pixels. The destination can be any size,
+ * except when flicker filtering is enabled.
+ */
+
+ irq_ctl = 0;
+ if (mode_params->flags & VG_MODEFLAG_PANELOUT) {
+ if (mode_params->src_width != mode_params->mode_width) {
+ starting_width = (mode_params->hactive * mode_params->src_width) /
+ mode_params->mode_width;
+ hscale = (mode_params->src_width << 14) /
+ (mode_params->mode_width - 1);
+ irq_ctl |= (DC3_IRQFILT_ALPHA_FILT_EN | DC3_IRQFILT_GFX_FILT_EN);
+ } else {
+ starting_width = mode_params->hactive;
+ hscale = 0x4000;
+ }
+ if (mode_params->src_height != mode_params->mode_height) {
+ starting_height = (output_height * mode_params->src_height) /
+ mode_params->mode_height;
+ vscale = (mode_params->src_height << 14) /
+ (mode_params->mode_height - 1);
+ irq_ctl |= (DC3_IRQFILT_ALPHA_FILT_EN | DC3_IRQFILT_GFX_FILT_EN);
+ } else {
+ starting_height = output_height;
+ vscale = 0x4000;
+ }
+ } else {
+ starting_width = mode_params->src_width;
+ starting_height = mode_params->src_height;
+ if (mode_params->src_width != mode_params->hactive) {
+ hscale = (mode_params->src_width << 14) /
+ (mode_params->hactive - 1);
+ irq_ctl |= (DC3_IRQFILT_ALPHA_FILT_EN | DC3_IRQFILT_GFX_FILT_EN);
+ } else {
+ hscale = 0x4000;
+ }
+ if (mode_params->src_height != output_height) {
+ vscale = (mode_params->src_height << 14) / (output_height - 1);
+ irq_ctl |= (DC3_IRQFILT_ALPHA_FILT_EN | DC3_IRQFILT_GFX_FILT_EN);
+ } else {
+ vscale = 0x4000;
+ }
+ }
+
+ starting_width = (starting_width + 7) & 0xFFFF8;
+
+ if (mode_params->hactive < (starting_width >> 1) ||
+ output_height < (starting_height >> 1) ||
+ (irq_ctl && (starting_width > 1024))) {
+ return CIM_STATUS_INVALIDSCALE;
+ }
+
+ /* VERIFY INTERLACED SCALING */
+ /* The output width must be less than or equal to 1024 pixels when the */
+ /* flicker filter is enabled. Also, scaling should be disabled when */
+ /* the interlacing mode is set to interlaced addressing. */
+
+ if (mode_params->flags & VG_MODEFLAG_INTERLACED) {
+ if ((((mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_FLICKER) && (mode_params->hactive > 1024))
+ || (((mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_ADDRESS) && irq_ctl)) {
+ return CIM_STATUS_INVALIDSCALE;
+ }
+ }
+
+ /* CHECK FOR VALID BPP */
+
+ switch (bpp) {
+ case 8:
+ bpp_mask = DC3_DCFG_DISP_MODE_8BPP;
+ break;
+ case 24:
+ bpp_mask = DC3_DCFG_DISP_MODE_24BPP;
+ break;
+ case 32:
+ bpp_mask = DC3_DCFG_DISP_MODE_32BPP;
+ break;
+ case 12:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_12BPP;
+ break;
+ case 15:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_15BPP;
+ break;
+ case 16:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_16BPP;
+ break;
+ default:
+ return CIM_STATUS_INVALIDPARAMS;
+ }
+
+ vg3_bpp = bpp;
+
+ /* CLEAR PANNING OFFSETS */
+
+ vg3_delta_x = 0;
+ vg3_delta_y = 0;
+
+ /* SAVE PANEL PARAMETERS */
+
+ if (mode_params->flags & VG_MODEFLAG_PANELOUT) {
+ vg3_panel_enable = 1;
+ vg3_panel_width = mode_params->panel_width;
+ vg3_panel_height = mode_params->panel_height;
+ vg3_mode_width = mode_params->mode_width;
+ vg3_mode_height = mode_params->mode_height;
+
+ /* INVERT THE SHIFT CLOCK IF REQUESTED */
+ /* Note that we avoid writing the power management register if */
+ /* we can help it. */
+
+ temp = READ_VID32(DF_POWER_MANAGEMENT);
+ if ((mode_params->flags & VG_MODEFLAG_INVERT_SHFCLK) &&
+ !(temp & DF_PM_INVERT_SHFCLK)) {
+ WRITE_VID32(DF_POWER_MANAGEMENT, (temp | DF_PM_INVERT_SHFCLK));
+ } else if (!(mode_params->flags & VG_MODEFLAG_INVERT_SHFCLK) &&
+ (temp & DF_PM_INVERT_SHFCLK)) {
+ WRITE_VID32(DF_POWER_MANAGEMENT, (temp & ~DF_PM_INVERT_SHFCLK));
+ }
+
+ /* SET PANEL TIMING VALUES */
+
+ if (!(mode_params->flags & VG_MODEFLAG_NOPANELTIMINGS)) {
+ unsigned long pmtim1, pmtim2, dith_ctl;
+
+ if (mode_params->flags & VG_MODEFLAG_XVGA_TFT) {
+ pmtim1 = DF_DEFAULT_XVGA_PMTIM1;
+ pmtim2 = DF_DEFAULT_XVGA_PMTIM2;
+ dith_ctl = DF_DEFAULT_DITHCTL;
+ msr_value.low = DF_DEFAULT_XVGA_PAD_SEL_LOW;
+ msr_value.high = DF_DEFAULT_XVGA_PAD_SEL_HIGH;
+ } else if (mode_params->flags & VG_MODEFLAG_CUSTOM_PANEL) {
+ pmtim1 = mode_params->panel_tim1;
+ pmtim2 = mode_params->panel_tim2;
+ dith_ctl = mode_params->panel_dither_ctl;
+ msr_value.low = mode_params->panel_pad_sel_low;
+ msr_value.high = mode_params->panel_pad_sel_high;
+ } else {
+ pmtim1 = DF_DEFAULT_TFT_PMTIM1;
+ pmtim2 = DF_DEFAULT_TFT_PMTIM2;
+ dith_ctl = DF_DEFAULT_DITHCTL;
+ msr_value.low = DF_DEFAULT_TFT_PAD_SEL_LOW;
+ msr_value.high = DF_DEFAULT_TFT_PAD_SEL_HIGH;
+
+ }
+ WRITE_VID32(DF_VIDEO_PANEL_TIM1, pmtim1);
+ WRITE_VID32(DF_VIDEO_PANEL_TIM2, pmtim2);
+ WRITE_VID32(DF_DITHER_CONTROL, dith_ctl);
+ msr_write64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL, &msr_value);
+ }
+
+ /* SET APPROPRIATE PANEL OUTPUT MODE */
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+
+ msr_value.low &= ~DF_CONFIG_OUTPUT_MASK;
+ msr_value.low |= DF_OUTPUT_PANEL;
+ if (mode_params->flags & VG_MODEFLAG_CRT_AND_FP)
+ msr_value.low |= DF_SIMULTANEOUS_CRT_FP;
+ else
+ msr_value.low &= ~DF_SIMULTANEOUS_CRT_FP;
+
+ msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+
+ } else if (mode_params->flags & VG_MODEFLAG_TVOUT) {
+ vg3_panel_enable = 0;
+
+ /* SET APPROPRIATE TV OUTPUT MODE */
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+
+ msr_value.low &= ~DF_CONFIG_OUTPUT_MASK;
+ msr_value.low |= DF_OUTPUT_PANEL;
+ if (mode_params->flags & VG_MODEFLAG_CRT_AND_FP)
+ msr_value.low |= DF_SIMULTANEOUS_CRT_FP;
+ else
+ msr_value.low &= ~DF_SIMULTANEOUS_CRT_FP;
+
+ msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+
+ /* CONFIGURE PADS FOR VOP OUTPUT */
+ /* Note that the VOP clock is currently always inverted. */
+
+ msr_value.low = DF_DEFAULT_TV_PAD_SEL_LOW;
+ msr_value.high = DF_DEFAULT_TV_PAD_SEL_HIGH;
+ msr_write64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL, &msr_value);
+ } else {
+ vg3_panel_enable = 0;
+
+ /* SET OUTPUT TO CRT ONLY */
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+ msr_value.low &= ~DF_CONFIG_OUTPUT_MASK;
+ msr_value.low |= DF_OUTPUT_CRT;
+ msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+ }
+
+ /* SET UNLOCK VALUE */
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ /*-------------------------------------------------------------------*/
+ /* MAKE THE SYSTEM "SAFE" */
+ /* Before setting a mode, we first ensure that the system is in a */
+ /* benign quiescent state. This involves disabling compression and */
+ /* all interrupt sources. It also involves terminating all accesses */
+ /* to memory, including video, FIFO load, VIP and the GP. */
+ /*-------------------------------------------------------------------*/
+
+ /* DISABLE VGA
+ * VGA *MUST* be turned off before TGEN is enabled. If not, a condition
+ * will result where VGA Enable is waiting for a VSync to be latched but
+ * a VSync will not be generated until VGA is disabled.
+ */
+
+ temp = READ_REG32(DC3_GENERAL_CFG) & ~DC3_GCFG_VGAE;
+
+ /* DISABLE VIDEO (INCLUDING ALPHA WINDOWS) */
+
+ WRITE_VID32(DF_ALPHA_CONTROL_1, 0);
+ WRITE_VID32(DF_ALPHA_CONTROL_1 + 32, 0);
+ WRITE_VID32(DF_ALPHA_CONTROL_1 + 64, 0);
+
+ WRITE_REG32(DC3_GENERAL_CFG, (temp & ~DC3_GCFG_VIDE));
+ temp = READ_VID32(DF_VIDEO_CONFIG);
+ WRITE_VID32(DF_VIDEO_CONFIG, (temp & ~DF_VCFG_VID_EN));
+
+ /* DISABLE VG INTERRUPTS */
+
+ WRITE_REG32(DC3_IRQ, DC3_IRQ_MASK | DC3_VSYNC_IRQ_MASK |
+ DC3_IRQ_STATUS | DC3_VSYNC_IRQ_STATUS);
+
+ /* DISABLE GENLOCK */
+
+ genlk_ctl = READ_REG32(DC3_GENLK_CTL);
+ WRITE_REG32(DC3_GENLK_CTL, (genlk_ctl & ~DC3_GC_GENLOCK_ENABLE));
+
+ /* DISABLE VIP CAPTURE AND VIP INTERRUPTS */
+
+ WRITE_VIP32(VIP_CONTROL1, 0);
+ WRITE_VIP32(VIP_CONTROL2, 0);
+ WRITE_VIP32(VIP_INTERRUPT,
+ VIP_ALL_INTERRUPTS | (VIP_ALL_INTERRUPTS >> 16));
+
+ /* DISABLE COLOR KEYING
+ * The color key mechanism should be disabled whenever a mode switch
+ * occurs.
+ */
+
+ temp = READ_REG32(DC3_COLOR_KEY);
+ WRITE_REG32(DC3_COLOR_KEY, (temp & ~DC3_CLR_KEY_ENABLE));
+
+ /* BLANK THE DISPLAY
+ * Note that we never blank the panel. Most flat panels have very long
+ * latency requirements when setting their power low. Some panels require
+ * upwards of 500ms before VDD goes high again. Needless to say, we are
+ * not planning to take over one half a second inside this routine.
+ */
+
+ misc = READ_VID32(DF_VID_MISC);
+ config = READ_VID32(DF_DISPLAY_CONFIG);
+
+ WRITE_VID32(DF_VID_MISC, (misc | DF_DAC_POWER_DOWN));
+ WRITE_VID32(DF_DISPLAY_CONFIG,
+ (config & ~(DF_DCFG_DIS_EN | DF_DCFG_HSYNC_EN |
+ DF_DCFG_VSYNC_EN | DF_DCFG_DAC_BL_EN)));
+
+ /* DISABLE COMPRESSION */
+
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ gcfg &= ~(DC3_GCFG_CMPE | DC3_GCFG_DECE);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+
+ /* DISABLE THE TIMING GENERATOR */
+
+ dcfg = READ_REG32(DC3_DISPLAY_CFG);
+ dcfg &= ~DC3_DCFG_TGEN;
+ WRITE_REG32(DC3_DISPLAY_CFG, dcfg);
+
+ /* WAIT FOR PENDING MEMORY REQUESTS */
+
+ vg_delay_milliseconds(1);
+
+ /* DISABLE DISPLAY FIFO LOAD */
+
+ gcfg &= ~DC3_GCFG_DFLE;
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+ gcfg = 0;
+ dcfg = 0;
+
+ /* WAIT FOR THE GP TO BE IDLE (JUST IN CASE) */
+
+ while (((temp = READ_GP32(GP3_BLT_STATUS)) & GP3_BS_BLT_BUSY) ||
+ !(temp & GP3_BS_CB_EMPTY)) {
+ ;
+ }
+
+ /* SET THE DOT CLOCK FREQUENCY */
+
+ if (!(mode_params->flags & VG_MODEFLAG_EXCLUDEPLL)) {
+ if (mode_params->flags & VG_MODEFLAG_HALFCLOCK)
+ flags = VG_PLL_DIVIDE_BY_2;
+ else if (mode_params->flags & VG_MODEFLAG_QVGA)
+ flags = VG_PLL_DIVIDE_BY_4;
+ else
+ flags = 0;
+
+ /* ALLOW DOTREF TO BE USED AS THE PLL */
+ /* This is useful for some external TV encoders. */
+
+ if (mode_params->flags & VG_MODEFLAG_PLL_BYPASS)
+ flags |= VG_PLL_BYPASS;
+
+ /* ALLOW THE USER TO MANUALLY ENTER THE MSR VALUE */
+
+ if (mode_params->flags & VG_MODEFLAG_MANUAL_FREQUENCY)
+ flags |= VG_PLL_MANUAL;
+ if (mode_params->flags & VG_MODEFLAG_VIP_TO_DOT_CLOCK)
+ flags |= VG_PLL_VIP_CLOCK;
+
+ vg_set_clock_frequency(mode_params->frequency, flags);
+ }
+
+ /* CLEAR ALL BUFFER OFFSETS */
+
+ WRITE_REG32(DC3_FB_ST_OFFSET, 0);
+ WRITE_REG32(DC3_CB_ST_OFFSET, 0);
+ WRITE_REG32(DC3_CURS_ST_OFFSET, 0);
+
+ genlk_ctl = READ_REG32(DC3_GENLK_CTL) & ~(DC3_GC_ALPHA_FLICK_ENABLE |
+ DC3_GC_FLICKER_FILTER_ENABLE | DC3_GC_FLICKER_FILTER_MASK);
+
+ /* ENABLE INTERLACING */
+
+ if (mode_params->flags & VG_MODEFLAG_INTERLACED) {
+ irq_ctl |= DC3_IRQFILT_INTL_EN;
+
+ if ((mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_ADDRESS)
+ irq_ctl |= DC3_IRQFILT_INTL_ADDR;
+ else if ((mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_FLICKER) {
+ genlk_ctl |= DC3_GC_FLICKER_FILTER_1_8 |
+ DC3_GC_FLICKER_FILTER_ENABLE | DC3_GC_ALPHA_FLICK_ENABLE;
+ }
+ }
+
+ WRITE_REG32(DC3_GFX_SCALE, (vscale << 16) | (hscale & 0xFFFF));
+ WRITE_REG32(DC3_IRQ_FILT_CTL, irq_ctl);
+ WRITE_REG32(DC3_GENLK_CTL, genlk_ctl);
+
+ /* SET LINE SIZE AND PITCH
+ * The line size and pitch are calculated from the src_width parameter
+ * passed in to this routine. All other parameters are ignored.
+ * The pitch is set either to a power of 2 to allow efficient
+ * compression or to a linear value to allow efficient memory management.
+ */
+
+ switch (bpp) {
+ case 8:
+ size = mode_params->src_width;
+ line_size = starting_width;
+ break;
+
+ case 12:
+ case 15:
+ case 16:
+
+ size = mode_params->src_width << 1;
+ line_size = starting_width << 1;
+ break;
+
+ case 24:
+ case 32:
+ default:
+
+ size = mode_params->src_width << 2;
+ line_size = starting_width << 2;
+ break;
+ }
+
+ /* CALCULATE DV RAM SETTINGS AND POWER OF 2 PITCH */
+
+ pitch = 1024;
+ dv_size = DC3_DV_LINE_SIZE_1024;
+
+ if (size > 1024) {
+ pitch = 2048;
+ dv_size = DC3_DV_LINE_SIZE_2048;
+ }
+ if (size > 2048) {
+ pitch = 4096;
+ dv_size = DC3_DV_LINE_SIZE_4096;
+ }
+ if (size > 4096) {
+ pitch = 8192;
+ dv_size = DC3_DV_LINE_SIZE_8192;
+ }
+
+ /* OVERRIDE SETTINGS FOR LINEAR PITCH */
+
+ if (mode_params->flags & VG_MODEFLAG_LINEARPITCH) {
+ unsigned long max;
+
+ if (pitch != size) {
+ /* CALCULATE MAXIMUM ADDRESS (1K ALIGNED) */
+
+ max = size * output_height;
+ max = (max + 0x3FF) & 0xFFFFFC00;
+ WRITE_REG32(DC3_DV_TOP, max | DC3_DVTOP_ENABLE);
+
+ gcfg |= DC3_GCFG_FDTY;
+ pitch = size;
+ } else {
+ WRITE_REG32(DC3_DV_TOP, 0);
+ }
+ }
+
+ /* WRITE PITCH AND DV RAM SETTINGS */
+ /* The DV RAM line length is programmed at a power of 2 boundary */
+ /* in case the user wants to toggle back to a power of 2 pitch */
+ /* later. It could happen... */
+
+ temp = READ_REG32(DC3_DV_CTL);
+ WRITE_REG32(DC3_GFX_PITCH, pitch >> 3);
+ WRITE_REG32(DC3_DV_CTL, (temp & ~DC3_DV_LINE_SIZE_MASK) | dv_size);
+
+ /* SET THE LINE SIZE */
+
+ WRITE_REG32(DC3_LINE_SIZE, (line_size + 7) >> 3);
+
+ /* ALWAYS ENABLE VIDEO AND GRAPHICS DATA */
+ /* These bits are relics from a previous design and */
+ /* should always be enabled. */
+
+ dcfg |= (DC3_DCFG_VDEN | DC3_DCFG_GDEN);
+
+ /* SET PIXEL FORMAT */
+
+ dcfg |= bpp_mask;
+
+ /* ENABLE TIMING GENERATOR, TIM. REG. UPDATES, PALETTE BYPASS */
+ /* AND VERT. INT. SELECT */
+
+ dcfg |= (unsigned long)(DC3_DCFG_TGEN | DC3_DCFG_TRUP | DC3_DCFG_PALB |
+ DC3_DCFG_VISL);
+
+ /* SET FIFO PRIORITIES AND DISPLAY FIFO LOAD ENABLE
+ * Note that the bandwidth setting gets upgraded when scaling or flicker
+ * filtering are enabled, as they require more data throughput.
+ */
+
+ msr_read64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &msr_value);
+ msr_value.low &= ~(DC3_SPARE_DISABLE_CFIFO_HGO |
+ DC3_SPARE_VFIFO_ARB_SELECT |
+ DC3_SPARE_LOAD_WM_LPEN_MASK | DC3_SPARE_WM_LPEN_OVRD |
+ DC3_SPARE_DISABLE_INIT_VID_PRI | DC3_SPARE_DISABLE_VFIFO_WM);
+
+ if ((mode_params->flags & VG_MODEFLAG_BANDWIDTHMASK) ==
+ VG_MODEFLAG_HIGH_BAND
+ || ((mode_params->flags & VG_MODEFLAG_INTERLACED)
+ && (mode_params->flags & VG_MODEFLAG_INT_MASK) ==
+ VG_MODEFLAG_INT_FLICKER) || (irq_ctl & DC3_IRQFILT_GFX_FILT_EN)) {
+ /* HIGH BANDWIDTH */
+ /* Set agressive watermarks and disallow forced low priority */
+
+ gcfg |= 0x0000BA01;
+ dcfg |= 0x000EA000;
+ acfg = 0x001A0201;
+
+ msr_value.low |= DC3_SPARE_DISABLE_CFIFO_HGO |
+ DC3_SPARE_VFIFO_ARB_SELECT | DC3_SPARE_WM_LPEN_OVRD;
+ } else if ((mode_params->flags & VG_MODEFLAG_BANDWIDTHMASK) ==
+ VG_MODEFLAG_AVG_BAND) {
+ /* AVERAGE BANDWIDTH
+ * Set average watermarks and allow small regions of forced low
+ * priority.
+ */
+
+ gcfg |= 0x0000B601;
+ dcfg |= 0x00009000;
+ acfg = 0x00160001;
+
+ msr_value.low |= DC3_SPARE_DISABLE_CFIFO_HGO |
+ DC3_SPARE_VFIFO_ARB_SELECT | DC3_SPARE_WM_LPEN_OVRD;
+
+ /* SET THE NUMBER OF LOW PRIORITY LINES TO 1/2 THE TOTAL AVAILABLE */
+
+ temp = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0x7FF) + 1;
+ temp -= (READ_REG32(DC3_V_SYNC_TIMING) & 0x7FF) + 1;
+ temp >>= 1;
+ if (temp > 127)
+ temp = 127;
+
+ acfg |= temp << 9;
+ } else if ((mode_params->flags & VG_MODEFLAG_BANDWIDTHMASK) ==
+ VG_MODEFLAG_LOW_BAND) {
+ /* LOW BANDWIDTH
+ * Set low watermarks and allow larger regions of forced low priority
+ */
+
+ gcfg |= 0x00009501;
+ dcfg |= 0x00008000;
+ acfg = 0x00150001;
+
+ msr_value.low |= DC3_SPARE_DISABLE_CFIFO_HGO |
+ DC3_SPARE_VFIFO_ARB_SELECT | DC3_SPARE_WM_LPEN_OVRD;
+
+ /* SET THE NUMBER OF LOW PRIORITY LINES TO 3/4 THE TOTAL AVAILABLE */
+
+ temp = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0x7FF) + 1;
+ temp -= (READ_REG32(DC3_V_SYNC_TIMING) & 0x7FF) + 1;
+ temp = (temp * 3) >> 2;
+ if (temp > 127)
+ temp = 127;
+
+ acfg |= temp << 9;
+ } else {
+ /* LEGACY CHARACTERISTICS */
+ /* Arbitration from a single set of watermarks. */
+
+ gcfg |= 0x0000B601;
+ msr_value.low |= DC3_SPARE_DISABLE_VFIFO_WM |
+ DC3_SPARE_DISABLE_INIT_VID_PRI;
+ acfg = 0;
+ }
+
+ msr_write64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &msr_value);
+
+ /* ENABLE FLAT PANEL CENTERING */
+ /* For panel modes having a resolution smaller than the */
+ /* panel resolution, turn on data centering. */
+
+ if (mode_params->flags & VG_MODEFLAG_CENTERED)
+ dcfg |= DC3_DCFG_DCEN;
+
+ /* COMBINE AND SET TIMING VALUES */
+
+ temp = (mode_params->hactive - 1) | ((mode_params->htotal - 1) << 16);
+ WRITE_REG32(DC3_H_ACTIVE_TIMING, temp);
+ temp = (mode_params->hblankstart - 1) |
+ ((mode_params->hblankend - 1) << 16);
+ WRITE_REG32(DC3_H_BLANK_TIMING, temp);
+ temp = (mode_params->hsyncstart - 1) |
+ ((mode_params->hsyncend - 1) << 16);
+ WRITE_REG32(DC3_H_SYNC_TIMING, temp);
+ temp = (mode_params->vactive - 1) | ((mode_params->vtotal - 1) << 16);
+ WRITE_REG32(DC3_V_ACTIVE_TIMING, temp);
+ temp = (mode_params->vblankstart - 1) |
+ ((mode_params->vblankend - 1) << 16);
+ WRITE_REG32(DC3_V_BLANK_TIMING, temp);
+ temp = (mode_params->vsyncstart - 1) |
+ ((mode_params->vsyncend - 1) << 16);
+ WRITE_REG32(DC3_V_SYNC_TIMING, temp);
+ temp = (mode_params->vactive_even - 1) | ((mode_params->vtotal_even -
+ 1) << 16);
+ WRITE_REG32(DC3_V_ACTIVE_EVEN, temp);
+ temp = (mode_params->vblankstart_even - 1) |
+ ((mode_params->vblankend_even - 1) << 16);
+ WRITE_REG32(DC3_V_BLANK_EVEN, temp);
+ temp = (mode_params->vsyncstart_even - 1) |
+ ((mode_params->vsyncend_even - 1) << 16);
+ WRITE_REG32(DC3_V_SYNC_EVEN, temp);
+
+ /* SET THE VIDEO REQUEST REGISTER */
+
+ WRITE_VID32(DF_VIDEO_REQUEST, 0);
+
+ /* SET SOURCE DIMENSIONS */
+
+ WRITE_REG32(DC3_FB_ACTIVE, ((starting_width - 1) << 16) |
+ (starting_height - 1));
+
+ /* SET SYNC POLARITIES */
+
+ temp = READ_VID32(DF_DISPLAY_CONFIG);
+
+ temp &= ~(DF_DCFG_CRT_SYNC_SKW_MASK | DF_DCFG_PWR_SEQ_DLY_MASK |
+ DF_DCFG_CRT_HSYNC_POL | DF_DCFG_CRT_VSYNC_POL);
+
+ temp |= (DF_DCFG_CRT_SYNC_SKW_INIT |
+ DF_DCFG_PWR_SEQ_DLY_INIT | DF_DCFG_GV_PAL_BYP);
+
+ if (mode_params->flags & VG_MODEFLAG_NEG_HSYNC)
+ temp |= DF_DCFG_CRT_HSYNC_POL;
+ if (mode_params->flags & VG_MODEFLAG_NEG_VSYNC)
+ temp |= DF_DCFG_CRT_VSYNC_POL;
+
+ WRITE_VID32(DF_DISPLAY_CONFIG, temp);
+
+ WRITE_REG32(DC3_DISPLAY_CFG, dcfg);
+ WRITE_REG32(DC3_ARB_CFG, acfg);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+
+ /* RESTORE VALUE OF DC3_UNLOCK */
+
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_bpp
+ *
+ * This routine changes the display BPP on the fly. It is intended only to
+ * switch between pixel depths of the same pixel size 24<->32 or 15<->16, NOT
+ * between pixel depths of differing sizes 16<->32
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_bpp(int bpp)
+{
+ unsigned long unlock, dcfg, bpp_mask;
+
+ switch (bpp) {
+ case 8:
+ bpp_mask = DC3_DCFG_DISP_MODE_8BPP;
+ break;
+ case 24:
+ bpp_mask = DC3_DCFG_DISP_MODE_24BPP;
+ break;
+ case 32:
+ bpp_mask = DC3_DCFG_DISP_MODE_32BPP;
+ break;
+ case 12:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_12BPP;
+ break;
+ case 15:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_15BPP;
+ break;
+ case 16:
+ bpp_mask = DC3_DCFG_DISP_MODE_16BPP | DC3_DCFG_16BPP;
+ break;
+ default:
+ return CIM_STATUS_INVALIDPARAMS;
+ }
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ dcfg = READ_REG32(DC3_DISPLAY_CFG) & ~(DC3_DCFG_DISP_MODE_MASK |
+ DC3_DCFG_16BPP_MODE_MASK);
+ dcfg |= bpp_mask;
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_DISPLAY_CFG, dcfg);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_display_mode_index
+ *
+ * This routine searches the Cimarron mode table for a mode that matches the
+ * input parameters. If a match is found, the return value is the index into
+ * the mode table. If no match is found, the return value is -1.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_display_mode_index(VG_QUERY_MODE * query)
+{
+ unsigned int mode;
+ unsigned long hz_flag = 0xFFFFFFFF;
+ unsigned long bpp_flag = 0xFFFFFFFF;
+ unsigned long enc_flag = 0xFFFFFFFF;
+ unsigned long tv_flag = 0;
+ unsigned long interlaced = 0;
+ unsigned long halfclock = 0;
+ long minimum = 0x7FFFFFFF;
+ long diff;
+ int match = -1;
+
+ if (!query || !query->query_flags)
+ return -1;
+
+ if (query->query_flags & VG_QUERYFLAG_REFRESH) {
+ /* SET FLAGS TO MATCH REFRESH RATE */
+
+ if (query->hz == 56)
+ hz_flag = VG_SUPPORTFLAG_56HZ;
+ else if (query->hz == 60)
+ hz_flag = VG_SUPPORTFLAG_60HZ;
+ else if (query->hz == 70)
+ hz_flag = VG_SUPPORTFLAG_70HZ;
+ else if (query->hz == 72)
+ hz_flag = VG_SUPPORTFLAG_72HZ;
+ else if (query->hz == 75)
+ hz_flag = VG_SUPPORTFLAG_75HZ;
+ else if (query->hz == 85)
+ hz_flag = VG_SUPPORTFLAG_85HZ;
+ else if (query->hz == 90)
+ hz_flag = VG_SUPPORTFLAG_90HZ;
+ else if (query->hz == 100)
+ hz_flag = VG_SUPPORTFLAG_100HZ;
+ else
+ hz_flag = 0;
+ }
+
+ if (query->query_flags & VG_QUERYFLAG_BPP) {
+ /* SET BPP FLAGS TO LIMIT MODE SELECTION */
+
+ if (query->bpp == 8)
+ bpp_flag = VG_SUPPORTFLAG_8BPP;
+ else if (query->bpp == 12)
+ bpp_flag = VG_SUPPORTFLAG_12BPP;
+ else if (query->bpp == 15)
+ bpp_flag = VG_SUPPORTFLAG_15BPP;
+ else if (query->bpp == 16)
+ bpp_flag = VG_SUPPORTFLAG_16BPP;
+ else if (query->bpp == 24)
+ bpp_flag = VG_SUPPORTFLAG_24BPP;
+ else if (query->bpp == 32)
+ bpp_flag = VG_SUPPORTFLAG_32BPP;
+ else
+ bpp_flag = 0;
+ }
+
+ if (query->query_flags & VG_QUERYFLAG_ENCODER) {
+ /* SET ENCODER FLAGS TO LIMIT MODE SELECTION */
+
+ if (query->encoder == VG_ENCODER_ADV7171)
+ enc_flag = VG_SUPPORTFLAG_ADV7171;
+ else if (query->encoder == VG_ENCODER_SAA7127)
+ enc_flag = VG_SUPPORTFLAG_SAA7127;
+ else if (query->encoder == VG_ENCODER_FS454)
+ enc_flag = VG_SUPPORTFLAG_FS454;
+ else if (query->encoder == VG_ENCODER_ADV7300)
+ enc_flag = VG_SUPPORTFLAG_ADV7300;
+ else
+ enc_flag = 0;
+ }
+
+ if (query->query_flags & VG_QUERYFLAG_TVMODE) {
+ /* SET ENCODER FLAGS TO LIMIT MODE SELECTION */
+
+ if (query->tvmode == VG_TVMODE_NTSC)
+ tv_flag = VG_SUPPORTFLAG_NTSC;
+ else if (query->tvmode == VG_TVMODE_PAL)
+ tv_flag = VG_SUPPORTFLAG_PAL;
+ else if (query->tvmode == VG_TVMODE_480P)
+ tv_flag = VG_SUPPORTFLAG_480P;
+ else if (query->tvmode == VG_TVMODE_720P)
+ tv_flag = VG_SUPPORTFLAG_720P;
+ else if (query->tvmode == VG_TVMODE_1080I)
+ tv_flag = VG_SUPPORTFLAG_1080I;
+ else if (query->tvmode == VG_TVMODE_6X4_NTSC)
+ tv_flag = VG_SUPPORTFLAG_6X4_NTSC;
+ else if (query->tvmode == VG_TVMODE_8X6_NTSC)
+ tv_flag = VG_SUPPORTFLAG_8X6_NTSC;
+ else if (query->tvmode == VG_TVMODE_10X7_NTSC)
+ tv_flag = VG_SUPPORTFLAG_10X7_NTSC;
+ else if (query->tvmode == VG_TVMODE_6X4_PAL)
+ tv_flag = VG_SUPPORTFLAG_6X4_PAL;
+ else if (query->tvmode == VG_TVMODE_8X6_PAL)
+ tv_flag = VG_SUPPORTFLAG_8X6_PAL;
+ else if (query->tvmode == VG_TVMODE_10X7_PAL)
+ tv_flag = VG_SUPPORTFLAG_10X7_PAL;
+ else
+ tv_flag = 0xFFFFFFFF;
+ }
+
+ /* SET APPROPRIATE TV AND VOP FLAGS */
+
+ if (query->query_flags & VG_QUERYFLAG_INTERLACED)
+ interlaced = query->interlaced ? VG_MODEFLAG_INTERLACED : 0;
+ if (query->query_flags & VG_QUERYFLAG_HALFCLOCK)
+ halfclock = query->halfclock ? VG_MODEFLAG_HALFCLOCK : 0;
+
+ /* CHECK FOR INVALID REQUEST */
+
+ if (!hz_flag || !bpp_flag || !enc_flag || tv_flag == 0xFFFFFFFF)
+ return -1;
+
+ /* LOOP THROUGH THE AVAILABLE MODES TO FIND A MATCH */
+
+ for (mode = 0; mode < NUM_CIMARRON_DISPLAY_MODES; mode++) {
+ if ((!(query->query_flags & VG_QUERYFLAG_PANEL) ||
+ (CimarronDisplayModes[mode].
+ internal_flags & VG_SUPPORTFLAG_PANEL))
+ && (!(query->query_flags & VG_QUERYFLAG_TVOUT)
+ || (CimarronDisplayModes[mode].
+ internal_flags & VG_SUPPORTFLAG_TVOUT))
+ && (!(query->query_flags & VG_QUERYFLAG_INTERLACED)
+ || (CimarronDisplayModes[mode].
+ flags & VG_MODEFLAG_INTERLACED) == interlaced)
+ && (!(query->query_flags & VG_QUERYFLAG_HALFCLOCK)
+ || (CimarronDisplayModes[mode].
+ flags & VG_MODEFLAG_HALFCLOCK) == halfclock)
+ && (!(query->query_flags & VG_QUERYFLAG_PANELWIDTH)
+ || (CimarronDisplayModes[mode].panel_width ==
+ query->panel_width))
+ && (!(query->query_flags & VG_QUERYFLAG_PANELHEIGHT)
+ || (CimarronDisplayModes[mode].panel_height ==
+ query->panel_height))
+ && (!(query->query_flags & VG_QUERYFLAG_ACTIVEWIDTH)
+ || (CimarronDisplayModes[mode].hactive ==
+ query->active_width))
+ && (!(query->query_flags & VG_QUERYFLAG_ACTIVEHEIGHT)
+ || (CimarronDisplayModes[mode].vactive ==
+ query->active_height))
+ && (!(query->query_flags & VG_QUERYFLAG_TOTALWIDTH)
+ || (CimarronDisplayModes[mode].htotal == query->total_width))
+ && (!(query->query_flags & VG_QUERYFLAG_TOTALHEIGHT)
+ || (CimarronDisplayModes[mode].vtotal == query->total_height))
+ && (!(query->query_flags & VG_QUERYFLAG_BPP)
+ || (CimarronDisplayModes[mode].internal_flags & bpp_flag))
+ && (!(query->query_flags & VG_QUERYFLAG_REFRESH)
+ || (CimarronDisplayModes[mode].internal_flags & hz_flag))
+ && (!(query->query_flags & VG_QUERYFLAG_ENCODER)
+ || (CimarronDisplayModes[mode].internal_flags & enc_flag))
+ && (!(query->query_flags & VG_QUERYFLAG_TVMODE)
+ || ((CimarronDisplayModes[mode].
+ internal_flags & VG_SUPPORTFLAG_TVMODEMASK) ==
+ tv_flag))
+ && (!(query->query_flags & VG_QUERYFLAG_PIXELCLOCK)
+ || (CimarronDisplayModes[mode].frequency ==
+ query->frequency))) {
+ /* ALLOW SEARCHING BASED ON AN APPROXIMATE PIXEL CLOCK */
+
+ if (query->query_flags & VG_QUERYFLAG_PIXELCLOCK_APPROX) {
+ diff = query->frequency -
+ CimarronDisplayModes[mode].frequency;
+ if (diff < 0)
+ diff = -diff;
+
+ if (diff < minimum) {
+ minimum = diff;
+ match = mode;
+ }
+ } else {
+ match = mode;
+ break;
+ }
+ }
+ }
+
+ /* RETURN DISPLAY MODE INDEX */
+
+ return match;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_display_mode_information
+ *
+ * This routine retrieves all information for a display mode contained
+ * within Cimarron's mode tables.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_display_mode_information(unsigned int index, VG_DISPLAY_MODE * vg_mode)
+{
+ if (index > NUM_CIMARRON_DISPLAY_MODES)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ *vg_mode = CimarronDisplayModes[index];
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_display_mode_count
+ *
+ * This routine retrieves the count of all predefined Cimarron modes.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_display_mode_count(void)
+{
+ return NUM_CIMARRON_DISPLAY_MODES;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_current_display_mode
+ *
+ * This routine retrieves the settings for the current display. This includes
+ * any panel settings.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_current_display_mode(VG_DISPLAY_MODE * current_display, int *bpp)
+{
+ Q_WORD msr_value;
+ unsigned long active, blank, sync;
+ unsigned long i, m, n, p;
+ unsigned long genlk, irq, temp;
+ unsigned long flags = 0;
+ unsigned long iflags = 0;
+
+ /* READ THE CURRENT HORIZONTAL DISPLAY TIMINGS */
+
+ active = READ_REG32(DC3_H_ACTIVE_TIMING);
+ blank = READ_REG32(DC3_H_BLANK_TIMING);
+ sync = READ_REG32(DC3_H_SYNC_TIMING);
+
+ current_display->hactive = (active & 0xFFF) + 1;
+ current_display->hblankstart = (blank & 0xFFF) + 1;
+ current_display->hsyncstart = (sync & 0xFFF) + 1;
+
+ current_display->htotal = ((active >> 16) & 0xFFF) + 1;
+ current_display->hblankend = ((blank >> 16) & 0xFFF) + 1;
+ current_display->hsyncend = ((sync >> 16) & 0xFFF) + 1;
+
+ /* READ THE CURRENT VERTICAL DISPLAY TIMINGS */
+
+ active = READ_REG32(DC3_V_ACTIVE_TIMING);
+ blank = READ_REG32(DC3_V_BLANK_TIMING);
+ sync = READ_REG32(DC3_V_SYNC_TIMING);
+
+ current_display->vactive = (active & 0x7FF) + 1;
+ current_display->vblankstart = (blank & 0x7FF) + 1;
+ current_display->vsyncstart = (sync & 0x7FF) + 1;
+
+ current_display->vtotal = ((active >> 16) & 0x7FF) + 1;
+ current_display->vblankend = ((blank >> 16) & 0x7FF) + 1;
+ current_display->vsyncend = ((sync >> 16) & 0x7FF) + 1;
+
+ /* READ THE CURRENT EVEN FIELD VERTICAL DISPLAY TIMINGS */
+
+ active = READ_REG32(DC3_V_ACTIVE_EVEN);
+ blank = READ_REG32(DC3_V_BLANK_EVEN);
+ sync = READ_REG32(DC3_V_SYNC_EVEN);
+
+ current_display->vactive_even = (active & 0x7FF) + 1;
+ current_display->vblankstart_even = (blank & 0x7FF) + 1;
+ current_display->vsyncstart_even = (sync & 0x7FF) + 1;
+
+ current_display->vtotal_even = ((active >> 16) & 0x7FF) + 1;
+ current_display->vblankend_even = ((blank >> 16) & 0x7FF) + 1;
+ current_display->vsyncend_even = ((sync >> 16) & 0x7FF) + 1;
+
+ /* READ THE CURRENT SOURCE DIMENSIONS */
+ /* The DC3_FB_ACTIVE register is only used when scaling is enabled. */
+ /* As the goal of this routine is to return a structure that can be */
+ /* passed to vg_set_custom_mode to exactly recreate the current mode, */
+ /* we must check the status of the scaler/filter. */
+
+ genlk = READ_REG32(DC3_GENLK_CTL);
+ irq = READ_REG32(DC3_IRQ_FILT_CTL);
+ temp = READ_REG32(DC3_FB_ACTIVE);
+
+ current_display->src_height = (temp & 0xFFFF) + 1;
+ current_display->src_width = ((temp >> 16) & 0xFFF8) + 8;
+
+ /* READ THE CURRENT PANEL CONFIGURATION */
+ /* We can only infer some of the panel settings based on hardware */
+ /* (like when panning). We will instead assume that the current */
+ /* mode was set using Cimarron and use the panel variables inside */
+ /* Cimarron when returning the current mode information. */
+
+ if (vg3_panel_enable) {
+ Q_WORD msr_value;
+
+ flags |= VG_MODEFLAG_PANELOUT;
+
+ current_display->panel_width = vg3_panel_width;
+ current_display->panel_height = vg3_panel_height;
+ current_display->mode_width = vg3_mode_width;
+ current_display->mode_height = vg3_mode_height;
+
+ if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DCEN)
+ flags |= VG_MODEFLAG_CENTERED;
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL, &msr_value);
+ current_display->panel_tim1 = READ_VID32(DF_VIDEO_PANEL_TIM1);
+ current_display->panel_tim2 = READ_VID32(DF_VIDEO_PANEL_TIM2);
+ current_display->panel_dither_ctl = READ_VID32(DF_DITHER_CONTROL);
+ current_display->panel_pad_sel_low = msr_value.low;
+ current_display->panel_pad_sel_high = msr_value.high;
+ }
+
+ /* SET MISCELLANEOUS MODE FLAGS */
+
+ /* INTERLACED */
+
+ if (irq & DC3_IRQFILT_INTL_EN) {
+ flags |= VG_MODEFLAG_INTERLACED;
+ if (irq & DC3_IRQFILT_INTL_ADDR)
+ flags |= VG_MODEFLAG_INT_ADDRESS;
+ else if (genlk & DC3_GC_FLICKER_FILTER_ENABLE)
+ flags |= VG_MODEFLAG_INT_FLICKER;
+ else
+ flags |= VG_MODEFLAG_INT_LINEDOUBLE;
+ }
+
+ /* POLARITIES */
+
+ temp = READ_VID32(DF_DISPLAY_CONFIG);
+ if (temp & DF_DCFG_CRT_HSYNC_POL)
+ flags |= VG_MODEFLAG_NEG_HSYNC;
+ if (temp & DF_DCFG_CRT_VSYNC_POL)
+ flags |= VG_MODEFLAG_NEG_VSYNC;
+
+ /* BPP */
+
+ temp = READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DISP_MODE_MASK;
+ if (temp == DC3_DCFG_DISP_MODE_8BPP) {
+ iflags |= VG_SUPPORTFLAG_8BPP;
+ *bpp = 8;
+ } else if (temp == DC3_DCFG_DISP_MODE_24BPP) {
+ iflags |= VG_SUPPORTFLAG_24BPP;
+ *bpp = 24;
+ } else if (temp == DC3_DCFG_DISP_MODE_32BPP) {
+ iflags |= VG_SUPPORTFLAG_32BPP;
+ *bpp = 32;
+ } else if (temp == DC3_DCFG_DISP_MODE_16BPP) {
+ temp = READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_16BPP_MODE_MASK;
+ if (temp == DC3_DCFG_16BPP) {
+ iflags |= VG_SUPPORTFLAG_16BPP;
+ *bpp = 16;
+ } else if (temp == DC3_DCFG_15BPP) {
+ iflags |= VG_SUPPORTFLAG_15BPP;
+ *bpp = 15;
+ } else if (temp == DC3_DCFG_12BPP) {
+ iflags |= VG_SUPPORTFLAG_12BPP;
+ *bpp = 12;
+ }
+ }
+
+ /* TV RELATED FLAGS */
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL, &msr_value);
+ if (msr_value.high & DF_INVERT_VOP_CLOCK)
+ flags |= VG_MODEFLAG_TVOUT;
+
+ /* LINEAR PITCH */
+
+ temp = (READ_REG32(DC3_GFX_PITCH) & 0x0000FFFF) << 3;
+ if (temp != 1024 && temp != 2048 && temp != 4096 && temp != 8192)
+ flags |= VG_MODEFLAG_LINEARPITCH;
+
+ /* SIMULTANEOUS CRT/FP */
+
+ msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
+ if (msr_value.low & DF_SIMULTANEOUS_CRT_FP)
+ flags |= VG_MODEFLAG_CRT_AND_FP;
+
+ /* SET PLL-RELATED FLAGS */
+
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+ if (msr_value.high & GLCP_DOTPLL_DIV4)
+ flags |= VG_MODEFLAG_QVGA;
+ if (msr_value.low & GLCP_DOTPLL_HALFPIX)
+ flags |= VG_MODEFLAG_HALFCLOCK;
+
+ /* SAVE THE FLAGS IN THE MODE STRUCTURE */
+
+ current_display->internal_flags = iflags;
+ current_display->flags = flags;
+
+ /* READ PIXEL CLOCK FREQUENCY */
+ /* We first search for an exact match. If none is found, we try */
+ /* a fixed point calculation and return CIM_STATUS_INEXACTMATCH. */
+
+ for (i = 0; i < NUM_CIMARRON_PLL_FREQUENCIES; i++) {
+ if (CimarronPLLFrequencies[i].pll_value == msr_value.high)
+ break;
+ }
+
+ if (i == NUM_CIMARRON_PLL_FREQUENCIES) {
+ /* ATTEMPT 16.16 CALCULATION */
+ /* We assume the input frequency is 48 MHz, which is represented */
+ /* in 16.16 fixed point as 0x300000. The PLL calculation is: */
+ /* n + 1 */
+ /* Fout = 48.000 * -------------- */
+ /* m + 1 * p + 1 */
+
+ p = msr_value.high & 0xF;
+ n = (msr_value.high >> 4) & 0xFF;
+ m = (msr_value.high >> 12) & 0x7;
+ current_display->frequency =
+ (0x300000 * (n + 1)) / ((p + 1) * (m + 1));
+
+ return CIM_STATUS_INEXACTMATCH;
+ }
+
+ current_display->frequency = CimarronPLLFrequencies[i].frequency;
+
+ /* NOW SEARCH FOR AN IDENTICAL MODE */
+ /* This is just to inform the user that an exact match was found. */
+ /* With an exact match, the user can use the refresh rate flag that */
+ /* is returned in the VG_DISPLAY_MODE structure. */
+
+ for (i = 0; i < NUM_CIMARRON_DISPLAY_MODES; i++) {
+ if ((CimarronDisplayModes[i].flags & current_display->flags) &&
+ CimarronDisplayModes[i].frequency ==
+ current_display->frequency &&
+ CimarronDisplayModes[i].hactive == current_display->hactive &&
+ CimarronDisplayModes[i].hblankstart ==
+ current_display->hblankstart
+ && CimarronDisplayModes[i].hsyncstart ==
+ current_display->hsyncstart
+ && CimarronDisplayModes[i].hsyncend ==
+ current_display->hsyncend
+ && CimarronDisplayModes[i].hblankend ==
+ current_display->hblankend
+ && CimarronDisplayModes[i].htotal == current_display->htotal
+ && CimarronDisplayModes[i].vactive == current_display->vactive
+ && CimarronDisplayModes[i].vblankstart ==
+ current_display->vblankstart
+ && CimarronDisplayModes[i].vsyncstart ==
+ current_display->vsyncstart
+ && CimarronDisplayModes[i].vsyncend ==
+ current_display->vsyncend
+ && CimarronDisplayModes[i].vblankend ==
+ current_display->vblankend
+ && CimarronDisplayModes[i].vtotal == current_display->vtotal) {
+ break;
+ }
+ }
+
+ if (i == NUM_CIMARRON_DISPLAY_MODES)
+ return CIM_STATUS_INEXACTMATCH;
+
+ current_display->internal_flags |=
+ (CimarronDisplayModes[i].internal_flags & VG_SUPPORTFLAG_HZMASK);
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_scaler_filter_coefficients
+ *
+ * This routine sets the vertical and horizontal filter coefficients for
+ * graphics scaling. If either of the input arrays is specified as NULL, a
+ * set of default coeffecients will be used.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_scaler_filter_coefficients(long h_taps[][5], long v_taps[][3])
+{
+ unsigned long irqfilt, i;
+ unsigned long temp0, temp1;
+ unsigned long lock;
+
+ /* ENABLE ACCESS TO THE HORIZONTAL COEFFICIENTS */
+
+ irqfilt = READ_REG32(DC3_IRQ_FILT_CTL);
+ irqfilt |= DC3_IRQFILT_H_FILT_SEL;
+
+ /* UNLOCK THE COEFFICIENT REGISTERS */
+
+ lock = READ_REG32(DC3_UNLOCK);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ /* WRITE COEFFICIENTS */
+ /* Coefficient indexes do not auto-increment, so we must */
+ /* write the address for every phase */
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ if (!h_taps) {
+ temp0 = CimarronHorizontalGraphicsFilter[i][0];
+ temp1 = CimarronHorizontalGraphicsFilter[i][1];
+ } else {
+ temp0 = ((unsigned long)h_taps[i][0] & 0x3FF) |
+ (((unsigned long)h_taps[i][1] & 0x3FF) << 10) |
+ (((unsigned long)h_taps[i][2] & 0x3FF) << 20);
+
+ temp1 = ((unsigned long)h_taps[i][3] & 0x3FF) |
+ (((unsigned long)h_taps[i][4] & 0x3FF) << 10);
+ }
+ WRITE_REG32(DC3_FILT_COEFF1, temp0);
+ WRITE_REG32(DC3_FILT_COEFF2, temp1);
+ }
+
+ /* ENABLE ACCESS TO THE VERTICAL COEFFICIENTS */
+
+ irqfilt &= ~DC3_IRQFILT_H_FILT_SEL;
+
+ /* WRITE COEFFICIENTS */
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ if (!v_taps) {
+ temp0 = CimarronVerticalGraphicsFilter[i];
+ } else {
+ temp0 = ((unsigned long)v_taps[i][0] & 0x3FF) |
+ (((unsigned long)v_taps[i][1] & 0x3FF) << 10) |
+ (((unsigned long)v_taps[i][2] & 0x3FF) << 20);
+ }
+
+ WRITE_REG32(DC3_FILT_COEFF1, temp0);
+ }
+
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_configure_flicker_filter
+ *
+ * This routine updates the VG flicker filter settings when in an interlaced
+ * mode. Note that flicker filtering is enabled inside a mode set. This routine
+ * is provided to change from the default flicker filter setting of
+ * 1/4, 1/2, 1/4.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_configure_flicker_filter(unsigned long flicker_strength, int flicker_alpha)
+{
+ unsigned long unlock;
+ unsigned long genlk_ctl;
+
+ /* CHECK FOR VALID FLICKER SETTING */
+
+ if (flicker_strength != VG_FLICKER_FILTER_NONE &&
+ flicker_strength != VG_FLICKER_FILTER_1_16 &&
+ flicker_strength != VG_FLICKER_FILTER_1_8 &&
+ flicker_strength != VG_FLICKER_FILTER_1_4 &&
+ flicker_strength != VG_FLICKER_FILTER_5_16) {
+ return CIM_STATUS_INVALIDPARAMS;
+ }
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ genlk_ctl = READ_REG32(DC3_GENLK_CTL) & ~(DC3_GC_FLICKER_FILTER_MASK |
+ DC3_GC_ALPHA_FLICK_ENABLE);
+ genlk_ctl |= flicker_strength;
+ if (flicker_alpha)
+ genlk_ctl |= DC3_GC_ALPHA_FLICK_ENABLE;
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_GENLK_CTL, genlk_ctl);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_clock_frequency
+ *
+ * This routine sets the frequency of the dot clock. The input to this
+ * routine is a 16.16 fraction. If an exact match is not found, this
+ * routine will program the closest available frequency and return
+ * CIM_STATUS_INEXACTMATCH.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_clock_frequency(unsigned long frequency, unsigned long pll_flags)
+{
+ Q_WORD msr_value;
+ unsigned long timeout;
+ unsigned long index = 0;
+ unsigned long unlock, i;
+ unsigned long pll_high, pll_low;
+ long diff, min = 0;
+
+ /* FIND THE REGISTER VALUES FOR THE DESIRED FREQUENCY */
+ /* Search the table for the closest frequency (16.16 format). */
+ /* This search is skipped if the user is manually specifying */
+ /* the MSR value. */
+
+ pll_low = 0;
+ if (!(pll_flags & VG_PLL_MANUAL)) {
+ min = (long)CimarronPLLFrequencies[0].frequency - (long)frequency;
+ if (min < 0L)
+ min = -min;
+
+ for (i = 1; i < NUM_CIMARRON_PLL_FREQUENCIES; i++) {
+ diff = (long)CimarronPLLFrequencies[i].frequency -
+ (long)frequency;
+ if (diff < 0L)
+ diff = -diff;
+
+ if (diff < min) {
+ min = diff;
+ index = i;
+ }
+ }
+
+ pll_high = CimarronPLLFrequencies[index].pll_value & 0x00007FFF;
+ } else {
+ pll_high = frequency;
+ }
+
+ if (pll_flags & VG_PLL_DIVIDE_BY_2)
+ pll_low |= GLCP_DOTPLL_HALFPIX;
+ if (pll_flags & VG_PLL_DIVIDE_BY_4)
+ pll_high |= GLCP_DOTPLL_DIV4;
+ if (pll_flags & VG_PLL_BYPASS)
+ pll_low |= GLCP_DOTPLL_BYPASS;
+ if (pll_flags & VG_PLL_VIP_CLOCK)
+ pll_high |= GLCP_DOTPLL_VIPCLK;
+
+ /* VERIFY THAT WE ARE NOT WRITING WHAT IS ALREADY IN THE REGISTERS */
+ /* The Dot PLL reset bit is tied to VDD for flat panels. This can */
+ /* cause a brief drop in flat panel power, which can cause serious */
+ /* glitches on some panels. */
+
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+
+ if ((msr_value.low & GLCP_DOTPLL_LOCK) &&
+ ((msr_value.low & (GLCP_DOTPLL_HALFPIX | GLCP_DOTPLL_BYPASS)) ==
+ pll_low) && (msr_value.high == pll_high)) {
+ return CIM_STATUS_OK;
+ }
+
+ /* PROGRAM THE SETTINGS WITH THE RESET BIT SET */
+ /* Clear the bypass bit to ensure that the programmed */
+ /* M, N and P values are being used. */
+
+ msr_value.high = pll_high;
+ msr_value.low &= ~(GLCP_DOTPLL_BYPASS | GLCP_DOTPLL_HALFPIX);
+ msr_value.low |= (pll_low | 0x00000001);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+
+ /* WAIT FOR THE LOCK BIT */
+ /* The PLL spec states that the PLL may take up to 100 us to */
+ /* properly lock. Furthermore, the lock signal is not 100% */
+ /* reliable. To address this, we add a hefty delay followed */
+ /* by a polling loop that times out after a 1000 reads. */
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ for (timeout = 0; timeout < 1280; timeout++)
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ for (timeout = 0; timeout < 1000; timeout++) {
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+ if (msr_value.low & GLCP_DOTPLL_LOCK)
+ break;
+ }
+
+ /* CLEAR THE RESET BIT */
+
+ msr_value.low &= 0xFFFFFFFE;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+
+ /* DID THE PLL SUCCESSFULLY LOCK? */
+
+ if (!(msr_value.low & GLCP_DOTPLL_LOCK))
+ return CIM_STATUS_NOLOCK;
+
+ /* RETURN THE APPROPRIATE CODE */
+
+ if (min == 0)
+ return CIM_STATUS_OK;
+ else
+ return CIM_STATUS_INEXACTMATCH;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_border_color
+ *
+ * This routine sets the color used as the border in centered panel modes.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_border_color(unsigned long border_color)
+{
+ unsigned long lock = READ_REG32(DC3_UNLOCK);
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_PAL_ADDRESS, 0x104);
+ WRITE_REG32(DC3_PAL_DATA, border_color);
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_cursor_enable
+ *
+ * This routine enables or disables the hardware cursor. This routine should
+ * only be called after the hardware cursor has been completely configured.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_cursor_enable(int enable)
+{
+ unsigned long unlock, gcfg;
+
+ /* SET OR CLEAR CURSOR ENABLE BIT */
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ if (enable)
+ gcfg |= DC3_GCFG_CURE;
+ else
+ gcfg &= ~(DC3_GCFG_CURE);
+
+ /* WRITE NEW REGISTER VALUE */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_mono_cursor_colors
+ *
+ * This routine sets the colors of the hardware monochrome cursor.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_mono_cursor_colors(unsigned long bkcolor, unsigned long fgcolor)
+{
+ unsigned long lock = READ_REG32(DC3_UNLOCK);
+
+ /* SET CURSOR COLORS */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_PAL_ADDRESS, 0x100);
+ WRITE_REG32(DC3_PAL_DATA, bkcolor);
+ WRITE_REG32(DC3_PAL_DATA, fgcolor);
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_cursor_position
+ *
+ * This routine sets the position of the hardware cursor. The cursor hotspots
+ * and memory offset must have been specified in an earlier call to
+ * a vg_set_cursor_shape_XX routine. The coordinates passed to this routine
+ * generally specify the focal point of the cursor, NOT the upper left
+ * coordinate of the cursor pattern. However, for operating systems that do
+ * not include a hotspot the input parameters may be negative.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_cursor_position(long xpos, long ypos, VG_PANNING_COORDINATES * panning)
+{
+ unsigned long unlock, memoffset;
+ unsigned long gcfg;
+ long x, xoffset;
+ long y, yoffset;
+
+ memoffset = vg3_cursor_offset;
+ x = xpos - (long)vg3_x_hotspot;
+ y = ypos - (long)vg3_y_hotspot;
+
+ /* HANDLE NEGATIVE COORDINATES */
+ /* This routine supports operating systems that use negative */
+ /* coordinates, instead of positive coordinates with an appropriate */
+ /* hotspot. */
+
+ if (xpos < 0)
+ xpos = 0;
+ if (ypos < 0)
+ ypos = 0;
+
+ if (x < -63)
+ return CIM_STATUS_INVALIDPARAMS;
+ if (y < -63)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ if (vg3_panel_enable) {
+ if ((vg3_mode_width > vg3_panel_width)
+ || (vg3_mode_height > vg3_panel_height)) {
+ vg_pan_desktop(xpos, ypos, panning);
+ x = x - (unsigned short)vg3_delta_x;
+ y = y - (unsigned short)vg3_delta_y;
+ } else {
+ panning->start_x = 0;
+ panning->start_y = 0;
+ panning->start_updated = 0;
+ }
+ }
+
+ /* ADJUST OFFSETS */
+ /* Cursor movement and panning work as follows: The cursor position */
+ /* refers to where the hotspot of the cursor is located. However, for */
+ /* non-zero hotspots, the cursor buffer actually begins before the */
+ /* specified position. */
+
+ if (x < 0) {
+ xoffset = -x;
+ x = 0;
+ } else {
+ xoffset = 0;
+ }
+ if (y < 0) {
+ yoffset = -y;
+ y = 0;
+ } else {
+ yoffset = 0;
+ }
+
+ if (vg3_color_cursor)
+ memoffset += (unsigned long)yoffset *192;
+
+ else
+ memoffset += (unsigned long)yoffset << 4;
+
+ /* SET COLOR CURSOR BIT */
+
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ if (vg3_color_cursor)
+ gcfg |= DC3_GCFG_CLR_CUR;
+ else
+ gcfg &= ~DC3_GCFG_CLR_CUR;
+
+ /* SET CURSOR POSITION */
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_CURS_ST_OFFSET, memoffset);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+ WRITE_REG32(DC3_CURSOR_X, (unsigned long)x |
+ (((unsigned long)xoffset) << 11));
+ WRITE_REG32(DC3_CURSOR_Y, (unsigned long)y |
+ (((unsigned long)yoffset) << 11));
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_mono_cursor_shape32
+ *
+ * This routine loads 32x32 cursor data into the cursor buffer in graphics
+ * memory. The outside of the GeodeLX cursor buffer is padded with
+ * transparency.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_mono_cursor_shape32(unsigned long memoffset, unsigned long *andmask,
+ unsigned long *xormask, unsigned long x_hotspot, unsigned long y_hotspot)
+{
+ int i;
+
+ /* SAVE THE CURSOR OFFSET AND HOTSPOTS */
+ /* These are reused later when updating the cursor position, panning */
+ /* and clipping the cursor pointer. */
+
+ vg3_x_hotspot = x_hotspot;
+ vg3_y_hotspot = y_hotspot;
+ vg3_cursor_offset = memoffset;
+ vg3_color_cursor = 0;
+
+ for (i = 0; i < 32; i++) {
+ /* EVEN QWORDS CONTAIN THE AND MASK */
+
+ WRITE_FB32(memoffset, 0xFFFFFFFF);
+ WRITE_FB32(memoffset + 4, andmask[i]);
+
+ /* ODD QWORDS CONTAIN THE XOR MASK */
+
+ WRITE_FB32(memoffset + 8, 0x00000000);
+ WRITE_FB32(memoffset + 12, xormask[i]);
+
+ memoffset += 16;
+ }
+
+ /* FILL THE LOWER HALF OF THE BUFFER WITH TRANSPARENT PIXELS */
+
+ for (i = 0; i < 32; i++) {
+ WRITE_FB32(memoffset, 0xFFFFFFFF);
+ WRITE_FB32(memoffset + 4, 0xFFFFFFFF);
+ WRITE_FB32(memoffset + 8, 0x00000000);
+ WRITE_FB32(memoffset + 12, 0x00000000);
+
+ memoffset += 16;
+ }
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_mono_cursor_shape64
+ *
+ * This routine loads 64x64 cursor data into the cursor buffer in graphics
+ * memory.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_mono_cursor_shape64(unsigned long memoffset, unsigned long *andmask,
+ unsigned long *xormask, unsigned long x_hotspot, unsigned long y_hotspot)
+{
+ int i;
+
+ /* SAVE THE CURSOR OFFSET AND HOTSPOTS */
+ /* These are reused later when updating the cursor position, panning */
+ /* and clipping the cursor pointer. */
+
+ vg3_x_hotspot = x_hotspot;
+ vg3_y_hotspot = y_hotspot;
+ vg3_cursor_offset = memoffset;
+ vg3_color_cursor = 0;
+
+ for (i = 0; i < 128; i += 2) {
+ /* EVEN QWORDS CONTAIN THE AND MASK */
+ /* We invert the dwords to prevent the calling */
+ /* application from having to think in terms of Qwords. */
+ /* The hardware data order is actually 63:0, or 31:0 of */
+ /* the second dword followed by 31:0 of the first dword. */
+
+ WRITE_FB32(memoffset, andmask[i + 1]);
+ WRITE_FB32(memoffset + 4, andmask[i]);
+
+ /* ODD QWORDS CONTAIN THE XOR MASK */
+
+ WRITE_FB32(memoffset + 8, xormask[i + 1]);
+ WRITE_FB32(memoffset + 12, xormask[i]);
+
+ memoffset += 16;
+ }
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_color_cursor_shape
+ *
+ * This routine loads 8:8:8:8 cursor data into the color cursor buffer.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_color_cursor_shape(unsigned long memoffset, unsigned char *data,
+ unsigned long width, unsigned long height, long pitch,
+ unsigned long x_hotspot, unsigned long y_hotspot)
+{
+ unsigned long y;
+
+ /* SAVE THE CURSOR OFFSET AND HOTSPOTS */
+ /* These are reused later when updating the cursor position, panning */
+ /* and clipping the cursor pointer. */
+
+ vg3_x_hotspot = x_hotspot;
+ vg3_y_hotspot = y_hotspot;
+ vg3_cursor_offset = memoffset;
+ vg3_color_cursor = 1;
+
+ /* WRITE THE CURSOR DATA */
+ /* The outside edges of the color cursor are filled with transparency */
+ /* The cursor buffer dimensions are 48x64. */
+
+ for (y = 0; y < height; y++) {
+ /* WRITE THE ACTIVE AND TRANSPARENT DATA */
+ /* We implement this as a macro in our dedication to squeaking */
+ /* every ounce of performance out of our code... */
+
+ WRITE_FB_STRING32(memoffset, data, width);
+ WRITE_FB_CONSTANT((memoffset + (width << 2)), 0, (48 - width));
+
+ /* INCREMENT PAST THE LINE */
+
+ memoffset += 192;
+ data += pitch;
+ }
+
+ /* WRITE THE EXTRA TRANSPARENT LINES */
+ /* Write the lines in one big bulk setting. */
+
+ WRITE_FB_CONSTANT(memoffset, 0, ((64 - height) * 48));
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_pan_desktop
+ *
+ * This routine sets the correct display offset based on the current cursor
+ * position.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_pan_desktop(unsigned long x, unsigned long y,
+ VG_PANNING_COORDINATES * panning)
+{
+ unsigned long modeShiftPerPixel;
+ unsigned long modeBytesPerScanline;
+ unsigned long startAddress;
+
+ /* TEST FOR NO-WORK */
+
+ if (x >= vg3_delta_x && x < (vg3_panel_width + vg3_delta_x) &&
+ y >= vg3_delta_y && y < (vg3_panel_height + vg3_delta_y)) {
+ panning->start_x = vg3_delta_x;
+ panning->start_y = vg3_delta_y;
+ panning->start_updated = 0;
+ return CIM_STATUS_OK;
+ }
+
+ if (vg3_bpp == 24)
+ modeShiftPerPixel = 2;
+ else
+ modeShiftPerPixel = (vg3_bpp + 7) >> 4;
+
+ modeBytesPerScanline = (READ_REG32(DC3_GFX_PITCH) & 0x0000FFFF) << 3;
+
+ /* ADJUST PANNING VARIABLES WHEN CURSOR EXCEEDS BOUNDARY */
+ /* Test the boundary conditions for each coordinate and update */
+ /* all variables and the starting offset accordingly. */
+
+ if (x < vg3_delta_x)
+ vg3_delta_x = x;
+ else if (x >= (vg3_delta_x + vg3_panel_width))
+ vg3_delta_x = x - vg3_panel_width + 1;
+
+ if (y < vg3_delta_y)
+ vg3_delta_y = y;
+ else if (y >= (vg3_delta_y + vg3_panel_height))
+ vg3_delta_y = y - vg3_panel_height + 1;
+
+ /* CALCULATE THE START OFFSET */
+
+ startAddress = (vg3_delta_x << modeShiftPerPixel) +
+ (vg3_delta_y * modeBytesPerScanline);
+
+ vg_set_display_offset(startAddress);
+
+ panning->start_updated = 1;
+ panning->start_x = vg3_delta_x;
+ panning->start_y = vg3_delta_y;
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_display_offset
+ *
+ * This routine sets the start address of the frame buffer. It is
+ * typically used to pan across a virtual desktop (frame buffer larger than
+ * the displayed screen) or to flip the display between multiple buffers.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_offset(unsigned long address)
+{
+ unsigned long lock, gcfg;
+
+ lock = READ_REG32(DC3_UNLOCK);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ /* DISABLE COMPRESSION */
+ /* When setting a non-zero display offset, we must disable display */
+ /* compression. We could maintain a variable and re-enable */
+ /* compression when the offset returns to zero. However, that */
+ /* creates additional complexity for applications that perform */
+ /* graphics animation. Re-enabling compression each time would */
+ /* be tedious and slow for such applications, implying that they */
+ /* would have to disable compression before starting the animation. */
+ /* We will instead disable compression and force the user to */
+ /* re-enable compression when they are ready. */
+
+ if (address != 0) {
+ if (READ_REG32(DC3_GENERAL_CFG) & DC3_GCFG_CMPE) {
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ WRITE_REG32(DC3_GENERAL_CFG,
+ (gcfg & ~(DC3_GCFG_CMPE | DC3_GCFG_DECE)));
+ }
+ }
+
+ WRITE_REG32(DC3_FB_ST_OFFSET, address);
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_display_pitch
+ *
+ * This routine sets the stride between successive lines of data in the frame
+ * buffer.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_pitch(unsigned long pitch)
+{
+ unsigned long temp, dvsize, dvtop, value;
+ unsigned long lock = READ_REG32(DC3_UNLOCK);
+
+ value = READ_REG32(DC3_GFX_PITCH) & 0xFFFF0000;
+ value |= (pitch >> 3);
+
+ /* PROGRAM THE DISPLAY PITCH */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_GFX_PITCH, value);
+
+ /* SET THE COMPRESSION BEHAVIOR BASED ON THE PITCH */
+ /* Strides that are not a power of two will not work with line */
+ /* by line compression. For these cases, we enable full-screen */
+ /* compression. In this mode, any write to the frame buffer */
+ /* region marks the entire frame as dirty. Also, the DV line */
+ /* size must be updated when the pitch is programmed outside of */
+ /* the power of 2 range specified in a mode set. */
+
+ if (pitch > 4096) {
+ dvsize = DC3_DV_LINE_SIZE_8192;
+ } else if (pitch > 2048) {
+ dvsize = DC3_DV_LINE_SIZE_4096;
+ } else if (pitch > 1024) {
+ dvsize = DC3_DV_LINE_SIZE_2048;
+ } else {
+ dvsize = DC3_DV_LINE_SIZE_1024;
+ }
+
+ temp = READ_REG32(DC3_DV_CTL);
+ WRITE_REG32(DC3_DV_CTL,
+ (temp & ~DC3_DV_LINE_SIZE_MASK) | dvsize | 0x00000001);
+
+ value = READ_REG32(DC3_GENERAL_CFG);
+
+ if (pitch == 1024 || pitch == 2048 || pitch == 4096 || pitch == 8192) {
+ value &= ~DC3_GCFG_FDTY;
+ dvtop = 0;
+ } else {
+ value |= DC3_GCFG_FDTY;
+
+ dvtop = (READ_REG32(DC3_FB_ACTIVE) & 0xFFF) + 1;
+ dvtop = ((dvtop * pitch) + 0x3FF) & 0xFFFFFC00;
+ dvtop |= DC3_DVTOP_ENABLE;
+ }
+
+ WRITE_REG32(DC3_GENERAL_CFG, value);
+ WRITE_REG32(DC3_DV_TOP, dvtop);
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_display_palette_entry
+ *
+ * This routine sets a single 8BPP palette entry in the display controller.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_palette_entry(unsigned long index, unsigned long palette)
+{
+ unsigned long dcfg, unlock;
+
+ if (index > 0xFF)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ dcfg = READ_REG32(DC3_DISPLAY_CFG);
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_DISPLAY_CFG, dcfg & ~DC3_DCFG_PALB);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ WRITE_REG32(DC3_PAL_ADDRESS, index);
+ WRITE_REG32(DC3_PAL_DATA, palette);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_display_palette
+ *
+ * This routine sets the entire palette in the display controller.
+ * A pointer is provided to a 256 entry table of 32-bit X:R:G:B values.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_display_palette(unsigned long *palette)
+{
+ unsigned long unlock, dcfg, i;
+
+ WRITE_REG32(DC3_PAL_ADDRESS, 0);
+
+ if (palette) {
+ unlock = READ_REG32(DC3_UNLOCK);
+ dcfg = READ_REG32(DC3_DISPLAY_CFG);
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_DISPLAY_CFG, dcfg & ~DC3_DCFG_PALB);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ for (i = 0; i < 256; i++)
+ WRITE_REG32(DC3_PAL_DATA, palette[i]);
+
+ return CIM_STATUS_OK;
+ }
+ return CIM_STATUS_INVALIDPARAMS;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_set_compression_enable
+ *
+ * This routine enables or disables display compression.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_set_compression_enable(int enable)
+{
+ Q_WORD msr_value;
+ unsigned long unlock, gcfg;
+ unsigned long temp;
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ if (enable) {
+ /* DO NOT ENABLE IF THE DISPLAY OFFSET IS NOT ZERO */
+
+ if (READ_REG32(DC3_FB_ST_OFFSET) & 0x0FFFFFFF)
+ return CIM_STATUS_ERROR;
+
+ /* ENABLE BIT 1 IN THE VG SPARE MSR
+ * The bus can hang when the VG attempts to merge compression writes.
+ * No performance is lost due to the GeodeLink QUACK features in
+ * GeodeLX. We also enable the command word check for a valid
+ * compression header.
+ */
+
+ msr_read64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &msr_value);
+ msr_value.low |= DC3_SPARE_FIRST_REQ_MASK;
+ msr_value.low &= ~DC3_SPARE_DISABLE_CWD_CHECK;
+ msr_write64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &msr_value);
+
+ /* CLEAR DIRTY/VALID BITS IN MEMORY CONTROLLER
+ * We don't want the controller to think that old lines are still
+ * valid. Writing a 1 to bit 0 of the DV Control register will force
+ * the hardware to clear all the valid bits.
+ */
+
+ temp = READ_REG32(DC3_DV_CTL);
+ WRITE_REG32(DC3_DV_CTL, temp | 0x00000001);
+
+ /* ENABLE COMPRESSION BITS */
+
+ gcfg |= DC3_GCFG_CMPE | DC3_GCFG_DECE;
+ } else {
+ gcfg &= ~(DC3_GCFG_CMPE | DC3_GCFG_DECE);
+ }
+
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_configure_compression
+ *
+ * This routine configures all aspects of display compression, including
+ * pitch, size and the offset of the compression buffer.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_configure_compression(VG_COMPRESSION_DATA * comp_data)
+{
+ unsigned long delta, size;
+ unsigned long comp_size, unlock;
+
+ /* CHECK FOR VALID PARAMETERS */
+ /* The maximum size for the compression buffer is 544 bytes (with */
+ /* the header) Also, the pitch cannot be less than the line size */
+ /* and the compression buffer offset must be 16-byte aligned. */
+
+ if (comp_data->size > 544 || comp_data->pitch < comp_data->size ||
+ comp_data->compression_offset & 0x0F) {
+ return CIM_STATUS_INVALIDPARAMS;
+ }
+
+ /* SUBTRACT 32 FROM SIZE */
+ /* The display controller will actually write 4 extra QWords. So, */
+ /* if we assume that "size" refers to the allocated size, we must */
+ /* subtract 32 bytes. */
+
+ comp_size = comp_data->size - 32;
+
+ /* CALCULATE REGISTER VALUES */
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ size = READ_REG32(DC3_LINE_SIZE) & ~DC3_LINE_SIZE_CBLS_MASK;
+ delta = READ_REG32(DC3_GFX_PITCH) & ~DC3_GFX_PITCH_CBP_MASK;
+
+ size |= ((comp_size >> 3) + 1) << DC3_LINE_SIZE_CB_SHIFT;
+ delta |= ((comp_data->pitch >> 3) << 16);
+
+ /* WRITE COMPRESSION PARAMETERS */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_CB_ST_OFFSET, comp_data->compression_offset);
+ WRITE_REG32(DC3_LINE_SIZE, size);
+ WRITE_REG32(DC3_GFX_PITCH, delta);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_test_timing_active
+ *
+ * This routine checks the status of the display timing generator.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_test_timing_active(void)
+{
+ if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN)
+ return 1;
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_test_vertical_active
+ *
+ * This routine checks if the display is currently in the middle of a frame
+ * (not in the VBlank interval)
+ *--------------------------------------------------------------------------*/
+
+int
+vg_test_vertical_active(void)
+{
+ if (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA)
+ return 0;
+
+ return 1;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_wait_vertical_blank
+ *
+ * This routine waits until the beginning of the vertical blank interval.
+ * When the display is already in vertical blank, this routine will wait until
+ * the beginning of the next vertical blank.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_wait_vertical_blank(void)
+{
+ if (vg_test_timing_active()) {
+ while (!vg_test_vertical_active()) ;
+ while (vg_test_vertical_active()) ;
+ }
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_test_even_field
+ *
+ * This routine tests the odd/even status of the current VG output field.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_test_even_field(void)
+{
+ if (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_EVEN_FIELD)
+ return 1;
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_configure_line_interrupt
+ *
+ * This routine configures the display controller's line count interrupt.
+ * This interrupt can be used to interrupt mid-frame or to interrupt at the
+ * beginning of vertical blank.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_configure_line_interrupt(VG_INTERRUPT_PARAMS * interrupt_info)
+{
+ unsigned long irq_line, irq_enable;
+ unsigned long lock;
+
+ irq_line = READ_REG32(DC3_IRQ_FILT_CTL);
+ irq_enable = READ_REG32(DC3_IRQ);
+ lock = READ_REG32(DC3_UNLOCK);
+
+ irq_line = (irq_line & ~DC3_IRQFILT_LINE_MASK) |
+ ((interrupt_info->line << 16) & DC3_IRQFILT_LINE_MASK);
+
+ /* ENABLE OR DISABLE THE INTERRUPT */
+ /* The line count is set before enabling and after disabling to */
+ /* minimize spurious interrupts. The line count is set even */
+ /* when interrupts are disabled to allow polling-based or debug */
+ /* applications. */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ if (interrupt_info->enable) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, irq_line);
+ WRITE_REG32(DC3_IRQ, ((irq_enable & ~DC3_IRQ_MASK) | DC3_IRQ_STATUS));
+ } else {
+ WRITE_REG32(DC3_IRQ, (irq_enable | DC3_IRQ_MASK));
+ WRITE_REG32(DC3_IRQ_FILT_CTL, irq_line);
+ }
+ WRITE_REG32(DC3_UNLOCK, lock);
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_test_and_clear_interrupt
+ *
+ * This routine resets any pending interrupt in the video generator. The
+ * return value indicates the interrupt status prior to the reset.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_test_and_clear_interrupt(void)
+{
+ unsigned long irq_enable;
+ unsigned long lock;
+
+ irq_enable = READ_REG32(DC3_IRQ);
+ lock = READ_REG32(DC3_UNLOCK);
+
+ /* NO ACTION IF INTERRUPTS ARE MASKED */
+ /* We are assuming that a driver or application will not want to receive */
+ /* the status of the interrupt when it is masked. */
+
+ if ((irq_enable & (DC3_IRQ_MASK | DC3_VSYNC_IRQ_MASK)) ==
+ (DC3_IRQ_MASK | DC3_VSYNC_IRQ_MASK))
+ return 0;
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_IRQ, irq_enable);
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return (irq_enable & (DC3_IRQ_STATUS | DC3_VSYNC_IRQ_STATUS));
+}
+
+/*---------------------------------------------------------------------------
+ * vg_test_flip_status
+ *
+ * This routine tests if a new display offset has been latched.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_test_flip_status(void)
+{
+ return (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_FLIP);
+}
+
+/*---------------------------------------------------------------------------
+ * vg_save_state
+ *
+ * This routine saves all persistent VG state information.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_save_state(VG_SAVE_RESTORE * vg_state)
+{
+ Q_WORD msr_value;
+ unsigned long irqfilt;
+ unsigned long offset, i;
+ unsigned long lock;
+
+ /* READ ALL CURRENT REGISTER SETTINGS */
+
+ vg_state->unlock = READ_REG32(DC3_UNLOCK);
+ vg_state->gcfg = READ_REG32(DC3_GENERAL_CFG);
+ vg_state->dcfg = READ_REG32(DC3_DISPLAY_CFG);
+ vg_state->arb_cfg = READ_REG32(DC3_ARB_CFG);
+ vg_state->fb_offset = READ_REG32(DC3_FB_ST_OFFSET);
+ vg_state->cb_offset = READ_REG32(DC3_CB_ST_OFFSET);
+ vg_state->cursor_offset = READ_REG32(DC3_CURS_ST_OFFSET);
+ vg_state->video_y_offset = READ_REG32(DC3_VID_Y_ST_OFFSET);
+ vg_state->video_u_offset = READ_REG32(DC3_VID_U_ST_OFFSET);
+ vg_state->video_v_offset = READ_REG32(DC3_VID_V_ST_OFFSET);
+ vg_state->dv_top = READ_REG32(DC3_DV_TOP);
+ vg_state->line_size = READ_REG32(DC3_LINE_SIZE);
+ vg_state->gfx_pitch = READ_REG32(DC3_GFX_PITCH);
+ vg_state->video_yuv_pitch = READ_REG32(DC3_VID_YUV_PITCH);
+ vg_state->h_active = READ_REG32(DC3_H_ACTIVE_TIMING);
+ vg_state->h_blank = READ_REG32(DC3_H_BLANK_TIMING);
+ vg_state->h_sync = READ_REG32(DC3_H_SYNC_TIMING);
+ vg_state->v_active = READ_REG32(DC3_V_ACTIVE_TIMING);
+ vg_state->v_blank = READ_REG32(DC3_V_BLANK_TIMING);
+ vg_state->v_sync = READ_REG32(DC3_V_SYNC_TIMING);
+ vg_state->fb_active = READ_REG32(DC3_FB_ACTIVE);
+ vg_state->cursor_x = READ_REG32(DC3_CURSOR_X);
+ vg_state->cursor_y = READ_REG32(DC3_CURSOR_Y);
+ vg_state->vid_ds_delta = READ_REG32(DC3_VID_DS_DELTA);
+ vg_state->fb_base = READ_REG32(DC3_PHY_MEM_OFFSET);
+ vg_state->dv_ctl = READ_REG32(DC3_DV_CTL);
+ vg_state->gfx_scale = READ_REG32(DC3_GFX_SCALE);
+ vg_state->irq_ctl = READ_REG32(DC3_IRQ_FILT_CTL);
+ vg_state->vbi_even_ctl = READ_REG32(DC3_VBI_EVEN_CTL);
+ vg_state->vbi_odd_ctl = READ_REG32(DC3_VBI_ODD_CTL);
+ vg_state->vbi_hor_ctl = READ_REG32(DC3_VBI_HOR);
+ vg_state->vbi_odd_line_enable = READ_REG32(DC3_VBI_LN_ODD);
+ vg_state->vbi_even_line_enable = READ_REG32(DC3_VBI_LN_EVEN);
+ vg_state->vbi_pitch = READ_REG32(DC3_VBI_PITCH);
+ vg_state->color_key = READ_REG32(DC3_COLOR_KEY);
+ vg_state->color_key_mask = READ_REG32(DC3_COLOR_MASK);
+ vg_state->color_key_x = READ_REG32(DC3_CLR_KEY_X);
+ vg_state->color_key_y = READ_REG32(DC3_CLR_KEY_Y);
+ vg_state->irq = READ_REG32(DC3_IRQ);
+ vg_state->genlk_ctl = READ_REG32(DC3_GENLK_CTL);
+ vg_state->vid_y_even_offset = READ_REG32(DC3_VID_EVEN_Y_ST_OFFSET);
+ vg_state->vid_u_even_offset = READ_REG32(DC3_VID_EVEN_U_ST_OFFSET);
+ vg_state->vid_v_even_offset = READ_REG32(DC3_VID_EVEN_V_ST_OFFSET);
+ vg_state->vactive_even = READ_REG32(DC3_V_ACTIVE_EVEN);
+ vg_state->vblank_even = READ_REG32(DC3_V_BLANK_EVEN);
+ vg_state->vsync_even = READ_REG32(DC3_V_SYNC_EVEN);
+
+ /* READ THE CURRENT PALETTE */
+
+ lock = READ_REG32(DC3_UNLOCK);
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_PAL_ADDRESS, 0);
+ for (i = 0; i < 261; i++)
+ vg_state->palette[i] = READ_REG32(DC3_PAL_DATA);
+
+ /* READ THE CURRENT FILTER COEFFICIENTS */
+
+ /* ENABLE ACCESS TO THE HORIZONTAL COEFFICIENTS */
+
+ irqfilt = READ_REG32(DC3_IRQ_FILT_CTL);
+ irqfilt |= DC3_IRQFILT_H_FILT_SEL;
+
+ /* READ HORIZONTAL COEFFICIENTS */
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ vg_state->h_coeff[(i << 1)] = READ_REG32(DC3_FILT_COEFF1);
+ vg_state->h_coeff[(i << 1) + 1] = READ_REG32(DC3_FILT_COEFF2);
+ }
+
+ /* ENABLE ACCESS TO THE VERTICAL COEFFICIENTS */
+
+ irqfilt &= ~DC3_IRQFILT_H_FILT_SEL;
+
+ /* READ COEFFICIENTS */
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ vg_state->v_coeff[i] = READ_REG32(DC3_FILT_COEFF1);
+ }
+
+ /* READ THE CURSOR DATA */
+
+ offset = READ_REG32(DC3_CURS_ST_OFFSET) & 0x0FFFFFFF;
+ for (i = 0; i < 3072; i++)
+ vg_state->cursor_data[i] = READ_FB32(offset + (i << 2));
+
+ /* READ THE CURRENT PLL */
+
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_DOTPLL, &msr_value);
+
+ vg_state->pll_flags = 0;
+ for (i = 0; i < NUM_CIMARRON_PLL_FREQUENCIES; i++) {
+ if (CimarronPLLFrequencies[i].pll_value == (msr_value.high & 0x7FFF)) {
+ vg_state->dot_pll = CimarronPLLFrequencies[i].frequency;
+ break;
+ }
+ }
+
+ if (i == NUM_CIMARRON_PLL_FREQUENCIES) {
+ /* NO MATCH */
+ /* Enter the frequency as a manual frequency. */
+
+ vg_state->dot_pll = msr_value.high;
+ vg_state->pll_flags |= VG_PLL_MANUAL;
+ }
+ if (msr_value.low & GLCP_DOTPLL_HALFPIX)
+ vg_state->pll_flags |= VG_PLL_DIVIDE_BY_2;
+ if (msr_value.low & GLCP_DOTPLL_BYPASS)
+ vg_state->pll_flags |= VG_PLL_BYPASS;
+ if (msr_value.high & GLCP_DOTPLL_DIV4)
+ vg_state->pll_flags |= VG_PLL_DIVIDE_BY_4;
+ if (msr_value.high & GLCP_DOTPLL_VIPCLK)
+ vg_state->pll_flags |= VG_PLL_VIP_CLOCK;
+
+ /* READ ALL VG MSRS */
+
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_CAP,
+ &(vg_state->msr_cap));
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_CONFIG,
+ &(vg_state->msr_config));
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_SMI,
+ &(vg_state->msr_smi));
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_ERROR,
+ &(vg_state->msr_error));
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_PM, &(vg_state->msr_pm));
+ msr_read64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_DIAG,
+ &(vg_state->msr_diag));
+ msr_read64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &(vg_state->msr_spare));
+ msr_read64(MSR_DEVICE_GEODELX_VG, DC3_RAM_CTL, &(vg_state->msr_ram_ctl));
+
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_restore_state
+ *
+ * This routine restores all persistent VG state information.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_restore_state(VG_SAVE_RESTORE * vg_state)
+{
+ unsigned long irqfilt, i;
+ unsigned long memoffset;
+
+ /* TEMPORARILY UNLOCK ALL REGISTERS */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ /* RESTORE THE FRAME BUFFER OFFSET */
+
+ WRITE_REG32(DC3_PHY_MEM_OFFSET, vg_state->fb_base);
+
+ /* BLANK GCFG AND DCFG */
+
+ WRITE_REG32(DC3_GENERAL_CFG, 0);
+ WRITE_REG32(DC3_DISPLAY_CFG, 0);
+
+ /* RESTORE ALL REGISTERS */
+
+ WRITE_REG32(DC3_ARB_CFG, vg_state->arb_cfg);
+ WRITE_REG32(DC3_FB_ST_OFFSET, vg_state->fb_offset);
+ WRITE_REG32(DC3_CB_ST_OFFSET, vg_state->cb_offset);
+ WRITE_REG32(DC3_CURS_ST_OFFSET, vg_state->cursor_offset);
+ WRITE_REG32(DC3_VID_Y_ST_OFFSET, vg_state->video_y_offset);
+ WRITE_REG32(DC3_VID_U_ST_OFFSET, vg_state->video_u_offset);
+ WRITE_REG32(DC3_VID_V_ST_OFFSET, vg_state->video_v_offset);
+ WRITE_REG32(DC3_DV_TOP, vg_state->dv_top);
+ WRITE_REG32(DC3_LINE_SIZE, vg_state->line_size);
+ WRITE_REG32(DC3_GFX_PITCH, vg_state->gfx_pitch);
+ WRITE_REG32(DC3_VID_YUV_PITCH, vg_state->video_yuv_pitch);
+ WRITE_REG32(DC3_H_ACTIVE_TIMING, vg_state->h_active);
+ WRITE_REG32(DC3_H_BLANK_TIMING, vg_state->h_blank);
+ WRITE_REG32(DC3_H_SYNC_TIMING, vg_state->h_sync);
+ WRITE_REG32(DC3_V_ACTIVE_TIMING, vg_state->v_active);
+ WRITE_REG32(DC3_V_BLANK_TIMING, vg_state->v_blank);
+ WRITE_REG32(DC3_V_SYNC_TIMING, vg_state->v_sync);
+ WRITE_REG32(DC3_FB_ACTIVE, vg_state->fb_active);
+ WRITE_REG32(DC3_CURSOR_X, vg_state->cursor_x);
+ WRITE_REG32(DC3_CURSOR_Y, vg_state->cursor_y);
+ WRITE_REG32(DC3_VID_DS_DELTA, vg_state->vid_ds_delta);
+ WRITE_REG32(DC3_PHY_MEM_OFFSET, vg_state->fb_base);
+ WRITE_REG32(DC3_DV_CTL, vg_state->dv_ctl | 0x00000001);
+ WRITE_REG32(DC3_GFX_SCALE, vg_state->gfx_scale);
+ WRITE_REG32(DC3_IRQ_FILT_CTL, vg_state->irq_ctl);
+ WRITE_REG32(DC3_VBI_EVEN_CTL, vg_state->vbi_even_ctl);
+ WRITE_REG32(DC3_VBI_ODD_CTL, vg_state->vbi_odd_ctl);
+ WRITE_REG32(DC3_VBI_HOR, vg_state->vbi_hor_ctl);
+ WRITE_REG32(DC3_VBI_LN_ODD, vg_state->vbi_odd_line_enable);
+ WRITE_REG32(DC3_VBI_LN_EVEN, vg_state->vbi_even_line_enable);
+ WRITE_REG32(DC3_VBI_PITCH, vg_state->vbi_pitch);
+ WRITE_REG32(DC3_COLOR_KEY, vg_state->color_key);
+ WRITE_REG32(DC3_COLOR_MASK, vg_state->color_key_mask);
+ WRITE_REG32(DC3_CLR_KEY_X, vg_state->color_key_x);
+ WRITE_REG32(DC3_CLR_KEY_Y, vg_state->color_key_y);
+ WRITE_REG32(DC3_IRQ, vg_state->irq);
+ WRITE_REG32(DC3_GENLK_CTL, vg_state->genlk_ctl);
+ WRITE_REG32(DC3_VID_EVEN_Y_ST_OFFSET, vg_state->vid_y_even_offset);
+ WRITE_REG32(DC3_VID_EVEN_U_ST_OFFSET, vg_state->vid_u_even_offset);
+ WRITE_REG32(DC3_VID_EVEN_V_ST_OFFSET, vg_state->vid_v_even_offset);
+ WRITE_REG32(DC3_V_ACTIVE_EVEN, vg_state->vactive_even);
+ WRITE_REG32(DC3_V_BLANK_EVEN, vg_state->vblank_even);
+ WRITE_REG32(DC3_V_SYNC_EVEN, vg_state->vsync_even);
+
+ /* RESTORE THE PALETTE */
+
+ WRITE_REG32(DC3_PAL_ADDRESS, 0);
+ for (i = 0; i < 261; i++)
+ WRITE_REG32(DC3_PAL_DATA, vg_state->palette[i]);
+
+ /* RESTORE THE HORIZONTAL FILTER COEFFICIENTS */
+
+ irqfilt = READ_REG32(DC3_IRQ_FILT_CTL);
+ irqfilt |= DC3_IRQFILT_H_FILT_SEL;
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+ WRITE_REG32(DC3_FILT_COEFF1, vg_state->h_coeff[(i << 1)]);
+ WRITE_REG32(DC3_FILT_COEFF2, vg_state->h_coeff[(i << 1) + 1]);
+ }
+
+ /* RESTORE VERTICAL COEFFICIENTS */
+
+ irqfilt &= ~DC3_IRQFILT_H_FILT_SEL;
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+ WRITE_REG32(DC3_FILT_COEFF1, vg_state->v_coeff[i]);
+ }
+
+ /* RESTORE THE CURSOR DATA */
+
+ memoffset = READ_REG32(DC3_CURS_ST_OFFSET) & 0x0FFFFFFF;
+ WRITE_FB_STRING32(memoffset, (unsigned char *)&(vg_state->cursor_data[0]),
+ 3072);
+
+ /* RESTORE THE PLL */
+ /* Use a common routine to use common code to poll for lock bit */
+
+ vg_set_clock_frequency(vg_state->dot_pll, vg_state->pll_flags);
+
+ /* RESTORE ALL VG MSRS */
+
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_CAP,
+ &(vg_state->msr_cap));
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_CONFIG,
+ &(vg_state->msr_config));
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_SMI,
+ &(vg_state->msr_smi));
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_ERROR,
+ &(vg_state->msr_error));
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_PM, &(vg_state->msr_pm));
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_DIAG,
+ &(vg_state->msr_diag));
+ msr_write64(MSR_DEVICE_GEODELX_VG, DC3_SPARE_MSR, &(vg_state->msr_spare));
+ msr_write64(MSR_DEVICE_GEODELX_VG, DC3_RAM_CTL, &(vg_state->msr_ram_ctl));
+
+ /* NOW RESTORE GCFG AND DCFG */
+
+ WRITE_REG32(DC3_DISPLAY_CFG, vg_state->dcfg);
+ WRITE_REG32(DC3_GENERAL_CFG, vg_state->gcfg);
+
+ /* FINALLY RESTORE UNLOCK */
+
+ WRITE_REG32(DC3_UNLOCK, vg_state->unlock);
+
+ return CIM_STATUS_OK;
+}
+
+/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ * CIMARRON VG READ ROUTINES
+ * These routines are included for use in diagnostics or when debugging. They
+ * can be optionally excluded from a project.
+ *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+
+#if CIMARRON_INCLUDE_VG_READ_ROUTINES
+
+/*---------------------------------------------------------------------------
+ * vg_read_graphics_crc
+ *
+ * This routine reads the Cyclic Redundancy Check (CRC) value for the graphics
+ * frame.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_read_graphics_crc(int crc_source)
+{
+ unsigned long gcfg, unlock;
+ unsigned long crc, vbi_even;
+ unsigned long interlaced;
+ unsigned long line, field;
+
+ if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN))
+ return 0xFFFFFFFF;
+
+ unlock = READ_REG32(DC3_UNLOCK);
+ gcfg = READ_REG32(DC3_GENERAL_CFG);
+ vbi_even = READ_REG32(DC3_VBI_EVEN_CTL);
+
+ vbi_even &= ~DC3_VBI_EVEN_ENABLE_CRC;
+
+ gcfg |= DC3_GCFG_SGRE | DC3_GCFG_CRC_MODE;
+ gcfg &= ~(DC3_GCFG_SGFR | DC3_GCFG_SIG_SEL | DC3_GCFG_FILT_SIG_SEL);
+
+ switch (crc_source) {
+ case VG_CRC_SOURCE_PREFILTER_EVEN:
+ case VG_CRC_SOURCE_PREFILTER:
+ gcfg |= DC3_GCFG_SIG_SEL;
+ break;
+ case VG_CRC_SOURCE_PREFLICKER:
+ case VG_CRC_SOURCE_PREFLICKER_EVEN:
+ gcfg |= DC3_GCFG_FILT_SIG_SEL;
+ break;
+ case VG_CRC_SOURCE_POSTFLICKER:
+ case VG_CRC_SOURCE_POSTFLICKER_EVEN: /* NO WORK */
+ break;
+
+ default:
+ return 0xFFFFFFFF;
+ }
+
+ if (crc_source & VG_CRC_SOURCE_EVEN)
+ field = 0;
+ else
+ field = DC3_LNCNT_EVEN_FIELD;
+
+ if ((interlaced = (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN))) {
+ /* WAIT FOR THE BEGINNING OF THE FIELD (LINE 1-5) */
+ /* Note that we wait for the field to be odd when CRCing the even */
+ /* field and vice versa. This is because the CRC will not begin */
+ /* until the following field. */
+
+ do {
+ line = READ_REG32(DC3_LINE_CNT_STATUS);
+ } while ((line & DC3_LNCNT_EVEN_FIELD) != field ||
+ ((line & DC3_LNCNT_V_LINE_CNT) >> 16) < 10 ||
+ ((line & DC3_LNCNT_V_LINE_CNT) >> 16) > 15);
+ } else {
+ /* NON-INTERLACED - EVEN FIELD CRCS ARE INVALID */
+
+ if (crc_source & VG_CRC_SOURCE_EVEN)
+ return 0xFFFFFFFF;
+ }
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_VBI_EVEN_CTL, vbi_even);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg & ~DC3_GCFG_SIGE);
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg | DC3_GCFG_SIGE);
+
+ /* WAIT FOR THE CRC TO BE COMPLETED */
+
+ while (!(READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_SIGC)) ;
+
+ /* READ THE COMPLETED CRC */
+
+ crc = READ_REG32(DC3_PAL_DATA);
+
+ /* RESTORE THE PALETTE SETTINGS */
+
+ gcfg &= ~DC3_GCFG_SGRE;
+ WRITE_REG32(DC3_GENERAL_CFG, gcfg);
+ WRITE_REG32(DC3_UNLOCK, unlock);
+
+ return crc;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_read_window_crc
+ *
+ * This routine reads the Cyclic Redundancy Check (CRC) value for a sub-
+ * section of the frame.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_read_window_crc(int crc_source, unsigned long x, unsigned long y,
+ unsigned long width, unsigned long height)
+{
+ Q_WORD msr_value;
+ unsigned long crc = 0;
+ unsigned long hactive, hblankstart;
+ unsigned long htotal, hblankend;
+ unsigned long line, field;
+ unsigned long diag;
+
+ hactive = ((READ_REG32(DC3_H_ACTIVE_TIMING)) & 0xFFF) + 1;
+ hblankstart = ((READ_REG32(DC3_H_BLANK_TIMING)) & 0xFFF) + 1;
+ htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
+ hblankend = ((READ_REG32(DC3_H_BLANK_TIMING) >> 16) & 0xFFF) + 1;
+
+ /* TIMINGS MUST BE ACTIVE */
+
+ if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN))
+ return 0xFFFFFFFF;
+
+ /* DISABLE GLCP ACTIONS */
+
+ msr_value.low = 0;
+ msr_value.high = 0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DIAGCTL, &msr_value);
+
+ if ((x == 0 && width == 1) || x == 1) {
+ /* SPECIAL CASE FOR X == 0 */
+ /* The comparator output is a clock late in the MCP, so we cannot */
+ /* easily catch the first pixel. If the first pixel is desired, */
+ /* we will insert a special state machine to CRC just the first */
+ /* pixel. */
+
+ /* N2 - DISPE HIGH AND Y == 1 */
+ /* Goto state YState = 2 */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x00000C00;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 2, &msr_value);
+
+ /* M3 - DISPE HIGH AND Y == 0 */
+ /* Goto YState = 1 */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x00000A00;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETM0CTL + 3, &msr_value);
+
+ /* N3 - DISPE LOW */
+ /* Goto YState = 0 */
+
+ msr_value.high = 0x00080000;
+ msr_value.low = 0x00000000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 3, &msr_value);
+
+ /* Y0 -> Y1 (SET M3) */
+
+ msr_value.high = 0x00000000;
+ msr_value.low = 0x0000C000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 18, &msr_value);
+
+ /* Y1 -> Y0 (SET N3) */
+
+ msr_value.low = 0x0000A000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 17, &msr_value);
+
+ /* Y1 -> Y2 (SET N2) */
+
+ msr_value.low = 0x00000A00;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 19, &msr_value);
+
+ /* N5 (XSTATE = 10 && CMP2 <= V. COUNTER <= CMP3) &&DISPE&& Y == 0 */
+ /* CRC into REGB */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x10800B20;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 5, &msr_value);
+
+ /* N6 (XSTATE = 10 && CMP2 <= V. COUNTER <= CMP3) && DISPE&&Y == 1 */
+ /* CRC into REGB */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x10800D20;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 6, &msr_value);
+ }
+
+ /* M4 (XSTATE = 00 AND VSYNC HIGH) */
+ /* Goto state 01 */
+ /* Note: VSync = H3A */
+
+ msr_value.high = 0x00000001;
+ msr_value.low = 0x000000A0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETM0CTL + 4, &msr_value);
+
+ /* N0 (XSTATE = 01 AND VSYNC LOW) */
+ /* Goto state 02 */
+ /* Note: VSync low = H3B */
+
+ msr_value.high = 0x00040000;
+ msr_value.low = 0x000000C0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL, &msr_value);
+
+ /* M5 (XSTATE = 10 AND VSYNC HIGH) */
+ /* Goto state 11 */
+
+ msr_value.high = 0x00000001;
+ msr_value.low = 0x00000120;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETM0CTL + 5, &msr_value);
+
+ /* N1 (XSTATE = 10 and DISPE HIGH) */
+ /* Increment H. Counter */
+ /* Note: DispE = H4 */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x00000120;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 1, &msr_value);
+
+ /* M0 (XSTATE = 10 and H. COUNTER == LIMIT) */
+ /* Clear H. Counter and increment V. Counter */
+
+ msr_value.high = 0x00000000;
+ msr_value.low = 0x00000122;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETM0CTL, &msr_value);
+
+ /* N4 (XSTATE = 10 && CMP0 <= H. COUNTER <= CMP1 && CMP2 <= V. COUNTER
+ * <= CMP3) && DISPE
+ * CRC into REGB
+ */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x10C20120;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 4, &msr_value);
+
+ /* COMPARATOR 0 VALUE */
+ /* We subtract 1 to account for a pipeline delay in the GLCP. */
+ /* When the x coordinate is 0, we must play a special game. */
+ /* If the width is exactly 1, we will set up a state machine */
+ /* to only CRC the first pixel. Otherwise, we will set it */
+ /* as an OR combination of a state that CRCs the first pixel */
+ /* and a state that CRCs 1 clock delayed width (width - 1) */
+
+ msr_value.high = 0;
+ if (x > 1)
+ msr_value.low = (x - 1) & 0xFFFF;
+ else
+ msr_value.low = x;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0, &msr_value);
+
+ /* COMPARATOR 1 VALUE */
+
+ if ((x == 0 || x == 1) && width > 1)
+ msr_value.low += width - 2;
+ else
+ msr_value.low += width - 1;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0 + 2, &msr_value);
+
+ /* COMPARATOR 2 VALUE */
+
+ msr_value.low = y << 16;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0 + 4, &msr_value);
+
+ /* COMPARATOR 3 VALUE */
+
+ msr_value.low += (height - 1) << 16;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0 + 6, &msr_value);
+
+ /* COMPARATOR MASKS */
+ /* Comparators 0 and 1 refer to lower 16 bits of RegB */
+
+ msr_value.low = 0x0000FFFF;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPMASK0, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPMASK0 + 2, &msr_value);
+
+ /* Comparators 2 and 3 refer to upper 16 bits of RegB */
+
+ msr_value.low = 0xFFFF0000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPMASK0 + 4, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPMASK0 + 6, &msr_value);
+
+ /* SET REGB MASK */
+ /* We set the mask such that all all 32 bits of data are CRCed */
+
+ msr_value.low = 0xFFFFFFFF;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGBMASK, &msr_value);
+
+ /* ACTIONS */
+
+ /* STATE 00->01 (SET 4M) */
+
+ msr_value.low = 0x000C0000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 14, &msr_value);
+
+ /* STATE 01->10 (SET 0N) */
+
+ msr_value.low = 0x0000000A;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 15, &msr_value);
+
+ /* STATE 10->11 (SET 5M) */
+
+ msr_value.low = 0x00C00000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 16, &msr_value);
+
+ /* CLEAR REGA WHEN TRANSITIONING TO STATE 10 */
+ /* Do not clear RegB as the initial value must be 0x00000001 */
+
+ msr_value.low = 0x0000000A;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0, &msr_value);
+
+ /* REGISTER ACTION 1
+ * CRC into RegB if cmp0 <= h.counter <= cmp1 && cmp2 <= v. counter <
+ * cmp3 && 7 xstate = 10
+ * Increment h.counter if xstate = 10 and HSync is low.
+ */
+
+ msr_value.low = 0x000A00A0;
+ if (x == 0 && width == 1)
+ msr_value.low = 0x00A000A0;
+ else if (x == 1 && width == 1)
+ msr_value.low = 0x0A0000A0;
+ else if (x == 1 && width > 1)
+ msr_value.low |= 0x0A000000;
+
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 1, &msr_value);
+
+ /* REGISTER ACTION 2 */
+ /* Increment V. Counter in REGA */
+
+ msr_value.low = 0x0000000C;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 2, &msr_value);
+
+ /* SET REGB TO 0x00000001 */
+
+ msr_value.low = 0x00000001;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGB, &msr_value);
+
+ /* SET XSTATE TO 0 */
+
+ msr_value.low = 0x00000000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_XSTATE, &msr_value);
+
+ /* SET YSTATE TO 0 */
+
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_YSTATE, &msr_value);
+
+ /* CLEAR ALL OTHER ACTIONS */
+ /* This prevents side-effects from previous accesses to the GLCP */
+ /* debug logic. */
+
+ msr_value.low = 0x00000000;
+ msr_value.high = 0x00000000;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 3, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 4, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 5, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 6, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 7, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 8, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 9, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 10, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 11, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 12, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 13, &msr_value);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 20, &msr_value);
+
+ /* SET DIAG SETTINGS BASED ON DESIRED CRC */
+
+ if (crc_source == VG_CRC_SOURCE_POSTFLICKER
+ || crc_source == VG_CRC_SOURCE_POSTFLICKER_EVEN) {
+ diag = 0x80808086;
+
+ /* ENABLE HW CLOCK GATING AND SET GLCP CLOCK TO DOT CLOCK */
+
+ msr_value.high = 0;
+ msr_value.low = 5;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, MSR_GEODELINK_PM, &msr_value);
+ msr_value.low = 0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+ msr_value.low = 3;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+
+ /* SET REGA LIMITS */
+ /* Lower counter uses pixels/line */
+ /* Upper counter is 0xFFFF to prevent rollover. */
+
+ msr_value.low = 0xFFFF0000 | (hactive - 1);
+ if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DCEN) {
+ msr_value.low += hblankstart - hactive;
+ msr_value.low += htotal - hblankend;
+ }
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGAVAL, &msr_value);
+
+ /* USE H4 FUNCTION A FOR DISPE AND H4 FUNCTION B FOR NOT DISPE */
+ /* DISPE is bit 34 */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0x20000FF0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 4, &msr_value);
+
+ /* USE H3 FUNCTION A FOR VSYNC AND H3 FUNCTION B FOR NOT VSYNC */
+ /* VSYNC is bit 32. */
+
+ msr_value.high = 0x00000000;
+ msr_value.low = 0x002055AA;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 3, &msr_value);
+ } else if (crc_source == VG_CRC_SOURCE_PREFLICKER
+ || crc_source == VG_CRC_SOURCE_PREFLICKER_EVEN) {
+ diag = 0x801F8032;
+
+ /* ENABLE HW CLOCK GATING AND SET GLCP CLOCK TO GEODELINK CLOCK */
+
+ msr_value.high = 0;
+ msr_value.low = 5;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, MSR_GEODELINK_PM, &msr_value);
+ msr_value.low = 0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+ msr_value.low = 2;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+
+ /* SET REGA LIMITS */
+ /* Lower counter uses pixels/line */
+ /* Upper counter is 0xFFFF to prevent rollover. */
+
+ msr_value.low = 0xFFFF0000 | (hactive - 1);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGAVAL, &msr_value);
+
+ /* USE H4 FUNCTION A FOR DISPE AND H4 FUNCTION B FOR NOT DISPE */
+ /* DISPE is bit 47 */
+
+ msr_value.high = 0x00000002;
+ msr_value.low = 0xF0000FF0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 4, &msr_value);
+
+ /* USE H3 FUNCTION A FOR VSYNC AND H3 FUNCTION B FOR NOT VSYNC */
+ /* VSYNC is bit 45. */
+
+ msr_value.high = 0x00000000;
+ msr_value.low = 0x002D55AA;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 3, &msr_value);
+ } else {
+ /* PREFILTER CRC */
+
+ diag = 0x80138048;
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_DIAG, &msr_value);
+
+ /* ENABLE HW CLOCK GATING AND SET GLCP CLOCK TO GEODELINK CLOCK */
+
+ msr_value.high = 0;
+ msr_value.low = 5;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, MSR_GEODELINK_PM, &msr_value);
+ msr_value.low = 0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+ msr_value.low = 2;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DBGCLKCTL, &msr_value);
+
+ /* SET REGA LIMITS */
+ /* Lower counter uses pixels/line */
+ /* Upper counter is 0xFFFF to prevent rollover. */
+ /* Note that we are assuming that the number of */
+ /* source pixels is specified in the FB_ACTIVE register */
+
+ msr_value.low =
+ 0xFFFF0000 | ((READ_REG32(DC3_FB_ACTIVE) >> 16) & 0xFFF);
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGAVAL, &msr_value);
+
+ /* USE H4 FUNCTION A FOR DISPE AND H4 FUNCTION B FOR NOT DISPE */
+ /* DISPE is bit 55 */
+
+ msr_value.high = 0x00000003;
+ msr_value.low = 0x70000FF0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 4, &msr_value);
+
+ /* USE H3 FUNCTION A FOR VSYNC AND H3 FUNCTION B FOR NOT VSYNC */
+ /* VSYNC is bit 53. */
+
+ msr_value.high = 0x00000000;
+ msr_value.low = 0x003555AA;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 3, &msr_value);
+ }
+
+ /* WAIT FOR THE CORRECT FIELD */
+ /* We use the VG line count and field indicator to determine when */
+ /* to kick off a CRC. */
+
+ if (crc_source & VG_CRC_SOURCE_EVEN)
+ field = 0;
+ else
+ field = DC3_LNCNT_EVEN_FIELD;
+
+ if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
+ /* WAIT FOR THE BEGINNING OF THE FIELD (LINE 1-5) */
+ /* Note that we wait for the field to be odd when CRCing the even */
+ /* field and vice versa. This is because the CRC will not begin */
+ /* until the following field. */
+
+ do {
+ line = READ_REG32(DC3_LINE_CNT_STATUS);
+ } while ((line & DC3_LNCNT_EVEN_FIELD) != field ||
+ ((line & DC3_LNCNT_V_LINE_CNT) >> 16) < 1 ||
+ ((line & DC3_LNCNT_V_LINE_CNT) >> 16) > 5);
+ } else {
+ /* NON-INTERLACED - EVEN FIELD CRCS ARE INVALID */
+
+ if (crc_source & VG_CRC_SOURCE_EVEN)
+ return 0xFFFFFFFF;
+ }
+
+ /* UPDATE VG DIAG OUTPUT */
+
+ msr_value.high = 0;
+ msr_value.low = diag;
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_DIAG, &msr_value);
+
+ /* CONFIGURE DIAG CONTROL */
+ /* Set RegA action1 to increment lower 16 bits and clear at limit. (5) */
+ /* Set RegA action2 to increment upper 16 bits. (6) */
+ /* Set RegB action1 to CRC32 (1) */
+ /* Set all comparators to REGA override (0,1 lower mbus, 2,3 upper mbus) */
+ /* Enable all actions */
+
+ msr_value.low = 0x80EA20A0;
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DIAGCTL, &msr_value);
+
+ /* DELAY TWO FRAMES */
+
+ while (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA) ;
+ while (!(READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA)) ;
+ while (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA) ;
+ while (!(READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA)) ;
+ while (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VNA) ;
+
+ /* VERIFY THAT XSTATE = 11 */
+
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_XSTATE, &msr_value);
+ if ((msr_value.low & 3) == 3) {
+ msr_read64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGB, &msr_value);
+
+ crc = msr_value.low;
+ }
+
+ /* DISABLE VG DIAG BUS OUTPUTS */
+
+ msr_value.low = 0x00000000;
+ msr_value.high = 0x00000000;
+ msr_write64(MSR_DEVICE_GEODELX_VG, MSR_GEODELINK_DIAG, &msr_value);
+
+ /* DISABLE GLCP ACTIONS */
+
+ msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DIAGCTL, &msr_value);
+
+ return crc;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_scaler_filter_coefficients
+ *
+ * This routine gets the vertical and horizontal filter coefficients for
+ * graphics scaling. The coefficients are sign extended to 32-bit values.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_scaler_filter_coefficients(long h_taps[][5], long v_taps[][3])
+{
+ unsigned long irqfilt, i;
+ unsigned long temp;
+ long coeff0, coeff1, coeff2;
+ unsigned long lock;
+
+ /* ENABLE ACCESS TO THE HORIZONTAL COEFFICIENTS */
+
+ lock = READ_REG32(DC3_UNLOCK);
+ irqfilt = READ_REG32(DC3_IRQ_FILT_CTL);
+ irqfilt |= DC3_IRQFILT_H_FILT_SEL;
+
+ /* WRITE COEFFICIENTS */
+ /* Coefficient indexes do not auto-increment, so we must */
+ /* write the address for every phase */
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ temp = READ_REG32(DC3_FILT_COEFF1);
+ coeff0 = (temp & 0x3FF);
+ coeff1 = (temp >> 10) & 0x3FF;
+ coeff2 = (temp >> 20) & 0x3FF;
+
+ h_taps[i][0] = (coeff0 << 22) >> 22;
+ h_taps[i][1] = (coeff1 << 22) >> 22;
+ h_taps[i][2] = (coeff2 << 22) >> 22;
+
+ temp = READ_REG32(DC3_FILT_COEFF2);
+ coeff0 = (temp & 0x3FF);
+ coeff1 = (temp >> 10) & 0x3FF;
+
+ h_taps[i][3] = (coeff0 << 22) >> 22;
+ h_taps[i][4] = (coeff1 << 22) >> 22;
+ }
+
+ /* ENABLE ACCESS TO THE VERTICAL COEFFICIENTS */
+
+ irqfilt &= ~DC3_IRQFILT_H_FILT_SEL;
+
+ /* WRITE COEFFICIENTS */
+
+ for (i = 0; i < 256; i++) {
+ WRITE_REG32(DC3_IRQ_FILT_CTL, ((irqfilt & 0xFFFFFF00L) | i));
+
+ temp = READ_REG32(DC3_FILT_COEFF1);
+ coeff0 = (temp & 0x3FF);
+ coeff1 = (temp >> 10) & 0x3FF;
+ coeff2 = (temp >> 20) & 0x3FF;
+
+ v_taps[i][0] = (coeff0 << 22) >> 22;
+ v_taps[i][1] = (coeff1 << 22) >> 22;
+ v_taps[i][2] = (coeff2 << 22) >> 22;
+ }
+
+ WRITE_REG32(DC3_UNLOCK, lock);
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_flicker_filter_configuration
+ *
+ * This routine returns the current VG flicker filter configuration.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_flicker_filter_configuration(unsigned long *strength,
+ int *flicker_alpha)
+{
+ unsigned long genlk_ctl;
+
+ if (!strength || !flicker_alpha)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ genlk_ctl = READ_REG32(DC3_GENLK_CTL);
+ *strength = genlk_ctl & DC3_GC_FLICKER_FILTER_MASK;
+ if (genlk_ctl & DC3_GC_ALPHA_FLICK_ENABLE)
+ *flicker_alpha = 1;
+ else
+ *flicker_alpha = 0;
+
+ return CIM_STATUS_OK;
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_display_pitch
+ *
+ * This routine returns the current stride between successive lines of frame
+ * buffer data.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_get_display_pitch(void)
+{
+ return ((READ_REG32(DC3_GFX_PITCH) & 0x0000FFFF) << 3);
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_frame_buffer_line_size
+ *
+ * This routine returns the current size in bytes of one line of frame buffer
+ * data.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_get_frame_buffer_line_size(void)
+{
+ return ((READ_REG32(DC3_LINE_SIZE) & 0x3FF) << 3);
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_current_vline
+ *
+ * This routine returns the number of the current line that is being displayed
+ * by the display controller.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_get_current_vline(void)
+{
+ unsigned long current_line;
+
+ /* READ THE REGISTER TWICE TO ENSURE THAT THE VALUE IS NOT TRANSITIONING */
+
+ do {
+ current_line = READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_V_LINE_CNT;
+ }
+ while (current_line !=
+ (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_V_LINE_CNT));
+
+ return (current_line >> 16);
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_display_offset
+ *
+ * This routine returns the offset into the frame buffer for the first pixel
+ * of the display.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_get_display_offset(void)
+{
+ return (READ_REG32(DC3_FB_ST_OFFSET) & 0x0FFFFFFF);
+}
+
+/*---------------------------------------------------------------------------
+ * vg_get_cursor_info
+ *
+ * This routine returns the current settings for the hardware cursor.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_cursor_info(VG_CURSOR_DATA * cursor_data)
+{
+ unsigned long temp;
+
+ /* CURSOR OFFSET */
+
+ cursor_data->cursor_offset = READ_REG32(DC3_CURS_ST_OFFSET) & 0x0FFFFFFF;
+
+ /* CURSOR X POSITION */
+
+ temp = READ_REG32(DC3_CURSOR_X);
+ cursor_data->cursor_x = temp & 0x7FF;
+ cursor_data->clipx = (temp >> 11) & 0x3F;
+
+ /* CURSOR Y POSITION */
+
+ temp = READ_REG32(DC3_CURSOR_Y);
+ cursor_data->cursor_y = temp & 0x7FF;
+ cursor_data->clipy = (temp >> 11) & 0x3F;
+
+ /* CURSOR COLORS */
+
+ WRITE_REG32(DC3_PAL_ADDRESS, 0x100);
+ cursor_data->mono_color0 = READ_REG32(DC3_PAL_DATA);
+ cursor_data->mono_color1 = READ_REG32(DC3_PAL_DATA);
+
+ /* CURSOR ENABLES */
+
+ temp = READ_REG32(DC3_GENERAL_CFG);
+ if (temp & DC3_GCFG_CURE)
+ cursor_data->enable = 1;
+ else
+ cursor_data->enable = 0;
+ if (temp & DC3_GCFG_CLR_CUR)
+ cursor_data->color_cursor = 1;
+ else
+ cursor_data->color_cursor = 0;
+
+ return CIM_STATUS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_display_palette_entry
+ *
+ * This routine reads a single entry in the 8BPP display palette.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_display_palette_entry(unsigned long index, unsigned long *entry)
+{
+ if (index > 0xFF)
+ return CIM_STATUS_INVALIDPARAMS;
+
+ WRITE_REG32(DC3_PAL_ADDRESS, index);
+ *entry = READ_REG32(DC3_PAL_DATA);
+
+ return CIM_STATUS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_border_color
+ *
+ * This routine reads the current border color for centered displays.
+ *--------------------------------------------------------------------------*/
+
+unsigned long
+vg_get_border_color(void)
+{
+ WRITE_REG32(DC3_PAL_ADDRESS, 0x104);
+ return READ_REG32(DC3_PAL_DATA);
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_display_palette
+ *
+ * This routines reads the entire contents of the display palette into a
+ * buffer. The display palette consists of 256 X:R:G:B values.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_display_palette(unsigned long *palette)
+{
+ unsigned long i;
+
+ if (palette) {
+ WRITE_REG32(DC3_PAL_ADDRESS, 0);
+ for (i = 0; i < 256; i++) {
+ palette[i] = READ_REG32(DC3_PAL_DATA);
+ }
+ return CIM_STATUS_OK;
+ }
+ return CIM_STATUS_INVALIDPARAMS;
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_compression_info
+ *
+ * This routines reads the current status of the display compression hardware.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_compression_info(VG_COMPRESSION_DATA * comp_data)
+{
+ comp_data->compression_offset = READ_REG32(DC3_CB_ST_OFFSET) & 0x0FFFFFFF;
+ comp_data->pitch = (READ_REG32(DC3_GFX_PITCH) >> 13) & 0x7FFF8;
+ comp_data->size = ((READ_REG32(DC3_LINE_SIZE) >> (DC3_LINE_SIZE_CB_SHIFT -
+ 3)) & 0x3F8) + 24;
+
+ return CIM_STATUS_OK;
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_compression_enable
+ *
+ * This routines reads the current enable status of the display compression
+ * hardware.
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_compression_enable(void)
+{
+ if (READ_REG32(DC3_GENERAL_CFG) & DC3_GCFG_CMPE)
+ return 1;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------
+ * vg_get_valid_bit
+ *--------------------------------------------------------------------------*/
+
+int
+vg_get_valid_bit(int line)
+{
+ unsigned long offset;
+ unsigned long valid;
+ unsigned long lock;
+
+ lock = READ_REG32(DC3_UNLOCK);
+ offset = READ_REG32(DC3_PHY_MEM_OFFSET) & 0xFF000000;
+ offset |= line;
+
+ WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
+ WRITE_REG32(DC3_PHY_MEM_OFFSET, offset);
+ WRITE_REG32(DC3_UNLOCK, lock);
+ valid = READ_REG32(DC3_DV_ACC) & 2;
+
+ if (valid)
+ return 1;
+ return 0;
+}
+
+#endif