/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/ati/atirgb514.c,v 1.4 2003/01/01 19:16:34 tsi Exp $ */
/*
 * Copyright 2001 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of Marc Aurele La France not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  Marc Aurele La France makes no representations
 * about the suitability of this software for any purpose.  It is provided
 * "as-is" without express or implied warranty.
 *
 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "ati.h"
#include "aticrtc.h"
#include "atimach64io.h"
#include "atirgb514.h"

/*
 * ATIRGB514PreInit --
 *
 * This function fills in the IBM RGB 514 portion of an ATIHWRec that is common
 * to all video modes generated by the server.
 */
void
ATIRGB514PreInit
(
    ATIPtr   pATI,
    ATIHWPtr pATIHW
)
{
    /* Get a work copy of IBM RGB 514 registers */
    ATIRGB514Save(pATI, pATIHW);

    /* Miscellaneous Clock Control */
    pATIHW->ibmrgb514[0x0002U] = 0x01U;

    /* Sync Control */
    pATIHW->ibmrgb514[0x0003U] &= ~0x80U;

    /* Horizontal Sync Control */
    pATIHW->ibmrgb514[0x0004U] = 0x00U;

    /* Power Management */
    pATIHW->ibmrgb514[0x0005U] = 0x00U;

    /* DAC Operation */
    pATIHW->ibmrgb514[0x0006U] &= ~0x04U;

    /* Palette Control */
    pATIHW->ibmrgb514[0x0007U] = 0x00U;

    /* PLL Control */
    pATIHW->ibmrgb514[0x0010U] = 0x01U;

    /* Cursor control */
    pATIHW->ibmrgb514[0x0030U] &= ~0x03U;       /* For now */

    /* Border (i.e. overscan) */
    pATIHW->ibmrgb514[0x0060U] = 0x00U;
    pATIHW->ibmrgb514[0x0061U] = 0x00U;
    pATIHW->ibmrgb514[0x0062U] = 0x00U;

    /* Miscellaneous Control */
    pATIHW->ibmrgb514[0x0070U] &= ~0x20U;
    pATIHW->ibmrgb514[0x0071U] = 0x41U; /* See workaround in ATIRGB514Set() */

#ifndef AVOID_CPIO

    if (pATIHW->crtc == ATI_CRTC_VGA)
    {
        /* Pixel Format */
        pATIHW->ibmrgb514[0x000AU] = 0x03U;

        /* Miscellaneous Control */
        pATIHW->ibmrgb514[0x0070U] |= 0x40U;

        /* VRAM Mask */
        pATIHW->ibmrgb514[0x0090U] = 0x03U;
    }
    else

#endif /* AVOID_CPIO */

    {
        /* Miscellaneous Control */
        pATIHW->ibmrgb514[0x0070U] &= ~0x40U;

        /* VRAM Mask */
        pATIHW->ibmrgb514[0x0090U] = 0x00U;
        pATIHW->ibmrgb514[0x0091U] = 0x00U;

        /* Pixel Format */
        switch (pATI->depth)
        {
            case 8:
                pATIHW->ibmrgb514[0x000AU] = 0x03U;
                pATIHW->ibmrgb514[0x000BU] = 0x00U;
                break;

            case 15:
                pATIHW->ibmrgb514[0x000AU] = 0x04U;
                pATIHW->ibmrgb514[0x000CU] = 0xC4U;
                break;

            case 16:
                pATIHW->ibmrgb514[0x000AU] = 0x04U;
                pATIHW->ibmrgb514[0x000CU] = 0xC6U;
                break;

            case 24:
                if (pATI->bitsPerPixel == 24)
                {
                    pATIHW->ibmrgb514[0x000AU] = 0x05U;
                    pATIHW->ibmrgb514[0x000DU] = 0x01U;
                }
                else
                {
                    pATIHW->ibmrgb514[0x000AU] = 0x06U;
                    pATIHW->ibmrgb514[0x000EU] = 0x03U;
                }
                break;

            default:
                break;
        }
    }

    if (pATI->rgbBits == 8)
        pATIHW->ibmrgb514[0x0071U] |= 0x04U;
}

/*
 * ATIRGB514Save --
 *
 * This function saves IBM RGB514 related data into an ATIHWRec.
 */
void
ATIRGB514Save
(
    ATIPtr   pATI,
    ATIHWPtr pATIHW
)
{
    CARD32 crtc_gen_cntl, dac_cntl;
    CARD8  index_lo, index_hi, index_ctl;
    int    Index;

    /* Temporarily switch to Mach64 CRTC */
    crtc_gen_cntl = inr(CRTC_GEN_CNTL);
    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
        outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);

    /* Temporarily switch to IBM RGB 514 registers */
    dac_cntl = inr(DAC_CNTL) & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
    outr(DAC_CNTL, dac_cntl | DAC_EXT_SEL_RS2);

    index_lo = in8(M64_DAC_WRITE);
    index_hi = in8(M64_DAC_DATA);
    index_ctl = in8(M64_DAC_READ);

    out8(M64_DAC_WRITE, 0x00U);
    out8(M64_DAC_DATA, 0x00U);
    out8(M64_DAC_READ, 0x01U);  /* Auto-increment */

    /* Save IBM RGB 514 registers */
    for (Index = 0;  Index < NumberOf(pATIHW->ibmrgb514);  Index++)
    {
        /* Need to rewrite the index every so often... */
        if ((Index == 0x0100) || (Index == 0x0500))
        {
            out8(M64_DAC_WRITE, 0);
            out8(M64_DAC_DATA, Index >> 8);
        }
        pATIHW->ibmrgb514[Index] = in8(M64_DAC_MASK);
    }

    /* Restore registers */
    out8(M64_DAC_WRITE, index_lo);
    out8(M64_DAC_DATA, index_hi);
    out8(M64_DAC_READ, index_ctl);
    outr(DAC_CNTL, dac_cntl);
    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
        outr(CRTC_GEN_CNTL, crtc_gen_cntl);
}

/*
 * ATIRGB514Calculate --
 *
 * This function fills in the IBM RGB 514 portion of an ATIHWRec that is
 * specific to a display mode.  pATIHW->ibmrgb514 has already been
 * initialised by a previous call to ATIRGB514PreInit().
 */
void
ATIRGB514Calculate
(
    ATIPtr         pATI,
    ATIHWPtr       pATIHW,
    DisplayModePtr pMode
)
{
    if (pATI->OptionCSync || (pMode->Flags & (V_CSYNC | V_PCSYNC)))
        pATIHW->ibmrgb514[0x0006U] |= 0x08U;
    else
        pATIHW->ibmrgb514[0x0006U] &= ~0x08U;

    if (pMode->Flags & V_INTERLACE)
        pATIHW->ibmrgb514[0x0071U] |= 0x20U;
    else
        pATIHW->ibmrgb514[0x0071U] &= ~0x20U;
}

/*
 * ATIRGB514Set --
 *
 * This function is called to set an IBM RGB514's registers.
 */
void
ATIRGB514Set
(
    ATIPtr   pATI,
    ATIHWPtr pATIHW
)
{
    CARD32 crtc_gen_cntl, dac_cntl;
    CARD8  index_lo, index_hi, index_ctl;
    int    Index;

    /* Temporarily switch to Mach64 CRTC */
    crtc_gen_cntl = inr(CRTC_GEN_CNTL);
    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
        outr(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);

    /* Temporarily switch to IBM RGB 514 registers */
    dac_cntl = inr(DAC_CNTL) & ~(DAC_EXT_SEL_RS2 | DAC_EXT_SEL_RS3);
    outr(DAC_CNTL, dac_cntl | DAC_EXT_SEL_RS2);

    index_lo = in8(M64_DAC_WRITE);
    index_hi = in8(M64_DAC_DATA);
    index_ctl = in8(M64_DAC_READ);

    out8(M64_DAC_WRITE, 0x00U);
    out8(M64_DAC_DATA, 0x00U);
    out8(M64_DAC_READ, 0x01U);  /* Auto-increment */

    /* Load IBM RGB 514 registers */
    for (Index = 0;  Index < NumberOf(pATIHW->ibmrgb514);  Index++)
         out8(M64_DAC_MASK, pATIHW->ibmrgb514[Index]);

#ifndef AVOID_CPIO

    /* Deal with documented anomaly */
    if (pATIHW->crtc == ATI_CRTC_VGA)
    {
        /* Reset Miscellaneous Control 2 */
        out8(M64_DAC_WRITE, 0x71U);
        out8(M64_DAC_DATA, 0x00U);
        out8(M64_DAC_MASK, pATIHW->ibmrgb514[0x0071U] & ~0x41U);
    }

#endif /* AVOID_CPIO */

    /* Restore registers */
    out8(M64_DAC_WRITE, index_lo);
    out8(M64_DAC_DATA, index_hi);
    out8(M64_DAC_READ, index_ctl);
    outr(DAC_CNTL, dac_cntl);
    if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
        outr(CRTC_GEN_CNTL, crtc_gen_cntl);
}
