/* Copyright (c) 2005 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.
 * */

/* 
 * This file contains routines to control the SC1200 video input port (VIP) 
 * hardware.
 * */

/*----------------------------------------------------------------------------
 * gfx_set_vip_enable
 *
 * This routine enables or disables the writes to memory from the video port. 
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_enable(int enable)
#else
int
gfx_set_vip_enable(int enable)
#endif
{
    unsigned long value;

    value = READ_VIP32(SC1200_VIP_CONTROL);
    if (enable)
        value |= SC1200_VIP_DATA_CAPTURE_EN;
    else
        value &= ~SC1200_VIP_DATA_CAPTURE_EN;
    WRITE_VIP32(SC1200_VIP_CONTROL, value);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_capture_run_mode
 *
 * This routine selects VIP capture run mode.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_capture_run_mode(int mode)
#else
int
gfx_set_vip_capture_run_mode(int mode)
#endif
{
    unsigned long value;

    value = READ_VIP32(SC1200_VIP_CONTROL);
    value &= ~SC1200_CAPTURE_RUN_MODE_MASK;
    switch (mode) {
    case VIP_CAPTURE_STOP_LINE:
        value |= SC1200_CAPTURE_RUN_MODE_STOP_LINE;
        break;
    case VIP_CAPTURE_STOP_FIELD:
        value |= SC1200_CAPTURE_RUN_MODE_STOP_FIELD;
        break;
    case VIP_CAPTURE_START_FIELD:
        value |= SC1200_CAPTURE_RUN_MODE_START;
        break;
    default:
        return GFX_STATUS_BAD_PARAMETER;
    }
    WRITE_VIP32(SC1200_VIP_CONTROL, value);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_base
 *
 * This routine sets the odd and even base address values for the VIP memory
 * buffer.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_base(unsigned long even, unsigned long odd)
#else
int
gfx_set_vip_base(unsigned long even, unsigned long odd)
#endif
{
    /* TRUE OFFSET IS SPECIFIED, NEED TO SET BIT 23 FOR HARDWARE */

    if (even)
        WRITE_VIP32(SC1200_VIP_EVEN_BASE,
                    even + (unsigned long) gfx_phys_fbptr);
    if (odd)
        WRITE_VIP32(SC1200_VIP_ODD_BASE, odd + (unsigned long) gfx_phys_fbptr);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_pitch
 *
 * This routine sets the number of bytes between scanlines for the VIP data.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_pitch(unsigned long pitch)
#else
int
gfx_set_vip_pitch(unsigned long pitch)
#endif
{
    WRITE_VIP32(SC1200_VIP_PITCH, pitch & SC1200_VIP_PITCH_MASK);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_mode
 *
 * This routine sets the VIP operating mode.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_mode(int mode)
#else
int
gfx_set_vip_mode(int mode)
#endif
{
    unsigned long config;

    config = READ_VIP32(SC1200_VIP_CONFIG);
    config &= ~SC1200_VIP_MODE_MASK;
    switch (mode) {
    case VIP_MODE_C:
        WRITE_VIP32(SC1200_VIP_CONFIG, config | SC1200_VIP_MODE_C);
        break;
    default:
        return GFX_STATUS_BAD_PARAMETER;
    }
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_enable
 *
 * This routine enables or disables the VBI data capture.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_enable(int enable)
#else
int
gfx_set_vbi_enable(int enable)
#endif
{
    unsigned long value;

    value = READ_VIP32(SC1200_VIP_CONTROL);
    if (enable)
        value |= SC1200_VIP_VBI_CAPTURE_EN;
    else
        value &= ~SC1200_VIP_VBI_CAPTURE_EN;
    WRITE_VIP32(SC1200_VIP_CONTROL, value);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_mode
 *
 * This routine sets the VBI data types captured to memory.
 * It receives a mask of all enabled types.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_mode(int mode)
#else
int
gfx_set_vbi_mode(int mode)
#endif
{
    unsigned long config;

    config = READ_VIP32(SC1200_VIP_CONFIG);
    config &=
        ~(SC1200_VBI_ANCILLARY_TO_MEMORY | SC1200_VBI_TASK_A_TO_MEMORY |
          SC1200_VBI_TASK_B_TO_MEMORY);

    if (mode & VBI_ANCILLARY)
        config |= SC1200_VBI_ANCILLARY_TO_MEMORY;
    if (mode & VBI_TASK_A)
        config |= SC1200_VBI_TASK_A_TO_MEMORY;
    if (mode & VBI_TASK_B)
        config |= SC1200_VBI_TASK_B_TO_MEMORY;
    WRITE_VIP32(SC1200_VIP_CONFIG, config);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_base
 *
 * This routine sets the odd and even base address values for VBI capture.
 *
 * "even" and "odd" should contain 16-byte aligned physical addresses.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_base(unsigned long even, unsigned long odd)
#else
int
gfx_set_vbi_base(unsigned long even, unsigned long odd)
#endif
{
    /* VIP HW REQUIRES THAT BASE ADDRESSES BE 16-BYTE ALIGNED */

    if (even)
        WRITE_VIP32(SC1200_VBI_EVEN_BASE, even & ~0xf);
    if (odd)
        WRITE_VIP32(SC1200_VBI_ODD_BASE, odd & ~0xf);

    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_pitch
 *
 * This routine sets the number of bytes between scanlines for VBI capture.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_pitch(unsigned long pitch)
#else
int
gfx_set_vbi_pitch(unsigned long pitch)
#endif
{
    WRITE_VIP32(SC1200_VBI_PITCH, pitch & SC1200_VBI_PITCH_MASK);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_direct
 *
 * This routine sets the VBI lines to be passed to the Direct VIP.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_direct(unsigned long even_lines, unsigned long odd_lines)
#else
int
gfx_set_vbi_direct(unsigned long even_lines, unsigned long odd_lines)
#endif
{
    WRITE_VIP32(SC1200_EVEN_DIRECT_VBI_LINE_ENABLE,
                even_lines & SC1200_DIRECT_VBI_LINE_ENABLE_MASK);
    WRITE_VIP32(SC1200_ODD_DIRECT_VBI_LINE_ENABLE,
                odd_lines & SC1200_DIRECT_VBI_LINE_ENABLE_MASK);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vbi_interrupt
 *
 * This routine enables or disables the VBI field interrupt.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vbi_interrupt(int enable)
#else
int
gfx_set_vbi_interrupt(int enable)
#endif
{
    unsigned long value;

    value = READ_VIP32(SC1200_VIP_CONTROL);
    if (enable)
        value |= SC1200_VIP_VBI_FIELD_INTERRUPT_EN;
    else
        value &= ~SC1200_VIP_VBI_FIELD_INTERRUPT_EN;
    WRITE_VIP32(SC1200_VIP_CONTROL, value);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_bus_request_threshold_high
 *
 * This routine sets the VIP FIFO bus request threshold.
 * If enable is TRUE, VIP FIFO will be set to issue a bus request when it 
 * filled with 64 bytes. If enable is FALSE, VIP FIFO will be set to issue a 
 * bus request when it filled with 32 bytes.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_bus_request_threshold_high(int enable)
#else
int
gfx_set_vip_bus_request_threshold_high(int enable)
#endif
{
    unsigned long value;

    value = READ_VIP32(SC1200_VIP_CONFIG);
    if (enable)
        value &= ~SC1200_VIP_BUS_REQUEST_THRESHOLD;
    else
        value |= SC1200_VIP_BUS_REQUEST_THRESHOLD;
    WRITE_VIP32(SC1200_VIP_CONFIG, value);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_set_vip_last_line
 *
 * This routine sets the maximum number of lines captured in each field.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_set_vip_last_line(int last_line)
#else
int
gfx_set_vip_last_line(int last_line)
#endif
{
    unsigned long value;

    /* This feature is implemented in Rev C1 */
    if (gfx_chip_revision < SC1200_REV_C1)
        return (GFX_STATUS_OK);

    value = READ_VIP32(SC1200_VIP_LINE_TARGET);
    value &= ~SC1200_VIP_LAST_LINE_MASK;
    value |= ((last_line & 0x3FF) << 16);
    WRITE_VIP32(SC1200_VIP_LINE_TARGET, value);
    return (GFX_STATUS_OK);
}

/*----------------------------------------------------------------------------
 * gfx_test_vip_odd_field
 *
 * This routine returns 1 if the current VIP field is odd. Otherwise returns 0
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_test_vip_odd_field(void)
#else
int
gfx_test_vip_odd_field(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_STATUS) & SC1200_VIP_CURRENT_FIELD_ODD)
        return (1);
    else
        return (0);
}

/*----------------------------------------------------------------------------
 * gfx_test_vip_bases_updated
 *
 * This routine returns 1 if all of the VIP base registers have been updated,
 * i.e. there is no base register which has been written with a new address, 
 * that VIP has not already captured or started capturing into the new address
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_test_vip_bases_updated(void)
#else
int
gfx_test_vip_bases_updated(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_STATUS) & SC1200_VIP_BASE_NOT_UPDATED)
        return (0);
    else
        return (1);
}

/*----------------------------------------------------------------------------
 * gfx_test_vip_fifo_overflow
 *
 * This routine returns 1 if an overflow occurred on the FIFO between the VIP
 * and the fast X-bus, 0 otherwise. 
 * If an overflow occurred, the overflow status indication is reset.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_test_vip_fifo_overflow(void)
#else
int
gfx_test_vip_fifo_overflow(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_STATUS) & SC1200_VIP_FIFO_OVERFLOW) {
        /* Bits in vip status register are either read only or reset by 
         * writing 1 */
        WRITE_VIP32(SC1200_VIP_STATUS, SC1200_VIP_FIFO_OVERFLOW);
        return (1);
    }
    else {
        return (0);
    }
}

/*----------------------------------------------------------------------------
 * gfx_get_vip_line
 *
 * This routine returns the number of the current video line being
 * received by the VIP interface.
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vip_line(void)
#else
int
gfx_get_vip_line(void)
#endif
{
    return (int) (READ_VIP32(SC1200_VIP_CURRENT_LINE) &
                  SC1200_VIP_CURRENT_LINE_MASK);
}

/*----------------------------------------------------------------------------
 * gfx_get_vip_base
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
unsigned long
sc1200_get_vip_base(int odd)
#else
unsigned long
gfx_get_vip_base(int odd)
#endif
{
    /* MASK BIT 23 AND ABOVE TO MAKE IT A TRUE OFFSET */

    if (odd)
        return (READ_VIP32(SC1200_VIP_ODD_BASE));
    return (READ_VIP32(SC1200_VIP_EVEN_BASE));
}

/*----------------------------------------------------------------------------
 * gfx_get_vbi_pitch
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
unsigned long
sc1200_get_vbi_pitch(void)
#else
unsigned long
gfx_get_vbi_pitch(void)
#endif
{
    return (READ_VIP32(SC1200_VBI_PITCH) & SC1200_VBI_PITCH_MASK);
}

/*************************************************************/
/*  READ ROUTINES  |  INCLUDED FOR DIAGNOSTIC PURPOSES ONLY  */
/*************************************************************/

#if GFX_READ_ROUTINES

/*----------------------------------------------------------------------------
 * gfx_get_vip_enable
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vip_enable(void)
#else
int
gfx_get_vip_enable(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_CONTROL) & SC1200_VIP_DATA_CAPTURE_EN)
        return (1);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_get_vip_pitch
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
unsigned long
sc1200_get_vip_pitch(void)
#else
unsigned long
gfx_get_vip_pitch(void)
#endif
{
    return (READ_VIP32(SC1200_VIP_PITCH) & SC1200_VIP_PITCH_MASK);
}

/*----------------------------------------------------------------------------
 * gfx_get_vip_mode
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vip_mode(void)
#else
int
gfx_get_vip_mode(void)
#endif
{
    switch (READ_VIP32(SC1200_VIP_CONFIG) & SC1200_VIP_MODE_MASK) {
    case SC1200_VIP_MODE_C:
        return VIP_MODE_C;
    default:
        return (0);
    }
}

/*----------------------------------------------------------------------------
 * gfx_get_vbi_enable
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vbi_enable(void)
#else
int
gfx_get_vbi_enable(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_CONTROL) & SC1200_VIP_VBI_CAPTURE_EN)
        return (1);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_get_vbi_mode
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vbi_mode(void)
#else
int
gfx_get_vbi_mode(void)
#endif
{
    int config;
    int mode = 0;

    config =
        (int) (READ_VIP32(SC1200_VIP_CONFIG) & (SC1200_VBI_ANCILLARY_TO_MEMORY
                                                | SC1200_VBI_TASK_A_TO_MEMORY |
                                                SC1200_VBI_TASK_B_TO_MEMORY));
    if (config & SC1200_VBI_ANCILLARY_TO_MEMORY)
        mode |= VBI_ANCILLARY;
    if (config & SC1200_VBI_TASK_A_TO_MEMORY)
        mode |= VBI_TASK_A;
    if (config & SC1200_VBI_TASK_B_TO_MEMORY)
        mode |= VBI_TASK_B;
    return mode;
}

/*----------------------------------------------------------------------------
 * gfx_get_vbi_base
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
unsigned long
sc1200_get_vbi_base(int odd)
#else
unsigned long
gfx_get_vbi_base(int odd)
#endif
{
    /* MASK BIT 23 AND ABOVE TO MAKE IT A TRUE OFFSET */

    if (odd)
        return (READ_VIP32(SC1200_VBI_ODD_BASE));
    return (READ_VIP32(SC1200_VBI_EVEN_BASE));
}

/*----------------------------------------------------------------------------
 * gfx_get_vbi_direct
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
unsigned long
sc1200_get_vbi_direct(int odd)
#else
unsigned long
gfx_get_vbi_direct(int odd)
#endif
{
    /* MASK BIT 23 AND ABOVE TO MAKE IT A TRUE OFFSET */

    if (odd)
        return (READ_VIP32(SC1200_ODD_DIRECT_VBI_LINE_ENABLE) &
                SC1200_DIRECT_VBI_LINE_ENABLE_MASK);
    return (READ_VIP32(SC1200_EVEN_DIRECT_VBI_LINE_ENABLE) &
            SC1200_DIRECT_VBI_LINE_ENABLE_MASK);
}

/*---------------------------------------------------------------------------
 * gfx_get_vbi_interrupt
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vbi_interrupt(void)
#else
int
gfx_get_vbi_interrupt(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_CONTROL) & SC1200_VIP_VBI_FIELD_INTERRUPT_EN)
        return (1);
    return (0);
}

/*----------------------------------------------------------------------------
 * gfx_get_vip_bus_request_threshold_high
 *----------------------------------------------------------------------------
 */
#if GFX_VIP_DYNAMIC
int
sc1200_get_vip_bus_request_threshold_high(void)
#else
int
gfx_get_vip_bus_request_threshold_high(void)
#endif
{
    if (READ_VIP32(SC1200_VIP_CONFIG) & SC1200_VIP_BUS_REQUEST_THRESHOLD)
        return (1);
    return (0);
}

#endif                          /* GFX_READ_ROUTINES */

/* END OF FILE */