diff options
Diffstat (limited to 'src/cim/cim_vop.c')
-rw-r--r-- | src/cim/cim_vop.c | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/src/cim/cim_vop.c b/src/cim/cim_vop.c new file mode 100644 index 0000000..4b83c03 --- /dev/null +++ b/src/cim/cim_vop.c @@ -0,0 +1,604 @@ +/* + * 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 VOP configuration routines. + */ + +/*--------------------------------------------------------------------------- + * vop_set_vbi_window + * + * This routine configures the output position and location in memory of + * VBI data. + *--------------------------------------------------------------------------*/ + +int +vop_set_vbi_window(VOPVBIWINDOWBUFFER * buffer) +{ + unsigned long unlock, temp; + unsigned long hstart, hstop; + unsigned long htotal, hsyncstart; + + if (!buffer) + return CIM_STATUS_INVALIDPARAMS; + + unlock = READ_REG32(DC3_UNLOCK); + WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE); + + /* PROGRAM HORIZONTAL POSITION + * The horizontal position is a little tricky. The counter for the + * horizontal timings is reused for the VBI counter. Consequently, the + * horizontal start and stop values are based off the beginning of active + * data. However, the VG has a quirk. If the counter start position is + * before the beginning of HSync, it applies to the previous line. If + * the counter is after the beginning of HSync it applies to the current + * line. So, for one line the real range can be thought of as + * HSync_start to (HSync_start + htotal - 1). However, the counters + * must be between 0 and htotal - 1. When placing VBI data before the + * start of active data, the horizontal end position will thus be *less* + * than the horizontal start. + */ + + htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1; + hsyncstart = (READ_REG32(DC3_H_SYNC_TIMING) & 0xFFF) + 1; + + if (buffer->horz_from_hsync) { + /* VERIFY THAT THE INPUT IS VALID */ + + if (buffer->horz_start < 0 + || (buffer->horz_start + buffer->vbi_width) > htotal) + return CIM_STATUS_INVALIDPARAMS; + + hstart = buffer->horz_start + hsyncstart; + } else { + /* VERIFY THAT THE INPUT IS VALID */ + + if (buffer->horz_start < ((long)hsyncstart - (long)htotal) || + buffer->horz_start > (long)hsyncstart || + buffer->vbi_width > htotal) { + return CIM_STATUS_INVALIDPARAMS; + } + + hstart = buffer->horz_start + htotal; + } + + hstop = hstart + buffer->vbi_width; + if (hstart > htotal) + hstart -= htotal; + if (hstop > htotal) + hstop -= htotal; + hstart--; + hstop--; + WRITE_REG32(DC3_VBI_HOR, ((hstop << DC3_VBI_HOR_END_SHIFT) & + DC3_VBI_HOR_END_MASK) | (hstart & DC3_VBI_HOR_START_MASK)); + + /* WRITE LINE CAPTURE MASKS */ + + WRITE_REG32(DC3_VBI_LN_ODD, ((buffer->odd_line_offset << + DC3_VBI_ODD_LINE_SHIFT) & DC3_VBI_ODD_LINE_MASK) | + (buffer->odd_line_capture_mask & DC3_VBI_ODD_ENABLE_MASK)); + + WRITE_REG32(DC3_VBI_LN_EVEN, ((buffer->even_line_offset << + DC3_VBI_EVEN_LINE_SHIFT) & DC3_VBI_EVEN_LINE_MASK) | + (buffer->even_line_capture_mask & DC3_VBI_EVEN_ENABLE_MASK)); + + /* PROGRAM SOURCE OFFSETS + * Start with the even offsets. Note that we always enable 16-bit VBI, + * as this is the only way to get VBI data on each VOP clock. + */ + + temp = READ_REG32(DC3_VBI_EVEN_CTL) & ~DC3_VBI_EVEN_CTL_OFFSET_MASK; + temp |= DC3_VBI_EVEN_CTL_ENABLE_16; + if (buffer->enable_upscale) + temp |= DC3_VBI_EVEN_CTL_UPSCALE; + WRITE_REG32(DC3_VBI_EVEN_CTL, temp | + (buffer->even_address_offset & DC3_VBI_EVEN_CTL_OFFSET_MASK)); + + /* ODD OFFSET */ + + temp = READ_REG32(DC3_VBI_ODD_CTL) & ~DC3_VBI_ODD_CTL_OFFSET_MASK; + WRITE_REG32(DC3_VBI_ODD_CTL, temp | + (buffer->odd_address_offset & DC3_VBI_ODD_CTL_OFFSET_MASK)); + + /* PITCH */ + + temp = ((buffer->data_size >> 3) << 16) | ((buffer->data_pitch >> 3) & + 0x0000FFFF); + WRITE_REG32(DC3_VBI_PITCH, temp); + + WRITE_REG32(DC3_UNLOCK, unlock); + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_enable_vbi_output + * + * This routine enables/disables VBI fetching inside the video generator. + *--------------------------------------------------------------------------*/ + +int +vop_enable_vbi_output(int enable) +{ + unsigned long unlock, temp; + + unlock = READ_REG32(DC3_UNLOCK); + temp = READ_REG32(DC3_VBI_EVEN_CTL); + + if (enable) + temp |= DC3_VBI_ENABLE; + else + temp &= ~DC3_VBI_ENABLE; + + WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE); + WRITE_REG32(DC3_VBI_EVEN_CTL, temp); + WRITE_REG32(DC3_UNLOCK, unlock); + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_set_configuration + * + * This routine is passed a VOP_CONFIGURATION structure that contains all + * the necessary information to configure VOP output. + *--------------------------------------------------------------------------*/ + +int +vop_set_configuration(VOPCONFIGURATIONBUFFER * config) +{ + unsigned long vop_config = 0; + unsigned long alpha, control2; + unsigned long unlock; + unsigned long delta; + Q_WORD msr_value; + int rgb = 0; + + if (!config) + return CIM_STATUS_INVALIDPARAMS; + + unlock = READ_REG32(DC3_UNLOCK); + delta = READ_REG32(DC3_VID_DS_DELTA) & DC3_DS_DELTA_MASK; + + /* OVERRIDE THE OUTPUT SETTINGS TO ENABLE VOP OUTPUT */ + + if (config->mode != VOP_MODE_DISABLED) { + msr_read64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value); + msr_value.low &= ~DF_CONFIG_OUTPUT_MASK; + msr_value.low |= DF_OUTPUT_VOP; + msr_write64(MSR_DEVICE_GEODELX_DF, MSR_GEODELINK_CONFIG, &msr_value); + } + + /* SET THE UNIVERSAL VOP OPTIONS */ + + if (config->flags & VOP_FLAG_SWAP_UV) + vop_config |= VOP_CONFIG_SWAPUV; + if (config->flags & VOP_FLAG_SWAP_VBI) + vop_config |= VOP_CONFIG_SWAPVBI; + + /* SET THE MODE SPECIFIC PARAMETERS */ + + if (config->mode == VOP_MODE_601) { + vop_config |= config->vop601.flags; + vop_config |= config->vop601.vsync_shift; + vop_config |= VOP_CONFIG_ENABLE_601 | VOP_CONFIG_VIP2_0; + + switch (config->vop601.output_mode) { + case VOP_601_YUV_16BIT: + vop_config |= VOP_CONFIG_VIP2_16BIT; + break; + case VOP_601_YUV_4_4_4: + vop_config |= VOP_CONFIG_DISABLE_DECIMATE; + break; + case VOP_601_RGB_8_8_8: + vop_config |= VOP_CONFIG_DISABLE_DECIMATE | VOP_CONFIG_RGBMODE; + rgb = 1; + break; + } + + if (config->vop601.vsync_shift == VOP_VSYNC_LATER_BY_X) { + delta |= (config->vop601.vsync_shift_count & + DC3_601_VSYNC_SHIFT_MASK); + delta |= DC3_601_VSYNC_SHIFT_ENABLE; + } + } else { + if (config->flags & VOP_FLAG_VBI) + vop_config |= VOP_CONFIG_VBI; + if (config->flags & VOP_FLAG_TASK) + vop_config |= VOP_CONFIG_TASK; + if (config->flags & VOP_FLAG_SINGLECHIPCOMPAT) + vop_config |= VOP_CONFIG_SC_COMPATIBLE; + if (config->flags & VOP_FLAG_EXTENDEDSAV) + vop_config |= VOP_CONFIG_EXTENDED_SAV; + + switch (config->mode) { + case VOP_MODE_DISABLED: + vop_config |= VOP_CONFIG_DISABLED; + break; + case VOP_MODE_VIP11: + vop_config |= VOP_CONFIG_VIP1_1; + break; + case VOP_MODE_CCIR656: + vop_config |= VOP_CONFIG_CCIR656; + break; + case VOP_MODE_VIP20_8BIT: + vop_config |= VOP_CONFIG_VIP2_0; + break; + case VOP_MODE_VIP20_16BIT: + vop_config |= VOP_CONFIG_VIP2_0 | VOP_CONFIG_VIP2_16BIT; + break; + } + } + + /* SET THE 4:4:4 TO 4:2:2 DECIMATION ALGORITHM */ + + vop_config |= (config->conversion_mode); + + /* SET THE VSYNC OUT OPTIONS */ + + control2 = READ_VIP32(VIP_CONTROL2) & ~VIP_CONTROL2_SYNC2PIN_MASK; + control2 |= config->vsync_out; + WRITE_VIP32(VIP_CONTROL2, control2); + + /* FORCE THE CORRECT VOP COLOR SPACE */ + /* The output of the mixer will be either RGB or YUV. We must enable */ + /* or disable the VOP CSC based on the desired output format. */ + + alpha = READ_VID32(DF_VID_ALPHA_CONTROL); + if (!(alpha & DF_CSC_GRAPHICS_RGB_TO_YUV)) { + /* RGB OUTPUT FROM THE MIXER */ + + if (!rgb) + alpha |= DF_CSC_VOP_RGB_TO_YUV; + else + alpha &= ~DF_CSC_VOP_RGB_TO_YUV; + } else { + /* YUV OUTPUT FROM THE MIXER */ + /* As there is no YUV->RGB VOP conversion, we simply disable the */ + /* VOP CSC and trust that the user is competent. */ + + alpha &= ~DF_CSC_VOP_RGB_TO_YUV; + } + + /* AND WRITE THE CONFIGURATION */ + + WRITE_VID32(DF_VID_ALPHA_CONTROL, alpha); + WRITE_VOP32(VOP_CONFIGURATION, vop_config); + WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE); + WRITE_REG32(DC3_VID_DS_DELTA, delta); + WRITE_REG32(DC3_UNLOCK, unlock); + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_save_state + * + * This routine saves the necessary register contents in order to restore + * at a later point to the same state. Note that the capture state is + * forced to OFF in this routine. + *--------------------------------------------------------------------------*/ + +int +vop_save_state(VOPSTATEBUFFER * save_buffer) +{ + if (!save_buffer) + return CIM_STATUS_INVALIDPARAMS; + + save_buffer->config = READ_VOP32(VOP_CONFIGURATION); + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_restore_state + * + * This routine restores the state of the vop registers - which were + * previously saved using vop_save_state. + *--------------------------------------------------------------------------*/ + +int +vop_restore_state(VOPSTATEBUFFER * restore_buffer) +{ + if (!restore_buffer) + return CIM_STATUS_INVALIDPARAMS; + + WRITE_VOP32(VOP_CONFIGURATION, restore_buffer->config); + + return CIM_STATUS_OK; +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * CIMARRON VOP READ ROUTINES + * These routines are included for use in diagnostics or when debugging. They + * can be optionally excluded from a project. + *++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +#if CIMARRON_INCLUDE_VOP_READ_ROUTINES + +/*--------------------------------------------------------------------------- + * vop_get_current_mode + * + * This routine reads the current VIP operating mode and stores it in the + * passed VOP_CONFIGURATION structure. + *--------------------------------------------------------------------------*/ + +int +vop_get_current_mode(VOPCONFIGURATIONBUFFER * config) +{ + unsigned long vop_config = 0; + unsigned long alpha; + + if (!config) + return CIM_STATUS_INVALIDPARAMS; + + vop_config = READ_VOP32(VOP_CONFIGURATION); + alpha = READ_VID32(DF_VID_ALPHA_CONTROL); + + /* READ THE CURRENT MODE */ + + switch (vop_config & VOP_CONFIG_MODE_MASK) { + case VOP_CONFIG_DISABLED: + config->mode = VOP_MODE_DISABLED; + break; + case VOP_CONFIG_VIP1_1: + config->mode = VOP_MODE_VIP11; + break; + case VOP_CONFIG_CCIR656: + config->mode = VOP_MODE_CCIR656; + break; + case VOP_CONFIG_VIP2_0: + + if (vop_config & VOP_CONFIG_ENABLE_601) + config->mode = VOP_MODE_601; + else if (vop_config & VOP_CONFIG_VIP2_16BIT) + config->mode = VOP_MODE_VIP20_16BIT; + else + config->mode = VOP_MODE_VIP20_8BIT; + break; + } + + /* READ 601 SETTINGS */ + + config->vop601.flags = vop_config & (VOP_CONFIG_INVERT_DISPE | + VOP_CONFIG_INVERT_HSYNC | VOP_CONFIG_INVERT_VSYNC); + + config->vop601.vsync_shift = vop_config & VOP_CONFIG_VSYNC_MASK; + config->vop601.vsync_shift_count = + READ_REG32(DC3_VID_DS_DELTA) & DC3_601_VSYNC_SHIFT_MASK; + + if ((alpha & DF_CSC_GRAPHICS_RGB_TO_YUV) || + (alpha & DF_CSC_VOP_RGB_TO_YUV)) { + /* YUV OUTPUT */ + + if (vop_config & VOP_CONFIG_DISABLE_DECIMATE) + config->vop601.output_mode = VOP_601_YUV_4_4_4; + else if (vop_config & VOP_CONFIG_VIP2_16BIT) + config->vop601.output_mode = VOP_601_YUV_16BIT; + else + config->vop601.output_mode = VOP_601_YUV_8BIT; + } else { + config->vop601.output_mode = VOP_601_RGB_8_8_8; + } + + config->flags = 0; + + /* READ THE UNIVERSAL VOP OPTIONS */ + + if (vop_config & VOP_CONFIG_SWAPUV) + config->flags |= VOP_FLAG_SWAP_UV; + if (vop_config & VOP_CONFIG_SWAPVBI) + config->flags |= VOP_FLAG_SWAP_VBI; + if (vop_config & VOP_CONFIG_VBI) + config->flags |= VOP_FLAG_VBI; + if (vop_config & VOP_CONFIG_TASK) + config->flags |= VOP_FLAG_TASK; + if (vop_config & VOP_CONFIG_SC_COMPATIBLE) + config->flags |= VOP_FLAG_SINGLECHIPCOMPAT; + if (vop_config & VOP_CONFIG_EXTENDED_SAV) + config->flags |= VOP_FLAG_EXTENDEDSAV; + + config->conversion_mode = vop_config & VOP_CONFIG_422_MASK; + + config->vsync_out = READ_VIP32(VIP_CONTROL2) & VIP_CONTROL2_SYNC2PIN_MASK; + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_get_vbi_window + * + * This routine reads the current VBI configuration for VOP output. + *--------------------------------------------------------------------------*/ + +int +vop_get_vbi_configuration(VOPVBIWINDOWBUFFER * buffer) +{ + unsigned long temp; + unsigned long hstart, hstop; + unsigned long htotal, hsyncstart; + + if (!buffer) + return CIM_STATUS_INVALIDPARAMS; + + htotal = ((READ_REG32(DC3_H_ACTIVE_TIMING) >> 16) & 0xFFF) + 1; + hsyncstart = (READ_REG32(DC3_H_SYNC_TIMING) & 0xFFF) + 1; + + /* DECODE HORIZONTAL POSITION */ + /* This is done according to the requested horizontal origin */ + + temp = READ_REG32(DC3_VBI_HOR); + hstart = (temp & DC3_VBI_HOR_START_MASK) + 1; + hstop = ((temp & DC3_VBI_HOR_END_MASK) >> DC3_VBI_HOR_END_SHIFT) + 1; + if (buffer->horz_from_hsync) { + buffer->horz_start = hstart + htotal - hsyncstart; + if (buffer->horz_start >= (long)htotal) + buffer->horz_start -= htotal; + } else { + if (hstart > hsyncstart) + buffer->horz_start = (long)hstart - (long)htotal; + else + buffer->horz_start = hstart; + } + + if (hstop > hstart) + buffer->vbi_width = hstop - hstart; + else + buffer->vbi_width = (htotal - hstart) + hstop; + + /* READ LINE MASKS */ + + temp = READ_REG32(DC3_VBI_LN_ODD); + buffer->odd_line_offset = (temp & DC3_VBI_ODD_LINE_MASK) >> + DC3_VBI_ODD_LINE_SHIFT; + buffer->odd_line_capture_mask = (temp & DC3_VBI_ODD_ENABLE_MASK); + + temp = READ_REG32(DC3_VBI_LN_EVEN); + buffer->even_line_offset = (temp & DC3_VBI_EVEN_LINE_MASK) >> + DC3_VBI_EVEN_LINE_SHIFT; + buffer->even_line_capture_mask = (temp & DC3_VBI_EVEN_ENABLE_MASK); + + /* READ VBI UPSCALE SETTINGS */ + + buffer->enable_upscale = 0; + temp = READ_REG32(DC3_VBI_EVEN_CTL); + if (temp & DC3_VBI_EVEN_CTL_UPSCALE) + buffer->enable_upscale = 1; + + /* READ SOURCE OFFSETS */ + + buffer->even_address_offset = temp & DC3_VBI_EVEN_CTL_OFFSET_MASK; + buffer->odd_address_offset = + READ_REG32(DC3_VBI_ODD_CTL) & DC3_VBI_ODD_CTL_OFFSET_MASK; + + /* PITCH AND SIZE */ + + temp = READ_REG32(DC3_VBI_PITCH); + buffer->data_size = (temp >> 16) << 3; + buffer->data_pitch = (temp & 0xFFFF); + + return CIM_STATUS_OK; +} + +/*--------------------------------------------------------------------------- + * vop_get_vbi_enable + * + * This routine reads the current enable status of VBI output. + *--------------------------------------------------------------------------*/ + +int +vop_get_vbi_enable(void) +{ + if (READ_REG32(DC3_VBI_EVEN_CTL) & DC3_VBI_ENABLE) + return 1; + + return 0; +} + +/*--------------------------------------------------------------------------- + * vop_get_crc + * + * This routine returns a CRC of the current VOP data + --------------------------------------------------------------------------*/ + +unsigned long +vop_get_crc(void) +{ + unsigned long crc; + unsigned long config = READ_VOP32(VOP_CONFIGURATION); + unsigned long timeout = 1000; + + if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN)) + return 0xFFFFFFFF; + + /* RESET CRC */ + + WRITE_VOP32(VOP_CONFIGURATION, config & ~VOP_CONFIG_ENABLE_SIGNATURE); + + /* WAIT FOR THE RESET TO BE LATCHED */ + + while ((READ_VOP32(VOP_SIGNATURE) != 0x00000001) && timeout) + timeout--; + + WRITE_VOP32(VOP_CONFIGURATION, config | VOP_CONFIG_ENABLE_SIGNATURE); + + /* WAIT UNTIL NOT ACTIVE, THEN ACTIVE, NOT ACTIVE, THEN ACTIVE */ + + while (!(READ_VOP32(VOP_CONFIGURATION) & VOP_CONFIG_SIGVAL)) ; + + crc = READ_VOP32(VOP_SIGNATURE); + + return crc; +} + +/*--------------------------------------------------------------------------- + * vop_read_vbi_crc + * + * This routine returns a CRC of the current VBI data + ---------------------------------------------------------------------------*/ + +unsigned long +vop_read_vbi_crc(void) +{ + unsigned long gcfg, unlock, vbi_even; + unsigned long crc; + + if (!(READ_REG32(DC3_DISPLAY_CFG) & DC3_DCFG_TGEN) || + !(READ_REG32(DC3_VBI_EVEN_CTL) & DC3_VBI_ENABLE)) { + return 0xFFFFFFFF; + } + + unlock = READ_REG32(DC3_UNLOCK); + gcfg = READ_REG32(DC3_GENERAL_CFG); + vbi_even = READ_REG32(DC3_VBI_EVEN_CTL); + + gcfg |= DC3_GCFG_SGRE | DC3_GCFG_CRC_MODE; + gcfg &= ~(DC3_GCFG_SGFR | DC3_GCFG_SIG_SEL); + vbi_even |= DC3_VBI_EVEN_ENABLE_CRC; + + WRITE_REG32(DC3_UNLOCK, DC3_UNLOCK_VALUE); + WRITE_REG32(DC3_VBI_EVEN_CTL, vbi_even); + WRITE_REG32(DC3_GENERAL_CFG, gcfg & ~DC3_GCFG_SIGE); + WRITE_REG32(DC3_GENERAL_CFG, gcfg | DC3_GCFG_SIGE); + + /* WAIT FOR THE CRC TO BE COMPLETED */ + + while (!(READ_REG32(DC3_LINE_CNT_STATUS) & DC3_LNCNT_SIGC)) ; + + /* READ THE COMPLETED CRC */ + + crc = READ_REG32(DC3_PAL_DATA); + + /* RESTORE THE PALETTE SETTINGS */ + + gcfg &= ~DC3_GCFG_SGRE; + WRITE_REG32(DC3_GENERAL_CFG, gcfg); + WRITE_REG32(DC3_UNLOCK, unlock); + + return crc; +} + +#endif |