/*
 * 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 filter routines.  These routines program the video
  * hardware.
  */

/*---------------------------------------------------------------------------
 * df_set_crt_enable
 *
 * This routine enables or disables CRT output.
 *--------------------------------------------------------------------------*/

int
df_set_crt_enable(int crt_output)
{
    unsigned long config, misc;

    config = READ_VID32(DF_DISPLAY_CONFIG);
    misc = READ_VID32(DF_VID_MISC);

    switch (crt_output) {
        /* DISABLE DISPLAY */

    case DF_CRT_DISABLE:

        config &= ~(DF_DCFG_DIS_EN | DF_DCFG_HSYNC_EN |
                    DF_DCFG_VSYNC_EN | DF_DCFG_DAC_BL_EN);
        misc |= DF_DAC_POWER_DOWN;
        break;

        /* ENABLE THE DISPLAY */

    case DF_CRT_ENABLE:

        config |= (DF_DCFG_DIS_EN | DF_DCFG_HSYNC_EN |
                   DF_DCFG_VSYNC_EN | DF_DCFG_DAC_BL_EN);
        misc &= ~(DF_DAC_POWER_DOWN | DF_ANALOG_POWER_DOWN);
        break;

        /* HSYNC:OFF VSYNC:ON */

    case DF_CRT_STANDBY:

        config = (config & ~(DF_DCFG_DIS_EN | DF_DCFG_HSYNC_EN |
                             DF_DCFG_DAC_BL_EN)) | DF_DCFG_VSYNC_EN;
        misc |= DF_DAC_POWER_DOWN;
        break;

        /* HSYNC:ON VSYNC:OFF */

    case DF_CRT_SUSPEND:

        config = (config & ~(DF_DCFG_DIS_EN | DF_DCFG_VSYNC_EN |
                             DF_DCFG_DAC_BL_EN)) | DF_DCFG_HSYNC_EN;
        misc |= DF_DAC_POWER_DOWN;
        break;

    default:
        return CIM_STATUS_INVALIDPARAMS;
    }

    WRITE_VID32(DF_DISPLAY_CONFIG, config);
    WRITE_VID32(DF_VID_MISC, misc);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_panel_enable
 *
 * This routine enables or disables panel output.
 *--------------------------------------------------------------------------*/

int
df_set_panel_enable(int enable)
{
    unsigned long pm;

    pm = READ_VID32(DF_POWER_MANAGEMENT);

    if (enable)
        pm |= DF_PM_PANEL_ON;
    else
        pm &= ~DF_PM_PANEL_ON;

    WRITE_VID32(DF_POWER_MANAGEMENT, pm);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_configure_video_source
 *
 * This routine initializes all aspects of the source buffer for a video overlay.
 *--------------------------------------------------------------------------*/

int
df_configure_video_source(DF_VIDEO_SOURCE_PARAMS * video_source_odd,
                          DF_VIDEO_SOURCE_PARAMS * video_source_even)
{
    unsigned long pitch, ctrl, vcfg;
    unsigned long lock, vg_line, gcfg;
    unsigned long width, size, scale;
    unsigned long misc;

    lock = READ_REG32(DC3_UNLOCK);
    vg_line = READ_REG32(DC3_LINE_SIZE);
    gcfg = READ_REG32(DC3_GENERAL_CFG);
    vcfg = READ_VID32(DF_VIDEO_CONFIG);
    ctrl = READ_VID32(DF_VID_ALPHA_CONTROL);
    scale = READ_VID32(DF_VIDEO_SCALER);

    /* STORE THE DESIRED SCALING PROCEDURE */
    /* Cimarron supports two modes when programming the scale and position  */
    /* of the video window.  The first mode is designed to implicitly apply */
    /* the graphics scale to any video operations.  The second applys the   */
    /* video unchanged, allowing complete control by the user.  To allow    */
    /* visibility between modules, the current mode is stored in a spare    */
    /* bit in the DF miscellaneous register.                                */

    misc = READ_VID32(DF_VID_MISC);
    if (video_source_odd->flags & DF_SOURCEFLAG_IMPLICITSCALING)
        misc |= DF_USER_IMPLICIT_SCALING;
    else
        misc &= DF_USER_IMPLICIT_SCALING;
    WRITE_VID32(DF_VID_MISC, misc);

    /* PARAMETER - VIDEO PITCH */

    pitch =
        (video_source_odd->y_pitch >> 3) | ((video_source_odd->uv_pitch >> 3) <<
                                            16);

    /* PARAMETER - VIDEO FORMAT */

    gcfg &= ~DC3_GCFG_YUV_420;
    vcfg &= ~(DF_VCFG_VID_INP_FORMAT | DF_VCFG_4_2_0_MODE);
    ctrl &= ~(DF_VIDEO_INPUT_IS_RGB | DF_CSC_VIDEO_YUV_TO_RGB | DF_HD_VIDEO |
              DF_YUV_CSC_EN);

    /* SELECT PIXEL ORDERING */

    switch (video_source_odd->video_format & 3) {
    case 0:
        vcfg |= DF_VCFG_UYVY_FORMAT;
        break;
    case 1:
        vcfg |= DF_VCFG_Y2YU_FORMAT;
        break;
    case 2:
        vcfg |= DF_VCFG_YUYV_FORMAT;
        break;
    case 3:
        vcfg |= DF_VCFG_YVYU_FORMAT;
        break;
    }

    /* SELECT SOURCE FORMAT (4:2:2, 4:2:0, RGB) */

    switch (video_source_odd->video_format >> 2) {
    case 0:
        ctrl |= DF_CSC_VIDEO_YUV_TO_RGB;
        break;

    case 1:
        ctrl |= DF_CSC_VIDEO_YUV_TO_RGB;
        vcfg |= DF_VCFG_4_2_0_MODE;
        gcfg |= DC3_GCFG_YUV_420;
        break;

    case 2:
        ctrl |= DF_VIDEO_INPUT_IS_RGB;
        break;

    default:
        return CIM_STATUS_INVALIDPARAMS;
    }

    /* ALIGN TO APPROPRIATE OUTPUT COLOR SPACE                             */
    /* We have assumed until this point that the output color space is RGB */
    /* and the input (if YUV) is always SDTV video.                        */

    if (video_source_odd->flags & DF_SOURCEFLAG_HDTVSOURCE)
        ctrl |= DF_HD_VIDEO;

    if (ctrl & DF_CSC_GRAPHICS_RGB_TO_YUV) {
        /* YUV OUTPUT - DISABLE YUV->RGB AND ENABLE YUV->YUV */

        ctrl &= ~DF_CSC_VIDEO_YUV_TO_RGB;

        if ((!(ctrl & DF_HD_VIDEO) && (ctrl & DF_HD_GRAPHICS)) ||
            ((ctrl & DF_HD_VIDEO) && !(ctrl & DF_HD_GRAPHICS))) {
            ctrl |= DF_YUV_CSC_EN;
        }
    }

    /* PARAMETER - DISPLAY FILTER BUFFER SIZE                        */
    /* The line size in the video generator must be 32-byte aligned. */
    /* However, smaller alignments are managed by setting the        */
    /* appropriate pitch and clipping the video window.              */

    vcfg &= ~(DF_VCFG_LINE_SIZE_LOWER_MASK | DF_VCFG_LINE_SIZE_BIT8 |
              DF_VCFG_LINE_SIZE_BIT9);

    size = ((video_source_odd->width >> 1) + 7) & 0xFFF8;

    vcfg |= (size & 0x00FF) << 8;
    if (size & 0x0100)
        vcfg |= DF_VCFG_LINE_SIZE_BIT8;
    if (size & 0x0200)
        vcfg |= DF_VCFG_LINE_SIZE_BIT9;

    scale = (scale & ~0x7FF) | video_source_odd->height;

    /* PARAMETER - VIDEO GENERATOR BUFFER SIZE */

    vg_line &= ~DC3_LINE_SIZE_VLS_MASK;

    if (gcfg & DC3_GCFG_YUV_420)
        width = ((video_source_odd->width >> 1) + 7) & 0xFFF8;
    else
        width = ((video_source_odd->width << 1) + 31) & 0xFFE0;

    vg_line |= (width >> 3) << DC3_LINE_SIZE_VB_SHIFT;

    /* WRITE ALL PARAMETERS AT ONCE */

    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
    WRITE_VID32(DF_VIDEO_CONFIG, vcfg);
    WRITE_VID32(DF_VID_ALPHA_CONTROL, ctrl);
    WRITE_VID32(DF_VIDEO_SCALER, scale);
    WRITE_REG32(DC3_GENERAL_CFG, gcfg);
    WRITE_REG32(DC3_LINE_SIZE, vg_line);
    WRITE_REG32(DC3_VID_YUV_PITCH, pitch);

    /* WRITE EVEN OR ODD BUFFER OFFSETS                            */
    /* The even buffer is only valid inside an interlaced display. */

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        WRITE_REG32(DC3_VID_EVEN_Y_ST_OFFSET, video_source_even->y_offset);
        WRITE_REG32(DC3_VID_EVEN_U_ST_OFFSET, video_source_even->u_offset);
        WRITE_REG32(DC3_VID_EVEN_V_ST_OFFSET, video_source_even->v_offset);
    }

    WRITE_REG32(DC3_VID_Y_ST_OFFSET, video_source_odd->y_offset);
    WRITE_REG32(DC3_VID_U_ST_OFFSET, video_source_odd->u_offset);
    WRITE_REG32(DC3_VID_V_ST_OFFSET, video_source_odd->v_offset);

    WRITE_REG32(DC3_UNLOCK, lock);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_offsets
 *
 * This routine sets the starting offset for the video buffer(s).  The buffers
 * can also be configured inside df_configure_video_source, but a separate
 * routine is provided here to allow quick buffer flipping.
 *--------------------------------------------------------------------------*/

int
df_set_video_offsets(int even, unsigned long y_offset,
                     unsigned long u_offset, unsigned long v_offset)
{
    unsigned long lock = READ_REG32(DC3_UNLOCK);

    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);

    if (even) {
        WRITE_REG32(DC3_VID_EVEN_Y_ST_OFFSET, y_offset);
        WRITE_REG32(DC3_VID_EVEN_U_ST_OFFSET, u_offset);
        WRITE_REG32(DC3_VID_EVEN_V_ST_OFFSET, v_offset);
    }
    else {
        WRITE_REG32(DC3_VID_Y_ST_OFFSET, y_offset);
        WRITE_REG32(DC3_VID_U_ST_OFFSET, u_offset);
        WRITE_REG32(DC3_VID_V_ST_OFFSET, v_offset);
    }

    WRITE_REG32(DC3_UNLOCK, lock);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_scale
 *
 * This routine programs the horizontal/vertical scale factors for video.  To
 * disable scaling/filtering, this routine should be called with identical source
 * and destination dimensions.
 *--------------------------------------------------------------------------*/

int
df_set_video_scale(unsigned long src_width, unsigned long src_height,
                   unsigned long dst_width, unsigned long dst_height,
                   unsigned long flags)
{
    unsigned long temp, misc;
    unsigned long scale, gfxscale;
    unsigned long fbactive, src;
    unsigned long size, downscale;
    unsigned long vcfg, gcfg, unlock;

    /* APPLY THE GRAPHICS SCALE */
    /* When requested by the user, we will adjust the video scale by the  */
    /* current graphics scale factor.  This allows video to be programmed */
    /* in terms of the graphics source resolution.                        */

    misc = READ_VID32(DF_VID_MISC);
    if (misc & DF_USER_IMPLICIT_SCALING) {
        gfxscale = READ_REG32(DC3_GFX_SCALE);
        fbactive = READ_REG32(DC3_FB_ACTIVE);

        /* REVERSE ENGINEER THE SCALE FACTOR */
        /* The graphics scale factor is (source / (dst - 1)), so a little */
        /* math is performed to reverse engineer the correct scale for    */
        /* video.                                                         */
        /*                                                                */
        /* F = (0x4000*S)/(D-1)  ->  (D/S) = (((0x4000*S)/F)+1)/S         */

        scale = gfxscale & 0xFFFF;
        src = (fbactive >> 16) + 1;
        if (scale != 0x4000) {
            dst_width = dst_width * (((0x4000 * src) / scale) + 1);
            dst_width /= src;
        }

        scale = gfxscale >> 16;
        src = (fbactive & 0xFFFF) + 1;
        if (scale != 0x4000) {
            dst_height = dst_height * (((0x4000 * src) / scale) + 1);
            dst_height /= src;
        }
    }

    /* CHECK FOR VALID SCALING FACTOR */
    /* The display filter/video generator can support up to 8:1  */
    /* horizontal downscale and up to 4:1 vertical downscale.    */
    /* Scale factors above 4:1 horizontal and 2:1 horizontal     */
    /* will have a quality impact.  However, at such large scale */
    /* factors, it might not matter,                             */

    if (((flags & DF_SCALEFLAG_CHANGEX) && dst_width < (src_width >> 3)) ||
        ((flags & DF_SCALEFLAG_CHANGEY) && dst_height < (src_height >> 2))) {
        return CIM_STATUS_INVALIDSCALE;
    }

    /* ENABLE OR DISABLE ADVANCED SCALING FEATURES          */
    /* Scaling above 2:1 vertical and 4:1 horizontal relies */
    /* on mechanisms beside the line filter.                */

    if (flags & DF_SCALEFLAG_CHANGEX) {
        scale = READ_VID32(DF_VIDEO_SCALER);
        vcfg = READ_VID32(DF_VIDEO_CONFIG);
        vcfg &= ~(DF_VCFG_LINE_SIZE_LOWER_MASK | DF_VCFG_LINE_SIZE_BIT8 |
                  DF_VCFG_LINE_SIZE_BIT9);

        if (dst_width < (src_width >> 2)) {
            src_width >>= 1;
            WRITE_VID32(DF_VIDEO_SCALER, scale | DF_SCALE_DOUBLE_H_DOWNSCALE);
        }
        else {
            WRITE_VID32(DF_VIDEO_SCALER, scale & ~DF_SCALE_DOUBLE_H_DOWNSCALE);
        }

        /* PROGRAM A NEW LINE SIZE */
        /* The line size must be updated when using the Double Horizontal  */
        /* Downscale (DHD) bit.  This is because the amount of VFIFO space */
        /* consumed is effectively half in this mode.                      */

        size = ((src_width >> 1) + 7) & 0xFFF8;
        vcfg |= (size & 0x00FF) << 8;
        if (size & 0x0100)
            vcfg |= DF_VCFG_LINE_SIZE_BIT8;
        if (size & 0x0200)
            vcfg |= DF_VCFG_LINE_SIZE_BIT9;
        WRITE_VID32(DF_VIDEO_CONFIG, vcfg);
        WRITE_VID32(DF_VIDEO_XSCALE, ((0x10000 * src_width) / dst_width));
    }

    if (flags & DF_SCALEFLAG_CHANGEY) {
        unlock = READ_REG32(DC3_UNLOCK);
        gcfg = READ_REG32(DC3_GENERAL_CFG) & ~DC3_GCFG_VDSE;
        WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
        if (dst_height < (src_height >> 1)) {
            gcfg |= DC3_GCFG_VDSE;
            downscale = READ_REG32(DC3_VID_DS_DELTA) & ~DC3_DS_DELTA_MASK;
            if (dst_height == (src_height >> 2))
                downscale |= (0x3FFF << 18);
            else
                downscale |= (((src_height >> 1) << 14) / dst_height) << 18;

            WRITE_REG32(DC3_VID_DS_DELTA, downscale);
            WRITE_VID32(DF_VIDEO_YSCALE, 0x20000);
        }
        else {
            WRITE_VID32(DF_VIDEO_YSCALE, ((0x10000 * src_height) / dst_height));
        }
        WRITE_REG32(DC3_GENERAL_CFG, gcfg);
        WRITE_REG32(DC3_UNLOCK, unlock);
    }

    /* CHECK IF SCALING IS DISABLED */
    /* If no scaling occurs, we disable the hardware filter. */

    temp = READ_VID32(DF_VIDEO_CONFIG);
    if ((READ_VID32(DF_VIDEO_XSCALE) == 0x10000) &&
        (READ_VID32(DF_VIDEO_YSCALE) == 0x10000)) {
        WRITE_VID32(DF_VIDEO_CONFIG, (temp | DF_VCFG_SC_BYP));
    }
    else
        WRITE_VID32(DF_VIDEO_CONFIG, (temp & ~DF_VCFG_SC_BYP));

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_position
 *
 * This routine programs the position of the video window on the display.
 * An indent parameter is also passed to this program to prevent artifacts
 * when the video window is moved beyond the left edge of the screen.
 *--------------------------------------------------------------------------*/

int
df_set_video_position(DF_VIDEO_POSITION * video_window)
{
    unsigned long vblankstart_even, vblankend_even, vsyncend_even,
        vtotal_even, vactive_even;
    unsigned long hblankstart, hblankend, hsyncend, htotal, hactive;
    unsigned long vblankstart, vblankend, vsyncend, vtotal, vactive;
    unsigned long width, height, height_even;
    unsigned long adjust, border_x, border_y, border_y_even;
    unsigned long xstart, xend;
    unsigned long ystart, yend;
    unsigned long ckey_x, ckey_y;
    unsigned long x_copy, y_copy;
    unsigned long width_copy, height_copy;
    unsigned long vcfg, initread;
    unsigned long xscale, dst_clip;
    unsigned long ypos, ypos_even;
    unsigned long y, gfxscale;
    unsigned long misc, fbactive;
    unsigned long scale, src;
    unsigned long irq_ctl;
    unsigned long unlock;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    vblankend = ((READ_REG32(DC3_V_BLANK_TIMING) >> 16) & 0xFFF) + 1;
    hblankend = ((READ_REG32(DC3_H_BLANK_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    vblankstart = (READ_REG32(DC3_V_BLANK_TIMING) & 0xFFF) + 1;
    hblankstart = (READ_REG32(DC3_H_BLANK_TIMING) & 0xFFF) + 1;
    hactive = (READ_REG32(DC3_H_ACTIVE_TIMING) & 0xFFF) + 1;
    vactive = (READ_REG32(DC3_V_ACTIVE_TIMING) & 0xFFF) + 1;
    unlock = READ_REG32(DC3_UNLOCK);

    /* INCLUDE BORDER IF REQUESTED */

    if (video_window->flags & DF_POSFLAG_INCLUDEBORDER) {
        border_x = htotal - hblankend;
        border_y = vtotal - vblankend;
        hactive = hblankstart + htotal - hblankend;
        vactive = vblankstart + vtotal - vblankend;
    }
    else {
        border_x = border_y = 0;
    }

    /* APPLY THE GRAPHICS SCALE     */
    /* Do not alter the input data. */

    width_copy = video_window->width;
    height_copy = video_window->height;
    x_copy = video_window->x;
    y_copy = video_window->y;

    misc = READ_VID32(DF_VID_MISC);
    if (misc & DF_USER_IMPLICIT_SCALING) {
        gfxscale = READ_REG32(DC3_GFX_SCALE);
        fbactive = READ_REG32(DC3_FB_ACTIVE);

        /* REVERSE ENGINEER THE SCALE FACTOR */

        scale = gfxscale & 0xFFFF;
        src = (fbactive >> 16) + 1;
        if (scale != 0x4000) {
            width_copy = width_copy * (((0x4000 * src) / scale) + 1);
            width_copy /= src;
            x_copy = x_copy * (((0x4000 * src) / scale) + 1);
            x_copy /= src;
        }

        scale = gfxscale >> 16;
        src = (fbactive & 0xFFFF) + 1;
        if (scale != 0x4000) {
            height_copy = height_copy * (((0x4000 * src) / scale) + 1);
            height_copy /= src;
            y_copy = y_copy * (((0x4000 * src) / scale) + 1);
            y_copy /= src;
        }
    }

    /* HANDLE INTERLACING */
    /* When the output is interlaced, we must set the position and height */
    /* on the fields and not on the composite image.                      */

    if ((irq_ctl = READ_REG32(DC3_IRQ_FILT_CTL)) & DC3_IRQFILT_INTL_EN) {
        vsyncend_even = ((READ_REG32(DC3_V_SYNC_EVEN) >> 16) & 0xFFF) + 1;
        vtotal_even = ((READ_REG32(DC3_V_ACTIVE_EVEN) >> 16) & 0xFFF) + 1;
        vblankend_even = ((READ_REG32(DC3_V_BLANK_EVEN) >> 16) & 0xFFF) + 1;
        vactive_even = (READ_REG32(DC3_V_ACTIVE_EVEN) & 0xFFF) + 1;
        vblankstart_even = (READ_REG32(DC3_V_BLANK_EVEN) & 0xFFF) + 1;

        if (video_window->flags & DF_POSFLAG_INCLUDEBORDER) {
            border_y_even = vtotal_even - vblankend_even;
            vactive_even = vblankstart_even + vtotal_even - vblankend_even;
        }
        else
            border_y_even = 0;

        /*
         * THE ODD FIELD MUST ALWAYS PRECEDE THE EVEN FIELD
         * This implies that we can never start video on an odd y position
         * in the composite image.  This is required because the only way
         * to accomplish an odd y start would be to switch the buffer
         * which could have serious repercussions for genlocked VIP.
         */

        y = y_copy >> 1;

        /* CALCULATE Y POSITION FOR ODD FIELD */
        /* Clip the video window to the odd field timings. Note that the */
        /* height in the odd field may be greater if the video height is */
        /* odd.                                                          */

        height = (height_copy + 1) >> 1;
        if ((y + height) > vactive)
            height = vactive - y;

        ystart = y + vtotal_even - vsyncend_even + 1;
        if (video_window->flags & DF_POSFLAG_INCLUDEBORDER)
            ystart -= border_y_even;

        yend = ystart + height;
        ypos = (yend << 16) | ystart;

        /* CALCULATE Y POSITION FOR EVEN FIELD */

        height_even = height_copy >> 1;
        if ((y + height_even) > vactive_even)
            height_even = vactive_even - y;

        ystart = y + vtotal - vsyncend + 1;
        if (video_window->flags & DF_POSFLAG_INCLUDEBORDER)
            ystart -= border_y;

        yend = ystart + height_even;
        ypos_even = (yend << 16) | ystart;

        /* CALCULATE ACTUAL FRAME BUFFER HEIGHT */
        /* The y position and height are used to determine the actual    */
        /* placement of the color key region.  The region will either be */
        /* the sum of the even and odd fields (for interlaced addressing */
        /* or flicker filtering) or it will be the union of the two (for */
        /* line doubling).  We must also adjust the region such that the */
        /* origin (0, 0) is centered on the beginning of graphics data.  */
        /* This is only a problem if video is being displayed over the   */
        /* overscan area.                                                */

        if ((READ_REG32(DC3_GENLK_CTL) & DC3_GC_FLICKER_FILTER_ENABLE) ||
            (irq_ctl & DC3_IRQFILT_INTL_ADDR)) {
            y <<= 1;
            height += height_even;
            adjust = border_y + border_y_even;
        }
        else {
            adjust = border_y;
            if (height_even > height)
                height = height_even;
        }
        if (video_window->flags & DF_POSFLAG_INCLUDEBORDER) {
            if (y > adjust) {
                y -= adjust;
                adjust = 0;
            }
            else {
                adjust -= y;
                if (height > adjust)
                    height -= adjust;
                else
                    height = 0;
            }
        }

    }
    else {
        y = y_copy;

        height = height_copy;
        if ((y + height) > vactive)
            height = vactive - y;

        ystart = y + vtotal - vsyncend + 1;
        if (video_window->flags & DF_POSFLAG_INCLUDEBORDER)
            ystart -= border_y;

        yend = ystart + height;
        ypos = (yend << 16) | ystart;
        ypos_even = 0;
    }

    /* HORIZONTAL POSITION */
    /* The horizontal values are identical for the even and odd field. */

    width = width_copy;
    xstart = x_copy + htotal - hsyncend - 14;
    if (video_window->flags & DF_POSFLAG_INCLUDEBORDER)
        xstart -= border_x;

    /* RIGHT CLIPPING */

    if ((x_copy + width) > hactive)
        width = hactive - x_copy;

    xend = xstart + width;

    /*
     * CALCULATE LEFT CLIPPING PARAMETER
     * The value passed in can be interpreted as destination pixels, in
     * which case the video scale is factored in, or as source pixels, in
     * which case the value is written directly.  Also, the display filter's
     * initial read address value is only programmable on 4-pixel increments.
     * However, we can achieve an arbitrary left clip by adjusting the
     * xstart value, as there is a 14-clock delay in which to play.  Also,
     * according to the designers, 4:2:0 and 4:2:2 behave identically when
     * setting the initial read address.  The addition of scaling further
     * complicates the algorithm.  When setting the initial read address, it
     * is in terms of source pixels, while adjusting the xstart value is in
     * destination pixels We may thus not be able to achieve a perfect
     * clipping fit for scaled video.  We compensate by including two
     * clipping parameters in our structure.  This allows us the user
     * additional control and it allows us to accurately convey to the user
     * the state of clipping on the machine.
     */

    initread = video_window->left_clip;
    dst_clip = 0;
    if (!(video_window->flags & DF_POSFLAG_DIRECTCLIP)) {
        xscale = READ_VID32(DF_VIDEO_XSCALE) & 0xFFFFF;
        initread = (initread * xscale) / 0x10000;
        if (xscale)
            dst_clip = ((initread & 3) * 0x10000) / xscale;
    }
    else
        dst_clip = video_window->dst_clip;

    /*
     * LIMIT THE CLIP
     * We technically have a 14 pixel window in which to play.  However,
     * taking the entire 14 pixels makes the video timing a little hairy...
     * Also note that we cannot do this when performing panel centering, as
     * the video would then exceed the mode size.
     */

    if (dst_clip > 4)
        dst_clip = 4;
    if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DCEN)
        dst_clip = 0;

    xstart -= dst_clip;

    vcfg = READ_VID32(DF_VIDEO_CONFIG);
    vcfg &= ~DF_VCFG_INIT_READ_MASK;
    vcfg |= (initread >> 2) << 16;

    /* SET COLOR KEY REGION */
    /* We are assuming that color keying will never be desired outside   */
    /* of the video region.  We adjust the color key region for graphics */
    /* scaling.                                                          */

    gfxscale = READ_REG32(DC3_GFX_SCALE);

    ckey_x = ((x_copy * (gfxscale & 0xFFFF)) / 0x4000) |
        ((((x_copy + width) * (gfxscale & 0xFFFF)) / 0x4000) << 16);
    ckey_y = ((y * (gfxscale >> 16)) / 0x4000) |
        ((((y + height) * (gfxscale >> 16)) / 0x4000) << 16);

    /* WRITE ALL PARAMETERS AT ONCE */

    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
    WRITE_REG32(DC3_CLR_KEY_X, ckey_x);
    WRITE_REG32(DC3_CLR_KEY_Y, ckey_y);
    WRITE_VID32(DF_VIDEO_X_POS, (xend << 16) | xstart);
    WRITE_VID32(DF_VIDEO_Y_POS, ypos);
    WRITE_VID32(DF_VID_YPOS_EVEN, ypos_even);
    WRITE_VID32(DF_VIDEO_CONFIG, vcfg);
    WRITE_REG32(DC3_UNLOCK, unlock);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_filter_coefficients
 *
 * This routine sets the horizontal and vertical filter coefficients for video
 * scaling.  These coefficients are used for upscaling and downscaling video.
 * If the phase256 parameter is 1, the coefficient arrays are used as single
 * arrays of 256 phases for both vertical and horizontal scaling.  If the
 * phase256 parameter is clear, the coefficient arrays are used as two
 * 128-phase arrays.  The first 128 entries represent the phases for
 * vertical scaling.  The last 128 entries represent the phases for
 * horizontal scaling.
 *--------------------------------------------------------------------------*/

int
df_set_video_filter_coefficients(long taps[][4], int phase256)
{
    unsigned long scale, coeff0, coeff1;
    unsigned long i;
    long (*defaults)[2];

    /* SET PHASE COUNT AND CHOOSE COEFFICIENT ARRAY */

    scale = READ_VID32(DF_VIDEO_SCALER);
    if (phase256) {
        WRITE_VID32(DF_VIDEO_SCALER, (scale & ~DF_SCALE_128_PHASES));
        defaults = CimarronVideoFilter256;
    }
    else {
        WRITE_VID32(DF_VIDEO_SCALER, (scale | DF_SCALE_128_PHASES));
        defaults = CimarronVideoFilter128;
    }

    /* PROGRAM COEFFICIENTS */

    for (i = 0; i < 256; i++) {
        if (!taps) {
            coeff0 = defaults[i][0];
            coeff1 = defaults[i][1];
        }
        else {
            if (taps[i][1] < 0)
                coeff0 = -taps[i][1] | 0x8000;
            else
                coeff0 = taps[i][1];

            coeff0 <<= 16;

            if (taps[i][0] < 0)
                coeff0 |= -taps[i][0] | 0x8000;
            else
                coeff0 |= taps[i][0];

            if (taps[i][3] < 0)
                coeff1 = -taps[i][3] | 0x8000;
            else
                coeff1 = taps[i][3];

            coeff1 <<= 16;

            if (taps[i][2] < 0)
                coeff1 |= -taps[i][2] | 0x8000;
            else
                coeff1 |= taps[i][2];
        }

        WRITE_VID32((DF_COEFFICIENT_BASE + (i << 3)), coeff0);
        WRITE_VID32((DF_COEFFICIENT_BASE + (i << 3) + 4), coeff1);
    }

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_enable
 *
 * This routine enables or disables the video overlay.
 *--------------------------------------------------------------------------*/

int
df_set_video_enable(int enable, unsigned long flags)
{
    unsigned long vcfg, lock, gcfg;
    unsigned long dcfg, vg_ckey, fifo = 0;

    vcfg = READ_VID32(DF_VIDEO_CONFIG);
    lock = READ_REG32(DC3_UNLOCK);
    gcfg = READ_REG32(DC3_GENERAL_CFG);

    /* SET VIDEO FIFO END WATERMARK */
    /* The video FIFO end watermark is set to 0 when video is disabled  */
    /* to allow low priority transactions in the VG.  Otherwise, the    */
    /* priority will be forced high until the VG fills the video FIFO   */
    /* by not fetching video.  That could take a while...  Note that    */
    /* we set the end priority to be 4 greater than the start.  We      */
    /* assume that the start priority has been configured by a modeset. */

    dcfg = READ_REG32(DC3_DISPLAY_CFG) & ~DC3_DCFG_VFHPEL_MASK;
    if (enable) {
        fifo = ((dcfg >> 12) & 0x0000000F) + 4;
        if (fifo > 0xF)
            fifo = 0xF;
    }
    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);
    WRITE_REG32(DC3_DISPLAY_CFG, dcfg | (fifo << 16));

    /* ENABLE OR DISABLE VIDEO */
    /* The mechanism to fetch video data is enabled first and */
    /* disabled last.                                         */

    if (enable) {
        WRITE_REG32(DC3_GENERAL_CFG, (gcfg | DC3_GCFG_VIDE));
        WRITE_VID32(DF_VIDEO_CONFIG, (vcfg | DF_VCFG_VID_EN));

        /* DISABLE COLOR KEYING IF REQUESTED BY THE USER */

        if (flags & DF_ENABLEFLAG_NOCOLORKEY) {
            /* OVERRIDE THE MODE TO COLOR KEYING */

            dcfg = READ_VID32(DF_DISPLAY_CONFIG);
            WRITE_VID32(DF_DISPLAY_CONFIG, (dcfg & ~DF_DCFG_VG_CK));

            /* DISABLE COLOR KEYING IN THE VG */

            vg_ckey = READ_REG32(DC3_COLOR_KEY);
            WRITE_REG32(DC3_COLOR_KEY, (vg_ckey & ~DC3_CLR_KEY_ENABLE));
        }
        else if (!(READ_VID32(DF_DISPLAY_CONFIG) & DF_DCFG_VG_CK)) {
            /* OTHERWISE RE-ENABLE COLOR KEYING */

            vg_ckey = READ_REG32(DC3_COLOR_KEY);
            WRITE_REG32(DC3_COLOR_KEY, (vg_ckey | DC3_CLR_KEY_ENABLE));
        }
    }
    else {
        WRITE_VID32(DF_VIDEO_CONFIG, (vcfg & ~DF_VCFG_VID_EN));
        WRITE_REG32(DC3_GENERAL_CFG, (gcfg & ~DC3_GCFG_VIDE));

        /* DISABLE COLOR KEY WINDOW WHEN VIDEO IS INACTIVE         */
        /* To mimic legacy functionality, we disble color keying   */
        /* when the video window is not active.  We will restore   */
        /* the enable when video is re-enabled if the appropriate  */
        /* bit is set in display config.                           */

        vg_ckey = READ_REG32(DC3_COLOR_KEY);
        WRITE_REG32(DC3_COLOR_KEY, (vg_ckey & ~DC3_CLR_KEY_ENABLE));
    }
    WRITE_REG32(DC3_UNLOCK, lock);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_color_key
 *
 * This routine configures the video color/chroma key mechanism.
 *--------------------------------------------------------------------------*/

int
df_set_video_color_key(unsigned long key, unsigned long mask, int graphics)
{
    unsigned long lock, vg_ckey, df_dcfg;

    vg_ckey = READ_REG32(DC3_COLOR_KEY);
    lock = READ_REG32(DC3_UNLOCK);
    df_dcfg = READ_VID32(DF_DISPLAY_CONFIG);

    WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE);

    if (graphics) {
        /* COLOR KEY - USE VG HARDWARE */
        /* Note that color key is never enabled unless a video window */
        /* is active.  This is to match legacy behavior.              */

        df_dcfg &= ~DF_DCFG_VG_CK;
        vg_ckey = (vg_ckey & 0xFF000000) | (key & 0xFFFFFF);
        if (READ_VID32(DF_VIDEO_CONFIG) & DF_VCFG_VID_EN)
            vg_ckey |= DC3_CLR_KEY_ENABLE;
        else
            vg_ckey &= ~DC3_CLR_KEY_ENABLE;

        WRITE_VID32(DF_DISPLAY_CONFIG, df_dcfg);
        WRITE_REG32(DC3_COLOR_KEY, vg_ckey);
        WRITE_REG32(DC3_COLOR_MASK, (mask & 0xFFFFFF));
    }
    else {
        /* CHROMA KEY - USE DF HARDWARE */

        df_dcfg |= DF_DCFG_VG_CK;
        vg_ckey &= ~DC3_CLR_KEY_ENABLE;

        WRITE_REG32(DC3_COLOR_KEY, vg_ckey);
        WRITE_VID32(DF_DISPLAY_CONFIG, df_dcfg);
        WRITE_VID32(DF_VIDEO_COLOR_KEY, (key & 0xFFFFFF));
        WRITE_VID32(DF_VIDEO_COLOR_MASK, (mask & 0xFFFFFF));
    }

    WRITE_REG32(DC3_UNLOCK, lock);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_palette
 *
 * This routine loads the video hardware palette.  If a NULL pointer is
 * specified, the palette is bypassed.
 *-------------------------------------------------------------------------*/

int
df_set_video_palette(unsigned long *palette)
{
    unsigned long i, entry;
    unsigned long misc, dcfg;

    /* LOAD GEODE LX VIDEO PALETTE */

    WRITE_VID32(DF_PALETTE_ADDRESS, 0);
    for (i = 0; i < 256; i++) {
        if (palette)
            entry = palette[i];
        else
            entry = i | (i << 8) | (i << 16);
        WRITE_VID32(DF_PALETTE_DATA, entry);
    }

    /* ENABLE THE VIDEO PALETTE */
    /* Ensure that the video palette has an effect by routing video data */
    /* through the palette RAM and clearing the 'Bypass Both' bit.       */

    dcfg = READ_VID32(DF_DISPLAY_CONFIG);
    misc = READ_VID32(DF_VID_MISC);

    dcfg |= DF_DCFG_GV_PAL_BYP;
    misc &= ~DF_GAMMA_BYPASS_BOTH;

    WRITE_VID32(DF_DISPLAY_CONFIG, dcfg);
    WRITE_VID32(DF_VID_MISC, misc);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_palette_entry
 *
 * This routine loads a single entry of the video hardware palette.
 *--------------------------------------------------------------------------*/

int
df_set_video_palette_entry(unsigned long index, unsigned long palette)
{
    unsigned long misc, dcfg;

    if (index > 0xFF)
        return CIM_STATUS_INVALIDPARAMS;

    /* SET A SINGLE ENTRY */

    WRITE_VID32(DF_PALETTE_ADDRESS, index);
    WRITE_VID32(DF_PALETTE_DATA, palette);

    /* ENABLE THE VIDEO PALETTE */
    /* Ensure that the video palette has an effect by routing video data */
    /* through the palette RAM and clearing the 'Bypass Both' bit.       */

    dcfg = READ_VID32(DF_DISPLAY_CONFIG);
    misc = READ_VID32(DF_VID_MISC);

    /* Ensure that the Graphic data passes through the Gamma
     * Correction RAM
     *
     * XXX is this a bug? perhaps it should be setting the bit so that video
     * data is routed, according to the description above.
     * it also mismatches df_set_video_palette().
     */
    dcfg &= ~DF_DCFG_GV_PAL_BYP;

    /* Unset the "bypass both" bit to make sure the above selection (gfx/video
     * data through gamma correction RAM) takes effect.
     */
    misc &= ~DF_GAMMA_BYPASS_BOTH;

    WRITE_VID32(DF_DISPLAY_CONFIG, dcfg);
    WRITE_VID32(DF_VID_MISC, misc);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_configure_video_cursor_color_key
 *
 * This routine configures the hardware video cursor color key mechanism.
 *--------------------------------------------------------------------------*/

int
df_configure_video_cursor_color_key(DF_VIDEO_CURSOR_PARAMS * cursor_color_key)
{
    unsigned long key;

    if (cursor_color_key->select_color2 >= 24)
        return CIM_STATUS_INVALIDPARAMS;

    key = READ_VID32(DF_CURSOR_COLOR_KEY) & DF_CURSOR_COLOR_KEY_ENABLE;
    key =
        key | (cursor_color_key->key & 0xFFFFFF) | (cursor_color_key->
                                                    select_color2 << 24);

    WRITE_VID32(DF_CURSOR_COLOR_KEY, key);
    WRITE_VID32(DF_CURSOR_COLOR_MASK, (cursor_color_key->mask & 0xFFFFFF));
    WRITE_VID32(DF_CURSOR_COLOR_1, (cursor_color_key->color1 & 0xFFFFFF));
    WRITE_VID32(DF_CURSOR_COLOR_2, (cursor_color_key->color2 & 0xFFFFFF));

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_cursor_color_key_enable
 *
 * This routine enables or disables the video cursor color key.
 *--------------------------------------------------------------------------*/

int
df_set_video_cursor_color_key_enable(int enable)
{
    unsigned long temp = READ_VID32(DF_CURSOR_COLOR_KEY);

    if (enable)
        temp |= DF_CURSOR_COLOR_KEY_ENABLE;
    else
        temp &= ~DF_CURSOR_COLOR_KEY_ENABLE;

    WRITE_VID32(DF_CURSOR_COLOR_KEY, temp);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_configure_alpha_window
 *
 * This routine configures one of the three hardware alpha regions.
 *--------------------------------------------------------------------------*/

int
df_configure_alpha_window(int window, DF_ALPHA_REGION_PARAMS * alpha_data)
{
    unsigned long vsyncend_even, vtotal_even, vactive_even;
    unsigned long hsyncend, htotal, hactive;
    unsigned long vsyncend, vtotal, vactive;
    unsigned long alpha_ctl, pos;
    unsigned long hadjust, vadjust;
    unsigned long y, height;
    unsigned long xstart, xend;
    unsigned long ystart, yend;
    unsigned long x_copy, width_copy;
    unsigned long y_copy, height_copy;
    unsigned long scale, src, misc;
    unsigned long gfxscale, fbactive;
    unsigned long color;

    if (window > 2)
        return CIM_STATUS_INVALIDPARAMS;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    hactive = (READ_REG32(DC3_H_ACTIVE_TIMING) & 0xFFF) + 1;
    vactive = (READ_REG32(DC3_V_ACTIVE_TIMING) & 0xFFF) + 1;

    /* APPLY THE GRAPHICS SCALE */

    width_copy = alpha_data->width;
    height_copy = alpha_data->height;
    x_copy = alpha_data->x;
    y_copy = alpha_data->y;

    misc = READ_VID32(DF_VID_MISC);
    if (misc & DF_USER_IMPLICIT_SCALING) {
        gfxscale = READ_REG32(DC3_GFX_SCALE);
        fbactive = READ_REG32(DC3_FB_ACTIVE);

        /* REVERSE ENGINEER THE SCALE FACTOR */

        scale = gfxscale & 0xFFFF;
        src = (fbactive >> 16) + 1;
        if (scale != 0x4000) {
            width_copy = width_copy * (((0x4000 * src) / scale) + 1);
            width_copy /= src;
            x_copy = x_copy * (((0x4000 * src) / scale) + 1);
            x_copy /= src;
        }

        scale = gfxscale >> 16;
        src = (fbactive & 0xFFFF) + 1;
        if (scale != 0x4000) {
            height_copy = height_copy * (((0x4000 * src) / scale) + 1);
            height_copy /= src;
            y_copy = y_copy * (((0x4000 * src) / scale) + 1);
            y_copy /= src;
        }
    }

    /* SET PRIORITY */
    /* Priority is the only alpha parameter that is not in a register that */
    /* can be indexed based on the alpha window number.                    */

    pos = 16 + (window << 1);
    alpha_ctl = READ_VID32(DF_VID_ALPHA_CONTROL) & ~(3L << pos);
    alpha_ctl |= (alpha_data->priority & 3) << pos;
    WRITE_VID32(DF_VID_ALPHA_CONTROL, alpha_ctl);

    /* HANDLE INTERLACED MODES */

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        vsyncend_even = ((READ_REG32(DC3_V_SYNC_EVEN) >> 16) & 0xFFF) + 1;
        vtotal_even = ((READ_REG32(DC3_V_ACTIVE_EVEN) >> 16) & 0xFFF) + 1;
        vactive_even = (READ_REG32(DC3_V_ACTIVE_EVEN) & 0xFFF) + 1;

        y = y_copy >> 1;

        /* SET Y POSITION FOR ODD FIELD */

        height = (height_copy + 1) >> 1;
        vadjust = vtotal_even - vsyncend_even + 1;

        ystart = y + vadjust;
        yend = y + vadjust + height;

        if (yend > (vactive + vadjust))
            yend = vactive + vadjust;

        WRITE_VID32((DF_ALPHA_YPOS_1 + (window << 5)), (ystart | (yend << 16)));

        /* SET Y POSITION FOR EVEN FIELD */

        height = height_copy >> 1;
        vadjust = vtotal - vsyncend + 1;

        ystart = y + vadjust;
        yend = y + vadjust + height;

        if (yend > (vactive_even + vadjust))
            yend = vactive_even + vadjust;

        WRITE_VID32((DF_VID_ALPHA_Y_EVEN_1 + (window << 3)),
                    (ystart | (yend << 16)));
    }
    else {
        y = y_copy;
        height = height_copy;
        vadjust = vtotal - vsyncend + 1;

        ystart = y + vadjust;
        yend = y + vadjust + height;

        if (yend > (vactive + vadjust))
            yend = vactive + vadjust;

        WRITE_VID32((DF_ALPHA_YPOS_1 + (window << 5)), (ystart | (yend << 16)));
    }

    /* SET ALPHA X POSITION */
    /* The x position is the same for both the odd and even fields. */

    hadjust = htotal - hsyncend - 2;

    xstart = x_copy + hadjust;
    xend = x_copy + hadjust + width_copy;

    if (xend > (hactive + hadjust))
        xend = hactive + hadjust;

    WRITE_VID32((DF_ALPHA_XPOS_1 + (window << 5)), (xstart | (xend << 16)));

    /* SET COLOR REGISTER */

    color = alpha_data->color & 0xFFFFFF;
    if (alpha_data->flags & DF_ALPHAFLAG_COLORENABLED)
        color |= DF_ALPHA_COLOR_ENABLE;

    WRITE_VID32((DF_ALPHA_COLOR_1 + (window << 5)), color);

    /* SET ALPHA VALUE, DELTA AND PER PIXEL */

    alpha_ctl = READ_VID32(DF_ALPHA_CONTROL_1 + (window << 5)) &
        DF_ACTRL_WIN_ENABLE;
    alpha_ctl |= (alpha_data->alpha_value & 0xFF) | DF_ACTRL_LOAD_ALPHA |
        (((unsigned long) alpha_data->delta & 0xFF) << 8);
    if (alpha_data->flags & DF_ALPHAFLAG_PERPIXELENABLED)
        alpha_ctl |= DF_ACTRL_PERPIXEL_EN;

    WRITE_VID32((DF_ALPHA_CONTROL_1 + (window << 5)), alpha_ctl);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_alpha_window_enable
 *
 * This routine enables or disables one of the three hardware alpha regions.
 *--------------------------------------------------------------------------*/

int
df_set_alpha_window_enable(int window, int enable)
{
    unsigned long alpha_ctl;

    if (window > 2)
        return CIM_STATUS_INVALIDPARAMS;

    alpha_ctl = READ_VID32(DF_ALPHA_CONTROL_1 + (window << 5));
    if (enable)
        alpha_ctl |= DF_ACTRL_WIN_ENABLE;
    else
        alpha_ctl &= ~DF_ACTRL_WIN_ENABLE;
    WRITE_VID32((DF_ALPHA_CONTROL_1 + (window << 5)), alpha_ctl);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_no_ck_outside_alpha
 *
 * This function affects how color/chroma keying is performed inside the video
 * window.
 *
 * If enable is 1, color/chroma key comparison is performed only inside
 * the enabled alpha windows. Outside the enabled alpha windows, video
 * is displayed if color keying is enabled, or graphics is displayed if
 * chroma keying is enabled.
 * If enable is 0, color/chroma key comparison is performed inside the
 * entire video window.
 *--------------------------------------------------------------------------*/

int
df_set_no_ck_outside_alpha(int enable)
{
    unsigned long value;

    value = READ_VID32(DF_VID_ALPHA_CONTROL);
    if (enable)
        value |= DF_NO_CK_OUTSIDE_ALPHA;
    else
        value &= ~DF_NO_CK_OUTSIDE_ALPHA;
    WRITE_VID32(DF_VID_ALPHA_CONTROL, value);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_video_request
 *
 * This routine sets the horizontal (pixel) and vertical (line) video request
 * values.
 *--------------------------------------------------------------------------*/

int
df_set_video_request(unsigned long x, unsigned long y)
{
    unsigned long htotal, hsyncend;
    unsigned long vtotal, vsyncend;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;

    /* SET DISPLAY FILTER VIDEO REQUEST */

    x += htotal - hsyncend - 2;
    y += vtotal - vsyncend + 1;

    if (x >= 0x1000 || y >= 0x800)
        return CIM_STATUS_INVALIDPARAMS;

    WRITE_VID32(DF_VIDEO_REQUEST, (y | (x << 16)));
    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_output_color_space
 *
 * This routine sets the color space used when combining graphics and video.
 *--------------------------------------------------------------------------*/

int
df_set_output_color_space(int color_space)
{
    unsigned long alpha_ctl;

    alpha_ctl = READ_VID32(DF_VID_ALPHA_CONTROL);

    alpha_ctl &= ~(DF_CSC_GRAPHICS_RGB_TO_YUV | DF_CSC_VIDEO_YUV_TO_RGB |
                   DF_HD_GRAPHICS | DF_YUV_CSC_EN | DF_ALPHA_DRGB);

    /* OUTPUT IS RGB */
    /* Enable YUV->RGB CSC if necessary and enable alpha output if  */
    /* requested.                                                   */

    if (color_space == DF_OUTPUT_RGB || color_space == DF_OUTPUT_ARGB) {
        if (!(alpha_ctl & DF_VIDEO_INPUT_IS_RGB))
            alpha_ctl |= DF_CSC_VIDEO_YUV_TO_RGB;

        if (color_space == DF_OUTPUT_ARGB)
            alpha_ctl |= DF_ALPHA_DRGB;
    }

    /* OUTPUT IS YUV */
    /* Enable YUV->YUV CSC if necessary and enable RGB->YUV CSC. */

    else if (color_space == DF_OUTPUT_SDTV || color_space == DF_OUTPUT_HDTV) {
        alpha_ctl |= DF_CSC_GRAPHICS_RGB_TO_YUV;

        if (((alpha_ctl & DF_HD_VIDEO) && color_space == DF_OUTPUT_SDTV) ||
            (!(alpha_ctl & DF_HD_VIDEO) && color_space == DF_OUTPUT_HDTV)) {
            alpha_ctl |= DF_YUV_CSC_EN;
        }

        if (color_space == DF_OUTPUT_HDTV)
            alpha_ctl |= DF_HD_GRAPHICS;
    }
    else
        return CIM_STATUS_INVALIDPARAMS;

    WRITE_VID32(DF_VID_ALPHA_CONTROL, alpha_ctl);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_set_output_path
 *
 * This routine changes the current output path in the display filter.
 *--------------------------------------------------------------------------*/

int
df_set_output_path(int format)
{
    unsigned long panel_tim2, panel_pm;
    unsigned long output = 0;
    Q_WORD msr_value;

    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
    msr_value.low &= ~(DF_SIMULTANEOUS_CRT_FP | DF_CONFIG_OUTPUT_MASK);
    panel_tim2 = READ_VID32(DF_VIDEO_PANEL_TIM2);
    panel_pm = READ_VID32(DF_POWER_MANAGEMENT);

    if (format == DF_DISPLAY_CRT) {
        /* SiBZ #4188 */
        /* When CRT output is selected, the DF drives the DISP_EN signal   */
        /* with the CRT display enable.  As a consequence, systems that    */
        /* wire the DISP_EN signal to the TFT backlight control will not   */
        /* be able to set CRT-only output without leaving the backlight    */
        /* enabled.  To workaround this issue, we are setting simultaneous */
        /* TFT/CRT and disabling the TFT logic.  The only caveat to this   */
        /* is that some TFT pins are shared with VIP 601 pins.  VIP 601    */
        /* will thus not work when in this pseudo-CRT mode.  To address    */
        /* THAT issue, normal CRT mode sets (in cim_vg.c) will set CRT     */
        /* as the DF output format.  This will allow VIP 601 on CRT-only   */
        /* systems without a TFT attached.                                 */

        panel_pm &= ~DF_PM_PANEL_ON;
        panel_tim2 |= DF_PMTIM2_TFT_PASSHTHROUGH;
        output = DF_OUTPUT_PANEL | DF_SIMULTANEOUS_CRT_FP;
    }
    else if (format == DF_DISPLAY_FP || format == DF_DISPLAY_CRT_FP) {
        panel_pm |= DF_PM_PANEL_ON;
        panel_tim2 &= ~DF_PMTIM2_TFT_PASSHTHROUGH;

        if (format == DF_DISPLAY_FP)
            output = DF_OUTPUT_PANEL;
        else if (format == DF_DISPLAY_CRT_FP)
            output = DF_OUTPUT_PANEL | DF_SIMULTANEOUS_CRT_FP;
    }
    else {
        switch (format) {
        case DF_DISPLAY_VOP:
            output = DF_OUTPUT_VOP;
            break;
        case DF_DISPLAY_DRGB:
            output = DF_OUTPUT_DRGB;
            break;
        case DF_DISPLAY_CRT_DRGB:
            output = DF_OUTPUT_DRGB | DF_SIMULTANEOUS_CRT_FP;
            break;
        default:
            return CIM_STATUS_INVALIDPARAMS;
        }
    }
    msr_value.low |= output;
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value);
    WRITE_VID32(DF_VIDEO_PANEL_TIM2, panel_tim2);
    WRITE_VID32(DF_POWER_MANAGEMENT, panel_pm);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_test_video_flip_status
 *
 * This routine tests if a new video offset has been latched.
 *--------------------------------------------------------------------------*/

unsigned long
df_test_video_flip_status(void)
{
    return (READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_VFLIP);
}

/*---------------------------------------------------------------------------
 * df_save_state
 *
 * This routine saves all persistent DF state information.
 *--------------------------------------------------------------------------*/

int
df_save_state(DF_SAVE_RESTORE * df_state)
{
    unsigned long i;

    /* READ ALL DF REGISTERS */

    df_state->vcfg = READ_VID32(DF_VIDEO_CONFIG);
    df_state->dcfg = READ_VID32(DF_DISPLAY_CONFIG);
    df_state->video_x = READ_VID32(DF_VIDEO_X_POS);
    df_state->video_y = READ_VID32(DF_VIDEO_Y_POS);
    df_state->video_scaler = READ_VID32(DF_VIDEO_SCALER);
    df_state->video_color_key = READ_VID32(DF_VIDEO_COLOR_KEY);
    df_state->video_color_mask = READ_VID32(DF_VIDEO_COLOR_MASK);
    df_state->sat_limit = READ_VID32(DF_SATURATION_LIMIT);
    df_state->vid_misc = READ_VID32(DF_VID_MISC);
    df_state->video_yscale = READ_VID32(DF_VIDEO_YSCALE);
    df_state->video_xscale = READ_VID32(DF_VIDEO_XSCALE);
    df_state->vid_alpha_control = READ_VID32(DF_VID_ALPHA_CONTROL);
    df_state->cursor_key = READ_VID32(DF_CURSOR_COLOR_KEY);
    df_state->cursor_mask = READ_VID32(DF_CURSOR_COLOR_MASK);
    df_state->cursor_color1 = READ_VID32(DF_CURSOR_COLOR_1);
    df_state->cursor_color2 = READ_VID32(DF_CURSOR_COLOR_2);
    df_state->alpha_xpos1 = READ_VID32(DF_ALPHA_XPOS_1);
    df_state->alpha_ypos1 = READ_VID32(DF_ALPHA_YPOS_1);
    df_state->alpha_color1 = READ_VID32(DF_ALPHA_COLOR_1);
    df_state->alpha_control1 = READ_VID32(DF_ALPHA_CONTROL_1);
    df_state->alpha_xpos2 = READ_VID32(DF_ALPHA_XPOS_2);
    df_state->alpha_ypos2 = READ_VID32(DF_ALPHA_YPOS_2);
    df_state->alpha_color2 = READ_VID32(DF_ALPHA_COLOR_2);
    df_state->alpha_control2 = READ_VID32(DF_ALPHA_CONTROL_2);
    df_state->alpha_xpos3 = READ_VID32(DF_ALPHA_XPOS_3);
    df_state->alpha_ypos3 = READ_VID32(DF_ALPHA_YPOS_3);
    df_state->alpha_color3 = READ_VID32(DF_ALPHA_COLOR_3);
    df_state->alpha_control3 = READ_VID32(DF_ALPHA_CONTROL_3);
    df_state->vid_request = READ_VID32(DF_VIDEO_REQUEST);
    df_state->vid_ypos_even = READ_VID32(DF_VID_YPOS_EVEN);
    df_state->alpha_ypos_even1 = READ_VID32(DF_VID_ALPHA_Y_EVEN_1);
    df_state->alpha_ypos_even2 = READ_VID32(DF_VID_ALPHA_Y_EVEN_2);
    df_state->alpha_ypos_even3 = READ_VID32(DF_VID_ALPHA_Y_EVEN_3);
    df_state->panel_tim1 = READ_VID32(DF_VIDEO_PANEL_TIM1);
    df_state->panel_tim2 = READ_VID32(DF_VIDEO_PANEL_TIM2);
    df_state->panel_pm = READ_VID32(DF_POWER_MANAGEMENT);
    df_state->panel_dither = READ_VID32(DF_DITHER_CONTROL);

    /* READ DF PALETTE */

    WRITE_VID32(DF_PALETTE_ADDRESS, 0);
    for (i = 0; i < 256; i++)
        df_state->palette[i] = READ_VID32(DF_PALETTE_DATA);

    /* READ FILTER COEFFICIENTS */

    for (i = 0; i < 512; i++)
        df_state->coefficients[i] = READ_VID32(DF_COEFFICIENT_BASE + (i << 2));

    /* READ ALL DF MSRS */

    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CAP, &(df_state->msr_cap));
    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG,
               &(df_state->msr_config));
    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_SMI, &(df_state->msr_smi));
    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_ERROR,
               &(df_state->msr_error));
    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_PM, &(df_state->msr_pm));
    msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_DIAG,
               &(df_state->msr_diag));
    msr_read64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF,
               &(df_state->msr_df_diag));
    msr_read64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL, &(df_state->msr_pad_sel));

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_restore_state
 *
 * This routine restores all persistent DF state information.
 *--------------------------------------------------------------------------*/

int
df_restore_state(DF_SAVE_RESTORE * df_state)
{
    unsigned long i;

    /* CLEAR VCFG AND DCFG */

    WRITE_VID32(DF_VIDEO_CONFIG, 0);
    WRITE_VID32(DF_DISPLAY_CONFIG, 0);

    /* RESTORE DF MSRS */

    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CAP, &(df_state->msr_cap));
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG,
                &(df_state->msr_config));
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_SMI, &(df_state->msr_smi));
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_ERROR,
                &(df_state->msr_error));
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_PM, &(df_state->msr_pm));
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_DIAG,
                &(df_state->msr_diag));
    msr_write64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF,
                &(df_state->msr_df_diag));
    msr_write64(MSR_DEVICE_GEODELX_DF, DF_MSR_PAD_SEL,
                &(df_state->msr_pad_sel));

    /* RESTORE ALL DF REGISTERS */

    WRITE_VID32(DF_VIDEO_X_POS, df_state->video_x);
    WRITE_VID32(DF_VIDEO_Y_POS, df_state->video_y);
    WRITE_VID32(DF_VIDEO_SCALER, df_state->video_scaler);
    WRITE_VID32(DF_VIDEO_COLOR_KEY, df_state->video_color_key);
    WRITE_VID32(DF_VIDEO_COLOR_MASK, df_state->video_color_mask);
    WRITE_VID32(DF_SATURATION_LIMIT, df_state->sat_limit);
    WRITE_VID32(DF_VID_MISC, df_state->vid_misc);
    WRITE_VID32(DF_VIDEO_YSCALE, df_state->video_yscale);
    WRITE_VID32(DF_VIDEO_XSCALE, df_state->video_xscale);
    WRITE_VID32(DF_VID_ALPHA_CONTROL, df_state->vid_alpha_control);
    WRITE_VID32(DF_CURSOR_COLOR_KEY, df_state->cursor_key);
    WRITE_VID32(DF_CURSOR_COLOR_MASK, df_state->cursor_mask);
    WRITE_VID32(DF_CURSOR_COLOR_1, df_state->cursor_color1);
    WRITE_VID32(DF_CURSOR_COLOR_2, df_state->cursor_color2);
    WRITE_VID32(DF_ALPHA_XPOS_1, df_state->alpha_xpos1);
    WRITE_VID32(DF_ALPHA_YPOS_1, df_state->alpha_ypos1);
    WRITE_VID32(DF_ALPHA_COLOR_1, df_state->alpha_color1);
    WRITE_VID32(DF_ALPHA_CONTROL_1, df_state->alpha_control1);
    WRITE_VID32(DF_ALPHA_XPOS_2, df_state->alpha_xpos2);
    WRITE_VID32(DF_ALPHA_YPOS_2, df_state->alpha_ypos2);
    WRITE_VID32(DF_ALPHA_COLOR_2, df_state->alpha_color2);
    WRITE_VID32(DF_ALPHA_CONTROL_2, df_state->alpha_control1);
    WRITE_VID32(DF_ALPHA_XPOS_3, df_state->alpha_xpos3);
    WRITE_VID32(DF_ALPHA_YPOS_3, df_state->alpha_ypos3);
    WRITE_VID32(DF_ALPHA_COLOR_3, df_state->alpha_color3);
    WRITE_VID32(DF_ALPHA_CONTROL_3, df_state->alpha_control3);
    WRITE_VID32(DF_VIDEO_REQUEST, df_state->vid_request);
    WRITE_VID32(DF_VID_YPOS_EVEN, df_state->vid_ypos_even);
    WRITE_VID32(DF_VID_ALPHA_Y_EVEN_1, df_state->alpha_ypos_even1);
    WRITE_VID32(DF_VID_ALPHA_Y_EVEN_2, df_state->alpha_ypos_even2);
    WRITE_VID32(DF_VID_ALPHA_Y_EVEN_3, df_state->alpha_ypos_even3);
    WRITE_VID32(DF_VIDEO_PANEL_TIM1, df_state->panel_tim1);
    WRITE_VID32(DF_VIDEO_PANEL_TIM2, df_state->panel_tim2);
    WRITE_VID32(DF_POWER_MANAGEMENT, df_state->panel_pm);
    WRITE_VID32(DF_DITHER_CONTROL, df_state->panel_dither);

    /* RESTORE DF PALETTE */

    WRITE_VID32(DF_PALETTE_ADDRESS, 0);
    for (i = 0; i < 256; i++)
        WRITE_VID32(DF_PALETTE_DATA, df_state->palette[i]);

    /* RESTORE FILTER COEFFICIENTS */

    for (i = 0; i < 512; i++)
        WRITE_VID32(DF_COEFFICIENT_BASE + (i << 2), df_state->coefficients[i]);

    /* RESTORE DCFG AND VCFG */

    WRITE_VID32(DF_DISPLAY_CONFIG, df_state->dcfg);
    WRITE_VID32(DF_VIDEO_CONFIG, df_state->vcfg);

    return CIM_STATUS_OK;
}

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * CIMARRON DF READ ROUTINES
 * These routines are included for use in diagnostics or when debugging.  They
 * can be optionally excluded from a project.
 *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

#if CIMARRON_INCLUDE_DF_READ_ROUTINES

/*---------------------------------------------------------------------------
 * df_read_composite_crc
 *
 * This routine reads the CRC of the combination of graphics/video data.  This
 * CRC checks data immediately before the CRT DACs.
 *--------------------------------------------------------------------------*/

unsigned long
df_read_composite_crc(int crc_source)
{
    Q_WORD msr_value;
    unsigned long crc;
    unsigned long interlaced;
    unsigned long line, field;
    unsigned long timeout = 1000;

    if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN))
        return 0xFFFFFFFF;

    /* ENABLE 32-BIT CRCS */

    msr_read64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF, &msr_value);
    msr_value.low |= DF_DIAG_32BIT_CRC;
    msr_write64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF, &msr_value);

    /* RESET THE CRC */

    WRITE_VID32(DF_VID_CRC, 0);

    /* WAIT FOR THE RESET TO BE LATCHED */

    while ((READ_VID32(DF_VID_CRC32) != 0x00000001) && timeout)
        timeout--;

    /* 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 & DF_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 & DF_CRC_SOURCE_EVEN)
            return 0xFFFFFFFF;
    }

    /* ENABLE THE CRC */

    WRITE_VID32(DF_VID_CRC, 1);

    /* WAIT FOR THE CRC TO BE COMPLETED */

    while (!(READ_VID32(DF_VID_CRC) & 4));

    crc = READ_VID32(DF_VID_CRC32);

    return crc;
}

/*---------------------------------------------------------------------------
 * df_read_composite_window_crc
 *
 * This routine reads the CRC of a rectangular subsection of the combination
 * of graphics/video data.
 *--------------------------------------------------------------------------*/

unsigned long
df_read_composite_window_crc(unsigned long x, unsigned long y,
                             unsigned long width, unsigned long height,
                             int source)
{
    Q_WORD msr_value;
    unsigned long interlaced;
    unsigned long line, field;
    unsigned long crc = 0;
    unsigned long hsyncend, htotal, hsyncstart;
    unsigned long vsyncend, vtotal, vsyncstart;
    unsigned long hblankstart, hactive;
    unsigned long vblankstart, vactive;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    hsyncstart = (READ_REG32(DC3_H_SYNC_TIMING) & 0xFFF) + 1;
    hactive = (READ_REG32(DC3_H_ACTIVE_TIMING) & 0xFFF) + 1;
    hblankstart = (READ_REG32(DC3_H_BLANK_TIMING) & 0xFFF) + 1;
    if ((interlaced = (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN)) &&
        !(source & DF_CRC_SOURCE_EVEN)) {
        vsyncend = ((READ_REG32(DC3_V_SYNC_EVEN) >> 16) & 0xFFF) + 1;
        vtotal = ((READ_REG32(DC3_V_ACTIVE_EVEN) >> 16) & 0xFFF) + 1;
        vsyncstart = (READ_REG32(DC3_V_SYNC_EVEN) & 0xFFF) + 1;
        vactive = (READ_REG32(DC3_V_ACTIVE_EVEN) & 0xFFF) + 1;
        vblankstart = (READ_REG32(DC3_V_BLANK_EVEN) & 0xFFF) + 1;
    }
    else {
        vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
        vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
        vsyncstart = (READ_REG32(DC3_V_SYNC_TIMING) & 0xFFF) + 1;
        vactive = (READ_REG32(DC3_V_ACTIVE_TIMING) & 0xFFF) + 1;
        vblankstart = (READ_REG32(DC3_V_BLANK_TIMING) & 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);

    /* ENABLE HW CLOCK GATING AND SET GLCP CLOCK TO DOT CLOCK */

    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);

    /* USE H4 FUNCTION A FOR HSYNC AND H4 FUNCTION B FOR NOT HSYNC */
    /* HSYNC is bit 30 for the DF                                  */

    msr_value.high = 0x00000001;
    msr_value.low = 0xE0000FF0;
    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 54 for VG and bit 29 for DF                    */

    msr_value.high = 0x00000000;
    msr_value.low = 0x001D55AA;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_H0CTL + 3, &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 HSYNC LOW) */
    /* Increment H. Counter           */
    /* Note: HSync = H4               */

    msr_value.high = 0x00080000;
    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)
     * CRC into REGB
     */

    msr_value.high = 0x00000000;
    msr_value.low = 0x10C20120;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_SETN0CTL + 4, &msr_value);

    /* COMPARATOR 0 VALUE                                   */
    /* Value = xstart + (htotal - hsync_end) - 1            */
    /* The value will be adjusted for a border if necessary */

    msr_value.low = x + htotal - hsyncend - 1;
    if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DCEN)
        msr_value.low -= hblankstart - hactive;
    msr_value.low--;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0, &msr_value);

    /* COMPARATOR 1 VALUE                                    */
    /* Value = xstart + (htotal - hsync_end - 1) - 1 + width */

    msr_value.low += width - 1;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0 + 2, &msr_value);

    /* COMPARATOR 2 VALUE                 */
    /* Value = ystart + vtotal - vsyncend */

    msr_value.low = (y + vtotal - vsyncend) << 16;
    if (READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_DCEN)
        msr_value.low -= (vblankstart - vactive) << 16;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_CMPVAL0 + 4, &msr_value);

    /* COMPARATOR 3 VALUE                              */
    /* Value = ystart + vtotal - vsyncend + height - 1 */

    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 only 24 bits of data are CRCed */

    msr_value.low = 0x00FFFFFF;
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGBMASK, &msr_value);

    /* SET REGA LIMITS                              */
    /* Lower counter uses htotal - sync_time - 1.   */
    /* Upper counter is 0xFFFF to prevent rollover. */

    msr_value.low = 0xFFFF0000 | (htotal - (hsyncend - hsyncstart) - 1);
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_REGAVAL, &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 8
     * Increment h.counter if xstate = 10 and HSync is low.
     */

    msr_value.low = 0x000A00A0;
    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);

    /* 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 + 17, &msr_value);
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 18, &msr_value);
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 19, &msr_value);
    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_ACTION0 + 20, &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 (source & DF_CRC_SOURCE_EVEN)
        field = 0;
    else
        field = DC3_LNCNT_EVEN_FIELD;

    if (interlaced) {
        /* 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 (source & DF_CRC_SOURCE_EVEN)
            return 0xFFFFFFFF;
    }

    /* CONFIGURE DISPLAY FILTER TO LOAD DATA ONTO LOWER 32-BITS */

    msr_value.high = 0;
    msr_value.low = 0x0000800B;
    msr_write64(MSR_DEVICE_GEODELX_DF, 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 DF DIAG BUS OUTPUTS */

    msr_value.low = 0x00000000;
    msr_value.high = 0x00000000;
    msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_DIAG, &msr_value);

    /* DISABLE GLCP ACTIONS */

    msr_write64(MSR_DEVICE_GEODELX_GLCP, GLCP_DIAGCTL, &msr_value);

    return crc;
}

/*---------------------------------------------------------------------------
 * df_read_panel_crc
 *
 * This routine reads the CRC for a frame of data after the panel dithering
 * logic.
 *--------------------------------------------------------------------------*/

unsigned long
df_read_panel_crc(void)
{
    Q_WORD msr_value;
    unsigned long timeout = 1000;

    if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN))
        return 0xFFFFFFFF;

    /* ENABLE 32-BIT CRCS */

    msr_read64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF, &msr_value);
    msr_value.low |= DF_DIAG_32BIT_CRC;
    msr_write64(MSR_DEVICE_GEODELX_DF, DF_MBD_MSR_DIAG_DF, &msr_value);

    /* RESET CRC */

    WRITE_VID32(DF_PANEL_CRC, 0);

    /* WAIT FOR THE RESET TO BE LATCHED */

    while ((READ_VID32(DF_PANEL_CRC32) != 0x00000001) && timeout)
        timeout--;

    WRITE_VID32(DF_PANEL_CRC, 1);

    /* WAIT FOR THE CRC TO BE COMPLETED */

    while (!(READ_VID32(DF_PANEL_CRC) & 4));

    return READ_VID32(DF_PANEL_CRC32);
}

/*---------------------------------------------------------------------------
 * df_get_video_enable
 *
 * This routine reads the enable status of the video overlay.
 *--------------------------------------------------------------------------*/

int
df_get_video_enable(int *enable, unsigned long *flags)
{
    *enable = 0;
    *flags = 0;
    if (READ_VID32(DF_VIDEO_CONFIG) & DF_VCFG_VID_EN) {
        *enable = 1;

        /* CHECK FOR COLOR KEY DISABLED */
        /* Color keying can be completely disabled when video is enabled to */
        /* allow unhindered per-pixel alpha blending.  As color keying is   */
        /* always disabled when video is disabled, it is only possible to   */
        /* test for this condition when video is enabled.                   */

        if (!(READ_VID32(DF_DISPLAY_CONFIG) & DF_DCFG_VG_CK) &&
            !(READ_REG32(DC3_COLOR_KEY) & DC3_CLR_KEY_ENABLE)) {
            *flags = DF_ENABLEFLAG_NOCOLORKEY;
        }
    }

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_source_configuration
 *
 * This routine reads the current configuration of the source buffers for the
 * video overlay.
 *--------------------------------------------------------------------------*/

int
df_get_video_source_configuration(DF_VIDEO_SOURCE_PARAMS * video_source_odd,
                                  DF_VIDEO_SOURCE_PARAMS * video_source_even)
{
    unsigned long format, temp;
    unsigned long size;

    /* READ VIDEO FORMAT */

    temp = READ_VID32(DF_VIDEO_CONFIG);

    format = (temp >> 2) & 3;
    if (temp & DF_VCFG_4_2_0_MODE)
        format |= 4;
    else if (READ_VID32(DF_VID_ALPHA_CONTROL) & DF_VIDEO_INPUT_IS_RGB)
        format |= 8;
    video_source_odd->video_format = format;

    /* CHECK IF SOURCE IS HD VIDEO */

    if (READ_VID32(DF_VID_ALPHA_CONTROL) & DF_HD_VIDEO)
        video_source_odd->flags = DF_SOURCEFLAG_HDTVSOURCE;
    else
        video_source_odd->flags = 0;

    /* READ SCALING ALGORITHM */

    if (READ_VID32(DF_VID_MISC) & DF_USER_IMPLICIT_SCALING)
        video_source_odd->flags |= DF_SOURCEFLAG_IMPLICITSCALING;

    /* READ VIDEO PITCH */

    temp = READ_REG32(DC3_VID_YUV_PITCH);
    video_source_odd->y_pitch = (temp & 0xFFFF) << 3;
    video_source_odd->uv_pitch = (temp >> 16) << 3;

    /* READ VIDEO SIZE */

    temp = READ_VID32(DF_VIDEO_CONFIG);
    size = (temp >> 8) & 0xFF;
    if (temp & DF_VCFG_LINE_SIZE_BIT8)
        size |= 0x100;
    if (temp & DF_VCFG_LINE_SIZE_BIT9)
        size |= 0x200;

    video_source_odd->width = size << 1;
    video_source_odd->height = READ_VID32(DF_VIDEO_SCALER) & 0x7FF;

    /* READ VIDEO OFFSETS */

    video_source_odd->y_offset = READ_REG32(DC3_VID_Y_ST_OFFSET) & 0xFFFFFFF;
    video_source_odd->u_offset = READ_REG32(DC3_VID_U_ST_OFFSET) & 0xFFFFFFF;
    video_source_odd->v_offset = READ_REG32(DC3_VID_V_ST_OFFSET) & 0xFFFFFFF;

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        video_source_even->y_offset =
            READ_REG32(DC3_VID_EVEN_Y_ST_OFFSET) & 0xFFFFFFF;
        video_source_even->u_offset =
            READ_REG32(DC3_VID_EVEN_U_ST_OFFSET) & 0xFFFFFFF;
        video_source_even->v_offset =
            READ_REG32(DC3_VID_EVEN_V_ST_OFFSET) & 0xFFFFFFF;
    }

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_position
 *
 * This routine reads the current position of the video overlay.
 *--------------------------------------------------------------------------*/

int
df_get_video_position(DF_VIDEO_POSITION * video_window)
{
    unsigned long xreg, yreg, dst_clip, clip;
    unsigned long height;
    unsigned long xend, yend;
    unsigned long hsyncend, htotal;
    unsigned long vsyncend, vtotal;
    unsigned long hadjust, vadjust;
    unsigned long misc, gfxscale;
    unsigned long temp;
    long xstart, ystart;

    video_window->flags = DF_POSFLAG_DIRECTCLIP;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;

    /* ODD FIELD START COUNTS FROM THE EVEN FIELD TIMINGS */
    /* We assume that the even field y position is always programmed */
    /* to be just after the odd field.                               */

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        vsyncend = ((READ_REG32(DC3_V_SYNC_EVEN) >> 16) & 0xFFF) + 1;
        vtotal = ((READ_REG32(DC3_V_ACTIVE_EVEN) >> 16) & 0xFFF) + 1;
    }
    else {
        vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
        vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    }

    hadjust = htotal - hsyncend - 14;
    vadjust = vtotal - vsyncend + 1;

    xreg = READ_VID32(DF_VIDEO_X_POS);
    yreg = READ_VID32(DF_VIDEO_Y_POS);

    xstart = (xreg & 0xFFF) - hadjust;
    ystart = (yreg & 0x7FF) - vadjust;
    xend = ((xreg >> 16) & 0xFFF) - hadjust;
    yend = ((yreg >> 16) & 0x7FF) - vadjust;

    height = yend - ystart;

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        /* Y COORDINATE IS ACTUALLY 2X THE ODD FIELD START */

        ystart <<= 1;

        /* CALCULATE THE EXACT VIDEO HEIGHT */
        /* The height of the video window is the sum of the */
        /* odd and even field heights.                      */

        yreg = READ_VID32(DF_VID_YPOS_EVEN);
        height += ((yreg >> 16) & 0x7FF) - (yreg & 0x7FF);
    }

    clip = ((READ_VID32(DF_VIDEO_CONFIG) >> 16) & 0x1FF) << 2;

    /* ADJUST FOR CLIPPING VALUES THAT ARE NOT FOUR-PIXEL ALIGNED */

    dst_clip = 0;
    if (xstart < 0) {
        dst_clip += -xstart;
        xstart = 0;
    }

    /* REVERSE THE GRAPHICS SCALE */

    misc = READ_VID32(DF_VID_MISC);
    if (misc & DF_USER_IMPLICIT_SCALING) {
        gfxscale = READ_REG32(DC3_GFX_SCALE);

        if (gfxscale != 0x40004000) {
            temp = ystart + height;
            temp = (temp * (gfxscale >> 16)) / 0x4000;

            xstart = (xstart * (gfxscale & 0xFFFF)) / 0x4000;
            xend = (xend * (gfxscale & 0xFFFF)) / 0x4000;
            ystart = (ystart * (gfxscale >> 16)) / 0x4000;
            height = temp - ystart;
        }
    }

    video_window->left_clip = clip;
    video_window->dst_clip = dst_clip;
    video_window->x = xstart;
    video_window->y = ystart;
    video_window->width = xend - xstart;
    video_window->height = height;

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_scale
 *
 * This routine reads the current scale values for video scaling.
 *--------------------------------------------------------------------------*/

int
df_get_video_scale(unsigned long *x_scale, unsigned long *y_scale)
{
    *x_scale = READ_VID32(DF_VIDEO_XSCALE) & 0x000FFFFF;
    *y_scale = READ_VID32(DF_VIDEO_YSCALE) & 0x000FFFFF;
    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_filter_coefficients
 *
 * This routine reads the coefficients for the video scaler/filter.
 *--------------------------------------------------------------------------*/

int
df_get_video_filter_coefficients(long taps[][4], int *phase256)
{
    unsigned long i, temp;
    long coeff;

    if (READ_VID32(DF_VIDEO_SCALER) & DF_SCALE_128_PHASES)
        *phase256 = 0;
    else
        *phase256 = 1;

    for (i = 0; i < 256; i++) {
        temp = READ_VID32(DF_COEFFICIENT_BASE + (i << 3));

        /* TAP 0 */

        coeff = temp & 0x7FFF;
        if (temp & 0x8000)
            coeff = -coeff;
        taps[i][0] = coeff;

        /* TAP 1 */

        temp >>= 16;
        coeff = temp & 0x7FFF;
        if (temp & 0x8000)
            coeff = -coeff;
        taps[i][1] = coeff;

        temp = READ_VID32(DF_COEFFICIENT_BASE + (i << 3) + 4);

        /* TAP 2 */

        coeff = temp & 0x7FFF;
        if (temp & 0x8000)
            coeff = -coeff;
        taps[i][2] = coeff;

        /* TAP 3 */

        temp >>= 16;
        coeff = temp & 0x7FFF;
        if (temp & 0x8000)
            coeff = -coeff;
        taps[i][3] = coeff;
    }

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_color_key
 *
 * This routine reads the current settings for hardware color/chroma keying.
 *--------------------------------------------------------------------------*/

int
df_get_video_color_key(unsigned long *key, unsigned long *mask, int *graphics)
{
    unsigned long chroma = READ_VID32(DF_DISPLAY_CONFIG) & DF_DCFG_VG_CK;

    if (chroma) {
        /* CHROMA KEY - READ KEY AND MASK FROM DF */

        *graphics = 0;
        *key = READ_VID32(DF_VIDEO_COLOR_KEY) & 0xFFFFFF;
        *mask = READ_VID32(DF_VIDEO_COLOR_MASK) & 0xFFFFFF;
    }
    else {
        *graphics = 1;

        *key = READ_REG32(DC3_COLOR_KEY) & 0xFFFFFF;
        *mask = READ_REG32(DC3_COLOR_MASK) & 0xFFFFFF;
    }

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_palette_entry
 *
 * This routine returns a single palette entry.
 *--------------------------------------------------------------------------*/

int
df_get_video_palette_entry(unsigned long index, unsigned long *palette)
{
    if (index > 0xFF)
        return CIM_STATUS_INVALIDPARAMS;

    /* READ A SINGLE ENTRY */

    WRITE_VID32(DF_PALETTE_ADDRESS, index);
    *palette = READ_VID32(DF_PALETTE_DATA);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_palette
 *
 * This routine returns the entire video palette.
 *--------------------------------------------------------------------------*/

int
df_get_video_palette(unsigned long *palette)
{
    unsigned long i;

    WRITE_VID32(DF_PALETTE_ADDRESS, 0);
    for (i = 0; i < 256; i++)
        palette[i] = READ_VID32(DF_PALETTE_DATA);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_cursor_color_key
 *
 * This routine returns the current configuration for the hardware video cursor
 * color key.
 *--------------------------------------------------------------------------*/

int
df_get_video_cursor_color_key(DF_VIDEO_CURSOR_PARAMS * cursor_color_key)
{
    unsigned long key;

    cursor_color_key->flags = 0;
    cursor_color_key->color1 = READ_VID32(DF_CURSOR_COLOR_1) & 0xFFFFFF;
    cursor_color_key->color2 = READ_VID32(DF_CURSOR_COLOR_2) & 0xFFFFFF;
    cursor_color_key->mask = READ_VID32(DF_CURSOR_COLOR_MASK) & 0xFFFFFF;

    key = READ_VID32(DF_CURSOR_COLOR_KEY);
    cursor_color_key->key = key & 0xFFFFFF;
    cursor_color_key->select_color2 = (key >> 24) & 0x1F;

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_video_cursor_color_key_enable
 *
 * This routine returns the current enable status of the hardware video cursor
 * color key.
 *--------------------------------------------------------------------------*/

int
df_get_video_cursor_color_key_enable(void)
{
    if (READ_VID32(DF_CURSOR_COLOR_KEY) & DF_CURSOR_COLOR_KEY_ENABLE)
        return 1;

    return 0;
}

/*---------------------------------------------------------------------------
 * df_get_alpha_window_configuration
 *
 * This routine reads the current configuration for one of the three hardware
 * alpha regions.
 *--------------------------------------------------------------------------*/

int
df_get_alpha_window_configuration(int window,
                                  DF_ALPHA_REGION_PARAMS * alpha_data)
{
    unsigned long pos, color, alpha_ctl;
    unsigned long hsyncend, htotal;
    unsigned long vsyncend, vtotal;
    unsigned long hadjust, vadjust;
    unsigned long xreg, yreg;
    unsigned long misc, gfxscale;
    unsigned long temp;
    char delta;

    if (window > 2)
        return CIM_STATUS_INVALIDPARAMS;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        vtotal = ((READ_REG32(DC3_V_ACTIVE_EVEN) >> 16) & 0xFFF) + 1;
        vsyncend = ((READ_REG32(DC3_V_SYNC_EVEN) >> 16) & 0xFFF) + 1;
    }
    else {
        vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
        vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    }

    /* GET PRIORITY */

    pos = 16 + (window << 1);
    alpha_data->priority = (READ_VID32(DF_VID_ALPHA_CONTROL) >> pos) & 3L;

    /* GET ALPHA WINDOW */

    hadjust = htotal - hsyncend - 2;
    vadjust = vtotal - vsyncend + 1;

    xreg = READ_VID32(DF_ALPHA_XPOS_1 + (window << 5));
    yreg = READ_VID32(DF_ALPHA_YPOS_1 + (window << 5));
    alpha_data->width = ((xreg >> 16) & 0xFFF) - (xreg & 0xFFF);
    alpha_data->height = ((yreg >> 16) & 0x7FF) - (yreg & 0x7FF);
    alpha_data->x = (xreg & 0xFFF) - hadjust;
    alpha_data->y = (yreg & 0x7FF) - vadjust;

    /* REVERSE THE GRAPHICS SCALE */

    misc = READ_VID32(DF_VID_MISC);
    if (misc & DF_USER_IMPLICIT_SCALING) {
        gfxscale = READ_REG32(DC3_GFX_SCALE);
        if (gfxscale != 0x40004000) {
            temp = alpha_data->y + alpha_data->height;
            temp = (temp * (gfxscale >> 16)) / 0x4000;

            alpha_data->x = (alpha_data->x * (gfxscale & 0xFFFF)) / 0x4000;
            alpha_data->width =
                (alpha_data->width * (gfxscale & 0xFFFF)) / 0x4000;
            alpha_data->y = (alpha_data->y * (gfxscale >> 16)) / 0x4000;
            alpha_data->height = temp - alpha_data->y;
        }
    }

    if (READ_REG32(DC3_IRQ_FILT_CTL) & DC3_IRQFILT_INTL_EN) {
        /* Y COORDINATE IS ACTUALLY 2X THE ODD FIELD START */

        alpha_data->y <<= 1;

        /* CALCULATE THE EXACT VIDEO HEIGHT */
        /* The height of the video window is the sum of the */
        /* odd and even field heights.                      */

        yreg = READ_VID32(DF_VID_ALPHA_Y_EVEN_1 + (window << 3));
        alpha_data->height += ((yreg >> 16) & 0x7FF) - (yreg & 0x7FF);
    }

    /* GET COLOR REGISTER */

    color = READ_VID32(DF_ALPHA_COLOR_1 + (window << 5));
    alpha_data->color = color & 0xFFFFFF;
    if (color & DF_ALPHA_COLOR_ENABLE)
        alpha_data->flags = DF_ALPHAFLAG_COLORENABLED;
    else
        alpha_data->flags = 0;

    /* GET ALPHA VALUE, DELTA AND PER PIXEL */

    alpha_ctl = READ_VID32(DF_ALPHA_CONTROL_1 + (window << 5));
    alpha_data->alpha_value = alpha_ctl & 0xFF;
    if (alpha_ctl & DF_ACTRL_PERPIXEL_EN)
        alpha_data->flags |= DF_ALPHAFLAG_PERPIXELENABLED;

    delta = (char) ((alpha_ctl >> 8) & 0xFF);
    alpha_data->delta = (long) delta;
    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_alpha_window_enable
 *
 * This routine reads the current enable status of one of the three hardware
 * alpha regions.
 *--------------------------------------------------------------------------*/

int
df_get_alpha_window_enable(int window)
{
    if (window > 2)
        return 0;

    if (READ_VID32(DF_ALPHA_CONTROL_1 + (window << 5)) & DF_ACTRL_WIN_ENABLE)
        return 1;

    return 0;
}

/*---------------------------------------------------------------------------
 * df_get_video_request
 *
 * This routine reads the horizontal (pixel) and vertical (line) video request
 * values.
 *--------------------------------------------------------------------------*/

int
df_get_video_request(unsigned long *x, unsigned long *y)
{
    unsigned long request;
    unsigned long hsyncend, htotal;
    unsigned long vsyncend, vtotal;

    hsyncend = ((READ_REG32(DC3_H_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    vsyncend = ((READ_REG32(DC3_V_SYNC_TIMING) >> 16) & 0xFFF) + 1;
    htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;
    vtotal = ((READ_REG32(DC3_V_ACTIVE_TIMING) >> 16) & 0xFFF) + 1;

    request = READ_VID32(DF_VIDEO_REQUEST);
    *x = ((request >> 16) & 0xFFF) - (htotal - hsyncend - 2);
    *y = (request & 0x7FF) - (vtotal - vsyncend + 1);

    return CIM_STATUS_OK;
}

/*---------------------------------------------------------------------------
 * df_get_output_color_space
 *
 * This routine sets the color space used when combining graphics and video.
 *--------------------------------------------------------------------------*/

int
df_get_output_color_space(int *color_space)
{
    unsigned long alpha_ctl;

    alpha_ctl = READ_VID32(DF_VID_ALPHA_CONTROL);

    if ((alpha_ctl & DF_CSC_VIDEO_YUV_TO_RGB) ||
        !(alpha_ctl & DF_CSC_GRAPHICS_RGB_TO_YUV)) {
        if (alpha_ctl & DF_ALPHA_DRGB)
            *color_space = DF_OUTPUT_ARGB;
        else
            *color_space = DF_OUTPUT_RGB;
    }
    else {
        *color_space = DF_OUTPUT_SDTV;

        if (alpha_ctl & DF_HD_GRAPHICS)
            *color_space = DF_OUTPUT_HDTV;
    }

    return CIM_STATUS_OK;
}

#endif
