diff options
Diffstat (limited to 'src/cim/cim_vg.c')
-rw-r--r-- | src/cim/cim_vg.c | 3711 |
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 |