2009-06-05 14:42:42 +02:00
/*
* Copyright 2007 - 8 Advanced Micro Devices , Inc .
* Copyright 2008 Red Hat 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 COPYRIGHT HOLDER ( S ) OR AUTHOR ( S ) 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 .
*
* Authors : Dave Airlie
* Alex Deucher
*/
# include "drmP.h"
# include "radeon_drm.h"
# include "radeon.h"
# include "atom.h"
# include <asm/div64.h>
# include "drm_crtc_helper.h"
# include "drm_edid.h"
static int radeon_ddc_dump ( struct drm_connector * connector ) ;
static void avivo_crtc_load_lut ( struct drm_crtc * crtc )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
int i ;
2010-08-02 10:42:55 +10:00
DRM_DEBUG_KMS ( " %d \n " , radeon_crtc - > crtc_id ) ;
2009-06-05 14:42:42 +02:00
WREG32 ( AVIVO_DC_LUTA_CONTROL + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( AVIVO_DC_LUTA_BLACK_OFFSET_BLUE + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( AVIVO_DC_LUTA_BLACK_OFFSET_GREEN + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( AVIVO_DC_LUTA_BLACK_OFFSET_RED + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( AVIVO_DC_LUTA_WHITE_OFFSET_BLUE + radeon_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( AVIVO_DC_LUTA_WHITE_OFFSET_GREEN + radeon_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( AVIVO_DC_LUTA_WHITE_OFFSET_RED + radeon_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( AVIVO_DC_LUT_RW_SELECT , radeon_crtc - > crtc_id ) ;
WREG32 ( AVIVO_DC_LUT_RW_MODE , 0 ) ;
WREG32 ( AVIVO_DC_LUT_WRITE_EN_MASK , 0x0000003f ) ;
WREG8 ( AVIVO_DC_LUT_RW_INDEX , 0 ) ;
for ( i = 0 ; i < 256 ; i + + ) {
WREG32 ( AVIVO_DC_LUT_30_COLOR ,
( radeon_crtc - > lut_r [ i ] < < 20 ) |
( radeon_crtc - > lut_g [ i ] < < 10 ) |
( radeon_crtc - > lut_b [ i ] < < 0 ) ) ;
}
WREG32 ( AVIVO_D1GRPH_LUT_SEL + radeon_crtc - > crtc_offset , radeon_crtc - > crtc_id ) ;
}
2010-01-12 17:54:34 -05:00
static void evergreen_crtc_load_lut ( struct drm_crtc * crtc )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
int i ;
2010-08-02 10:42:55 +10:00
DRM_DEBUG_KMS ( " %d \n " , radeon_crtc - > crtc_id ) ;
2010-01-12 17:54:34 -05:00
WREG32 ( EVERGREEN_DC_LUT_CONTROL + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_BLACK_OFFSET_RED + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + radeon_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + radeon_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( EVERGREEN_DC_LUT_WHITE_OFFSET_RED + radeon_crtc - > crtc_offset , 0xffff ) ;
2010-04-22 22:58:50 -04:00
WREG32 ( EVERGREEN_DC_LUT_RW_MODE + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc - > crtc_offset , 0x00000007 ) ;
2010-01-12 17:54:34 -05:00
2010-04-22 22:58:50 -04:00
WREG32 ( EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc - > crtc_offset , 0 ) ;
2010-01-12 17:54:34 -05:00
for ( i = 0 ; i < 256 ; i + + ) {
2010-04-22 22:58:50 -04:00
WREG32 ( EVERGREEN_DC_LUT_30_COLOR + radeon_crtc - > crtc_offset ,
2010-01-12 17:54:34 -05:00
( radeon_crtc - > lut_r [ i ] < < 20 ) |
( radeon_crtc - > lut_g [ i ] < < 10 ) |
( radeon_crtc - > lut_b [ i ] < < 0 ) ) ;
}
}
2009-06-05 14:42:42 +02:00
static void legacy_crtc_load_lut ( struct drm_crtc * crtc )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
int i ;
uint32_t dac2_cntl ;
dac2_cntl = RREG32 ( RADEON_DAC_CNTL2 ) ;
if ( radeon_crtc - > crtc_id = = 0 )
dac2_cntl & = ( uint32_t ) ~ RADEON_DAC2_PALETTE_ACC_CTL ;
else
dac2_cntl | = RADEON_DAC2_PALETTE_ACC_CTL ;
WREG32 ( RADEON_DAC_CNTL2 , dac2_cntl ) ;
WREG8 ( RADEON_PALETTE_INDEX , 0 ) ;
for ( i = 0 ; i < 256 ; i + + ) {
WREG32 ( RADEON_PALETTE_30_DATA ,
( radeon_crtc - > lut_r [ i ] < < 20 ) |
( radeon_crtc - > lut_g [ i ] < < 10 ) |
( radeon_crtc - > lut_b [ i ] < < 0 ) ) ;
}
}
void radeon_crtc_load_lut ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
if ( ! crtc - > enabled )
return ;
2010-01-12 17:54:34 -05:00
if ( ASIC_IS_DCE4 ( rdev ) )
evergreen_crtc_load_lut ( crtc ) ;
else if ( ASIC_IS_AVIVO ( rdev ) )
2009-06-05 14:42:42 +02:00
avivo_crtc_load_lut ( crtc ) ;
else
legacy_crtc_load_lut ( crtc ) ;
}
2009-10-06 13:54:01 +10:00
/** Sets the color ramps on behalf of fbcon */
2009-06-05 14:42:42 +02:00
void radeon_crtc_fb_gamma_set ( struct drm_crtc * crtc , u16 red , u16 green ,
u16 blue , int regno )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
radeon_crtc - > lut_r [ regno ] = red > > 6 ;
radeon_crtc - > lut_g [ regno ] = green > > 6 ;
radeon_crtc - > lut_b [ regno ] = blue > > 6 ;
}
2009-10-06 13:54:01 +10:00
/** Gets the color ramps on behalf of fbcon */
void radeon_crtc_fb_gamma_get ( struct drm_crtc * crtc , u16 * red , u16 * green ,
u16 * blue , int regno )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
* red = radeon_crtc - > lut_r [ regno ] < < 6 ;
* green = radeon_crtc - > lut_g [ regno ] < < 6 ;
* blue = radeon_crtc - > lut_b [ regno ] < < 6 ;
}
2009-06-05 14:42:42 +02:00
static void radeon_crtc_gamma_set ( struct drm_crtc * crtc , u16 * red , u16 * green ,
2010-08-03 01:33:19 +01:00
u16 * blue , uint32_t start , uint32_t size )
2009-06-05 14:42:42 +02:00
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
2010-08-03 01:33:19 +01:00
int end = ( start + size > 256 ) ? 256 : start + size , i ;
2009-06-05 14:42:42 +02:00
2009-10-06 13:54:01 +10:00
/* userspace palettes are always correct as is */
2010-08-03 01:33:19 +01:00
for ( i = start ; i < end ; i + + ) {
2009-10-06 13:54:01 +10:00
radeon_crtc - > lut_r [ i ] = red [ i ] > > 6 ;
radeon_crtc - > lut_g [ i ] = green [ i ] > > 6 ;
radeon_crtc - > lut_b [ i ] = blue [ i ] > > 6 ;
2009-06-05 14:42:42 +02:00
}
radeon_crtc_load_lut ( crtc ) ;
}
static void radeon_crtc_destroy ( struct drm_crtc * crtc )
{
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
drm_crtc_cleanup ( crtc ) ;
kfree ( radeon_crtc ) ;
}
static const struct drm_crtc_funcs radeon_crtc_funcs = {
. cursor_set = radeon_crtc_cursor_set ,
. cursor_move = radeon_crtc_cursor_move ,
. gamma_set = radeon_crtc_gamma_set ,
. set_config = drm_crtc_helper_set_config ,
. destroy = radeon_crtc_destroy ,
} ;
static void radeon_crtc_init ( struct drm_device * dev , int index )
{
struct radeon_device * rdev = dev - > dev_private ;
struct radeon_crtc * radeon_crtc ;
int i ;
radeon_crtc = kzalloc ( sizeof ( struct radeon_crtc ) + ( RADEONFB_CONN_LIMIT * sizeof ( struct drm_connector * ) ) , GFP_KERNEL ) ;
if ( radeon_crtc = = NULL )
return ;
drm_crtc_init ( dev , & radeon_crtc - > base , & radeon_crtc_funcs ) ;
drm_mode_crtc_set_gamma_size ( & radeon_crtc - > base , 256 ) ;
radeon_crtc - > crtc_id = index ;
2009-07-13 21:04:08 +02:00
rdev - > mode_info . crtcs [ index ] = radeon_crtc ;
2009-06-05 14:42:42 +02:00
2009-08-28 15:46:53 +10:00
#if 0
2009-06-05 14:42:42 +02:00
radeon_crtc - > mode_set . crtc = & radeon_crtc - > base ;
radeon_crtc - > mode_set . connectors = ( struct drm_connector * * ) ( radeon_crtc + 1 ) ;
radeon_crtc - > mode_set . num_connectors = 0 ;
2009-08-28 15:46:53 +10:00
# endif
2009-06-05 14:42:42 +02:00
for ( i = 0 ; i < 256 ; i + + ) {
radeon_crtc - > lut_r [ i ] = i < < 2 ;
radeon_crtc - > lut_g [ i ] = i < < 2 ;
radeon_crtc - > lut_b [ i ] = i < < 2 ;
}
if ( rdev - > is_atom_bios & & ( ASIC_IS_AVIVO ( rdev ) | | radeon_r4xx_atom ) )
radeon_atombios_init_crtc ( dev , radeon_crtc ) ;
else
radeon_legacy_init_crtc ( dev , radeon_crtc ) ;
}
static const char * encoder_names [ 34 ] = {
" NONE " ,
" INTERNAL_LVDS " ,
" INTERNAL_TMDS1 " ,
" INTERNAL_TMDS2 " ,
" INTERNAL_DAC1 " ,
" INTERNAL_DAC2 " ,
" INTERNAL_SDVOA " ,
" INTERNAL_SDVOB " ,
" SI170B " ,
" CH7303 " ,
" CH7301 " ,
" INTERNAL_DVO1 " ,
" EXTERNAL_SDVOA " ,
" EXTERNAL_SDVOB " ,
" TITFP513 " ,
" INTERNAL_LVTM1 " ,
" VT1623 " ,
" HDMI_SI1930 " ,
" HDMI_INTERNAL " ,
" INTERNAL_KLDSCP_TMDS1 " ,
" INTERNAL_KLDSCP_DVO1 " ,
" INTERNAL_KLDSCP_DAC1 " ,
" INTERNAL_KLDSCP_DAC2 " ,
" SI178 " ,
" MVPU_FPGA " ,
" INTERNAL_DDI " ,
" VT1625 " ,
" HDMI_SI1932 " ,
" DP_AN9801 " ,
" DP_DP501 " ,
" INTERNAL_UNIPHY " ,
" INTERNAL_KLDSCP_LVTMA " ,
" INTERNAL_UNIPHY1 " ,
" INTERNAL_UNIPHY2 " ,
} ;
2010-01-07 14:22:32 -05:00
static const char * connector_names [ 15 ] = {
2009-06-05 14:42:42 +02:00
" Unknown " ,
" VGA " ,
" DVI-I " ,
" DVI-D " ,
" DVI-A " ,
" Composite " ,
" S-video " ,
" LVDS " ,
" Component " ,
" DIN " ,
" DisplayPort " ,
" HDMI-A " ,
" HDMI-B " ,
2010-01-07 14:22:32 -05:00
" TV " ,
" eDP " ,
2009-06-05 14:42:42 +02:00
} ;
2010-06-07 02:24:54 -04:00
static const char * hpd_names [ 6 ] = {
2009-12-04 14:45:27 -05:00
" HPD1 " ,
" HPD2 " ,
" HPD3 " ,
" HPD4 " ,
" HPD5 " ,
" HPD6 " ,
} ;
2009-06-05 14:42:42 +02:00
static void radeon_print_display_setup ( struct drm_device * dev )
{
struct drm_connector * connector ;
struct radeon_connector * radeon_connector ;
struct drm_encoder * encoder ;
struct radeon_encoder * radeon_encoder ;
uint32_t devices ;
int i = 0 ;
DRM_INFO ( " Radeon Display Connectors \n " ) ;
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
radeon_connector = to_radeon_connector ( connector ) ;
DRM_INFO ( " Connector %d: \n " , i ) ;
DRM_INFO ( " %s \n " , connector_names [ connector - > connector_type ] ) ;
2009-12-04 14:45:27 -05:00
if ( radeon_connector - > hpd . hpd ! = RADEON_HPD_NONE )
DRM_INFO ( " %s \n " , hpd_names [ radeon_connector - > hpd . hpd ] ) ;
2010-02-08 13:16:55 +10:00
if ( radeon_connector - > ddc_bus ) {
2009-06-05 14:42:42 +02:00
DRM_INFO ( " DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x \n " ,
radeon_connector - > ddc_bus - > rec . mask_clk_reg ,
radeon_connector - > ddc_bus - > rec . mask_data_reg ,
radeon_connector - > ddc_bus - > rec . a_clk_reg ,
radeon_connector - > ddc_bus - > rec . a_data_reg ,
2009-11-10 15:59:44 -05:00
radeon_connector - > ddc_bus - > rec . en_clk_reg ,
radeon_connector - > ddc_bus - > rec . en_data_reg ,
radeon_connector - > ddc_bus - > rec . y_clk_reg ,
radeon_connector - > ddc_bus - > rec . y_data_reg ) ;
2010-11-08 16:08:29 +00:00
if ( radeon_connector - > router . ddc_valid )
2010-08-05 21:21:18 -04:00
DRM_INFO ( " DDC Router 0x%x/0x%x \n " ,
2010-11-08 16:08:29 +00:00
radeon_connector - > router . ddc_mux_control_pin ,
radeon_connector - > router . ddc_mux_state ) ;
if ( radeon_connector - > router . cd_valid )
DRM_INFO ( " Clock/Data Router 0x%x/0x%x \n " ,
radeon_connector - > router . cd_mux_control_pin ,
radeon_connector - > router . cd_mux_state ) ;
2010-02-08 13:16:55 +10:00
} else {
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_VGA | |
connector - > connector_type = = DRM_MODE_CONNECTOR_DVII | |
connector - > connector_type = = DRM_MODE_CONNECTOR_DVID | |
connector - > connector_type = = DRM_MODE_CONNECTOR_DVIA | |
connector - > connector_type = = DRM_MODE_CONNECTOR_HDMIA | |
connector - > connector_type = = DRM_MODE_CONNECTOR_HDMIB )
DRM_INFO ( " DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org \n " ) ;
}
2009-06-05 14:42:42 +02:00
DRM_INFO ( " Encoders: \n " ) ;
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
radeon_encoder = to_radeon_encoder ( encoder ) ;
devices = radeon_encoder - > devices & radeon_connector - > devices ;
if ( devices ) {
if ( devices & ATOM_DEVICE_CRT1_SUPPORT )
DRM_INFO ( " CRT1: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_CRT2_SUPPORT )
DRM_INFO ( " CRT2: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_LCD1_SUPPORT )
DRM_INFO ( " LCD1: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_DFP1_SUPPORT )
DRM_INFO ( " DFP1: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_DFP2_SUPPORT )
DRM_INFO ( " DFP2: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_DFP3_SUPPORT )
DRM_INFO ( " DFP3: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_DFP4_SUPPORT )
DRM_INFO ( " DFP4: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_DFP5_SUPPORT )
DRM_INFO ( " DFP5: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
2010-09-24 14:59:32 -04:00
if ( devices & ATOM_DEVICE_DFP6_SUPPORT )
DRM_INFO ( " DFP6: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
2009-06-05 14:42:42 +02:00
if ( devices & ATOM_DEVICE_TV1_SUPPORT )
DRM_INFO ( " TV1: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
if ( devices & ATOM_DEVICE_CV_SUPPORT )
DRM_INFO ( " CV: %s \n " , encoder_names [ radeon_encoder - > encoder_id ] ) ;
}
}
i + + ;
}
}
2009-08-13 16:32:14 +10:00
static bool radeon_setup_enc_conn ( struct drm_device * dev )
2009-06-05 14:42:42 +02:00
{
struct radeon_device * rdev = dev - > dev_private ;
struct drm_connector * drm_connector ;
bool ret = false ;
if ( rdev - > bios ) {
if ( rdev - > is_atom_bios ) {
2010-03-18 01:04:01 -04:00
ret = radeon_get_atom_connector_info_from_supported_devices_table ( dev ) ;
if ( ret = = false )
2009-06-05 14:42:42 +02:00
ret = radeon_get_atom_connector_info_from_object_table ( dev ) ;
2010-01-04 19:12:02 -05:00
} else {
2009-06-05 14:42:42 +02:00
ret = radeon_get_legacy_connector_info_from_bios ( dev ) ;
2010-01-04 19:12:02 -05:00
if ( ret = = false )
ret = radeon_get_legacy_connector_info_from_table ( dev ) ;
}
2009-06-05 14:42:42 +02:00
} else {
if ( ! ASIC_IS_AVIVO ( rdev ) )
ret = radeon_get_legacy_connector_info_from_table ( dev ) ;
}
if ( ret ) {
2009-10-13 14:10:37 +10:00
radeon_setup_encoder_clones ( dev ) ;
2009-06-05 14:42:42 +02:00
radeon_print_display_setup ( dev ) ;
list_for_each_entry ( drm_connector , & dev - > mode_config . connector_list , head )
radeon_ddc_dump ( drm_connector ) ;
}
return ret ;
}
int radeon_ddc_get_modes ( struct radeon_connector * radeon_connector )
{
2010-02-05 04:21:19 -05:00
struct drm_device * dev = radeon_connector - > base . dev ;
struct radeon_device * rdev = dev - > dev_private ;
2009-06-05 14:42:42 +02:00
int ret = 0 ;
2010-08-05 21:21:18 -04:00
/* on hw with routers, select right port */
2010-11-08 16:08:29 +00:00
if ( radeon_connector - > router . ddc_valid )
radeon_router_select_ddc_port ( radeon_connector ) ;
2010-08-05 21:21:18 -04:00
2010-01-07 14:22:32 -05:00
if ( ( radeon_connector - > base . connector_type = = DRM_MODE_CONNECTOR_DisplayPort ) | |
( radeon_connector - > base . connector_type = = DRM_MODE_CONNECTOR_eDP ) ) {
2009-12-08 07:07:28 +10:00
struct radeon_connector_atom_dig * dig = radeon_connector - > con_priv ;
2010-01-14 11:42:17 +10:00
if ( ( dig - > dp_sink_type = = CONNECTOR_OBJECT_ID_DISPLAYPORT | |
dig - > dp_sink_type = = CONNECTOR_OBJECT_ID_eDP ) & & dig - > dp_i2c_bus )
2009-11-27 13:01:46 -05:00
radeon_connector - > edid = drm_get_edid ( & radeon_connector - > base , & dig - > dp_i2c_bus - > adapter ) ;
2009-12-08 07:07:28 +10:00
}
2009-06-05 14:42:42 +02:00
if ( ! radeon_connector - > ddc_bus )
return - 1 ;
2009-08-13 16:32:14 +10:00
if ( ! radeon_connector - > edid ) {
2009-10-15 16:16:35 -04:00
radeon_connector - > edid = drm_get_edid ( & radeon_connector - > base , & radeon_connector - > ddc_bus - > adapter ) ;
}
2010-02-05 04:21:19 -05:00
/* some servers provide a hardcoded edid in rom for KVMs */
if ( ! radeon_connector - > edid )
radeon_connector - > edid = radeon_combios_get_hardcoded_edid ( rdev ) ;
2009-10-15 16:16:35 -04:00
if ( radeon_connector - > edid ) {
drm_mode_connector_update_edid_property ( & radeon_connector - > base , radeon_connector - > edid ) ;
ret = drm_add_edid_modes ( & radeon_connector - > base , radeon_connector - > edid ) ;
2009-06-05 14:42:42 +02:00
return ret ;
}
drm_mode_connector_update_edid_property ( & radeon_connector - > base , NULL ) ;
2009-09-15 20:21:11 +10:00
return 0 ;
2009-06-05 14:42:42 +02:00
}
static int radeon_ddc_dump ( struct drm_connector * connector )
{
struct edid * edid ;
struct radeon_connector * radeon_connector = to_radeon_connector ( connector ) ;
int ret = 0 ;
2010-08-05 21:21:18 -04:00
/* on hw with routers, select right port */
2010-11-08 16:08:29 +00:00
if ( radeon_connector - > router . ddc_valid )
radeon_router_select_ddc_port ( radeon_connector ) ;
2010-08-05 21:21:18 -04:00
2009-06-05 14:42:42 +02:00
if ( ! radeon_connector - > ddc_bus )
return - 1 ;
edid = drm_get_edid ( connector , & radeon_connector - > ddc_bus - > adapter ) ;
if ( edid ) {
kfree ( edid ) ;
}
return ret ;
}
static inline uint32_t radeon_div ( uint64_t n , uint32_t d )
{
uint64_t mod ;
n + = d / 2 ;
mod = do_div ( n , d ) ;
return n ;
}
2010-09-29 11:37:41 -04:00
void radeon_compute_pll ( struct radeon_pll * pll ,
uint64_t freq ,
uint32_t * dot_clock_p ,
uint32_t * fb_div_p ,
uint32_t * frac_fb_div_p ,
uint32_t * ref_div_p ,
uint32_t * post_div_p )
2009-06-05 14:42:42 +02:00
{
uint32_t min_ref_div = pll - > min_ref_div ;
uint32_t max_ref_div = pll - > max_ref_div ;
2010-01-19 17:16:10 -05:00
uint32_t min_post_div = pll - > min_post_div ;
uint32_t max_post_div = pll - > max_post_div ;
2009-06-05 14:42:42 +02:00
uint32_t min_fractional_feed_div = 0 ;
uint32_t max_fractional_feed_div = 0 ;
uint32_t best_vco = pll - > best_vco ;
uint32_t best_post_div = 1 ;
uint32_t best_ref_div = 1 ;
uint32_t best_feedback_div = 1 ;
uint32_t best_frac_feedback_div = 0 ;
uint32_t best_freq = - 1 ;
uint32_t best_error = 0xffffffff ;
uint32_t best_vco_diff = 1 ;
uint32_t post_div ;
2010-03-08 12:55:16 -05:00
u32 pll_out_min , pll_out_max ;
2009-06-05 14:42:42 +02:00
2010-08-02 10:42:55 +10:00
DRM_DEBUG_KMS ( " PLL freq %llu %u %u \n " , freq , pll - > min_ref_div , pll - > max_ref_div ) ;
2009-06-05 14:42:42 +02:00
freq = freq * 1000 ;
2010-03-08 12:55:16 -05:00
if ( pll - > flags & RADEON_PLL_IS_LCD ) {
pll_out_min = pll - > lcd_pll_out_min ;
pll_out_max = pll - > lcd_pll_out_max ;
} else {
pll_out_min = pll - > pll_out_min ;
pll_out_max = pll - > pll_out_max ;
}
2010-01-19 17:16:10 -05:00
if ( pll - > flags & RADEON_PLL_USE_REF_DIV )
2009-06-05 14:42:42 +02:00
min_ref_div = max_ref_div = pll - > reference_div ;
else {
while ( min_ref_div < max_ref_div - 1 ) {
uint32_t mid = ( min_ref_div + max_ref_div ) / 2 ;
uint32_t pll_in = pll - > reference_freq / mid ;
if ( pll_in < pll - > pll_in_min )
max_ref_div = mid ;
else if ( pll_in > pll - > pll_in_max )
min_ref_div = mid ;
else
break ;
}
}
2010-01-19 17:16:10 -05:00
if ( pll - > flags & RADEON_PLL_USE_POST_DIV )
min_post_div = max_post_div = pll - > post_div ;
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV ) {
2009-06-05 14:42:42 +02:00
min_fractional_feed_div = pll - > min_frac_feedback_div ;
max_fractional_feed_div = pll - > max_frac_feedback_div ;
}
2010-09-29 11:37:39 -04:00
for ( post_div = max_post_div ; post_div > = min_post_div ; - - post_div ) {
2009-06-05 14:42:42 +02:00
uint32_t ref_div ;
2010-01-19 17:16:10 -05:00
if ( ( pll - > flags & RADEON_PLL_NO_ODD_POST_DIV ) & & ( post_div & 1 ) )
2009-06-05 14:42:42 +02:00
continue ;
/* legacy radeons only have a few post_divs */
2010-01-19 17:16:10 -05:00
if ( pll - > flags & RADEON_PLL_LEGACY ) {
2009-06-05 14:42:42 +02:00
if ( ( post_div = = 5 ) | |
( post_div = = 7 ) | |
( post_div = = 9 ) | |
( post_div = = 10 ) | |
( post_div = = 11 ) | |
( post_div = = 13 ) | |
( post_div = = 14 ) | |
( post_div = = 15 ) )
continue ;
}
for ( ref_div = min_ref_div ; ref_div < = max_ref_div ; + + ref_div ) {
uint32_t feedback_div , current_freq = 0 , error , vco_diff ;
uint32_t pll_in = pll - > reference_freq / ref_div ;
uint32_t min_feed_div = pll - > min_feedback_div ;
uint32_t max_feed_div = pll - > max_feedback_div + 1 ;
if ( pll_in < pll - > pll_in_min | | pll_in > pll - > pll_in_max )
continue ;
while ( min_feed_div < max_feed_div ) {
uint32_t vco ;
uint32_t min_frac_feed_div = min_fractional_feed_div ;
uint32_t max_frac_feed_div = max_fractional_feed_div + 1 ;
uint32_t frac_feedback_div ;
uint64_t tmp ;
feedback_div = ( min_feed_div + max_feed_div ) / 2 ;
tmp = ( uint64_t ) pll - > reference_freq * feedback_div ;
vco = radeon_div ( tmp , ref_div ) ;
2010-03-08 12:55:16 -05:00
if ( vco < pll_out_min ) {
2009-06-05 14:42:42 +02:00
min_feed_div = feedback_div + 1 ;
continue ;
2010-03-08 12:55:16 -05:00
} else if ( vco > pll_out_max ) {
2009-06-05 14:42:42 +02:00
max_feed_div = feedback_div ;
continue ;
}
while ( min_frac_feed_div < max_frac_feed_div ) {
frac_feedback_div = ( min_frac_feed_div + max_frac_feed_div ) / 2 ;
tmp = ( uint64_t ) pll - > reference_freq * 10000 * feedback_div ;
tmp + = ( uint64_t ) pll - > reference_freq * 1000 * frac_feedback_div ;
current_freq = radeon_div ( tmp , ref_div * post_div ) ;
2010-01-19 17:16:10 -05:00
if ( pll - > flags & RADEON_PLL_PREFER_CLOSEST_LOWER ) {
2010-07-17 12:28:02 +02:00
if ( freq < current_freq )
error = 0xffffffff ;
else
error = freq - current_freq ;
2009-07-13 11:08:18 -04:00
} else
error = abs ( current_freq - freq ) ;
2009-06-05 14:42:42 +02:00
vco_diff = abs ( vco - best_vco ) ;
if ( ( best_vco = = 0 & & error < best_error ) | |
( best_vco ! = 0 & &
2010-07-17 12:28:02 +02:00
( ( best_error > 100 & & error < best_error - 100 ) | |
2010-10-19 10:36:47 +10:00
( abs ( error - best_error ) < 100 & & vco_diff < best_vco_diff ) ) ) ) {
2009-06-05 14:42:42 +02:00
best_post_div = post_div ;
best_ref_div = ref_div ;
best_feedback_div = feedback_div ;
best_frac_feedback_div = frac_feedback_div ;
best_freq = current_freq ;
best_error = error ;
best_vco_diff = vco_diff ;
2010-10-19 10:36:47 +10:00
} else if ( current_freq = = freq ) {
if ( best_freq = = - 1 ) {
best_post_div = post_div ;
best_ref_div = ref_div ;
best_feedback_div = feedback_div ;
best_frac_feedback_div = frac_feedback_div ;
best_freq = current_freq ;
best_error = error ;
best_vco_diff = vco_diff ;
} else if ( ( ( pll - > flags & RADEON_PLL_PREFER_LOW_REF_DIV ) & & ( ref_div < best_ref_div ) ) | |
( ( pll - > flags & RADEON_PLL_PREFER_HIGH_REF_DIV ) & & ( ref_div > best_ref_div ) ) | |
( ( pll - > flags & RADEON_PLL_PREFER_LOW_FB_DIV ) & & ( feedback_div < best_feedback_div ) ) | |
( ( pll - > flags & RADEON_PLL_PREFER_HIGH_FB_DIV ) & & ( feedback_div > best_feedback_div ) ) | |
( ( pll - > flags & RADEON_PLL_PREFER_LOW_POST_DIV ) & & ( post_div < best_post_div ) ) | |
( ( pll - > flags & RADEON_PLL_PREFER_HIGH_POST_DIV ) & & ( post_div > best_post_div ) ) ) {
best_post_div = post_div ;
best_ref_div = ref_div ;
best_feedback_div = feedback_div ;
best_frac_feedback_div = frac_feedback_div ;
best_freq = current_freq ;
best_error = error ;
best_vco_diff = vco_diff ;
}
2009-06-05 14:42:42 +02:00
}
if ( current_freq < freq )
min_frac_feed_div = frac_feedback_div + 1 ;
else
max_frac_feed_div = frac_feedback_div ;
}
if ( current_freq < freq )
min_feed_div = feedback_div + 1 ;
else
max_feed_div = feedback_div ;
}
}
}
* dot_clock_p = best_freq / 10000 ;
* fb_div_p = best_feedback_div ;
* frac_fb_div_p = best_frac_feedback_div ;
* ref_div_p = best_ref_div ;
* post_div_p = best_post_div ;
}
static void radeon_user_framebuffer_destroy ( struct drm_framebuffer * fb )
{
struct radeon_framebuffer * radeon_fb = to_radeon_framebuffer ( fb ) ;
2010-09-27 16:17:17 +10:00
if ( radeon_fb - > obj ) {
2010-02-09 05:49:12 +00:00
drm_gem_object_unreference_unlocked ( radeon_fb - > obj ) ;
2010-09-27 16:17:17 +10:00
}
2009-06-05 14:42:42 +02:00
drm_framebuffer_cleanup ( fb ) ;
kfree ( radeon_fb ) ;
}
static int radeon_user_framebuffer_create_handle ( struct drm_framebuffer * fb ,
struct drm_file * file_priv ,
unsigned int * handle )
{
struct radeon_framebuffer * radeon_fb = to_radeon_framebuffer ( fb ) ;
return drm_gem_handle_create ( file_priv , radeon_fb - > obj , handle ) ;
}
static const struct drm_framebuffer_funcs radeon_fb_funcs = {
. destroy = radeon_user_framebuffer_destroy ,
. create_handle = radeon_user_framebuffer_create_handle ,
} ;
2010-03-30 05:34:13 +00:00
void
radeon_framebuffer_init ( struct drm_device * dev ,
struct radeon_framebuffer * rfb ,
struct drm_mode_fb_cmd * mode_cmd ,
struct drm_gem_object * obj )
2009-06-05 14:42:42 +02:00
{
2010-03-30 05:34:13 +00:00
rfb - > obj = obj ;
drm_framebuffer_init ( dev , & rfb - > base , & radeon_fb_funcs ) ;
drm_helper_mode_fill_fb_struct ( & rfb - > base , mode_cmd ) ;
2009-06-05 14:42:42 +02:00
}
static struct drm_framebuffer *
radeon_user_framebuffer_create ( struct drm_device * dev ,
struct drm_file * file_priv ,
struct drm_mode_fb_cmd * mode_cmd )
{
struct drm_gem_object * obj ;
2010-03-30 05:34:13 +00:00
struct radeon_framebuffer * radeon_fb ;
2009-06-05 14:42:42 +02:00
obj = drm_gem_object_lookup ( dev , file_priv , mode_cmd - > handle ) ;
2010-01-17 21:21:41 +01:00
if ( obj = = NULL ) {
dev_err ( & dev - > pdev - > dev , " No GEM object associated to handle 0x%08X, "
" can't create framebuffer \n " , mode_cmd - > handle ) ;
2010-08-08 13:36:38 +01:00
return ERR_PTR ( - ENOENT ) ;
2010-01-17 21:21:41 +01:00
}
2010-03-30 05:34:13 +00:00
radeon_fb = kzalloc ( sizeof ( * radeon_fb ) , GFP_KERNEL ) ;
2010-08-08 13:36:38 +01:00
if ( radeon_fb = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2010-03-30 05:34:13 +00:00
radeon_framebuffer_init ( dev , radeon_fb , mode_cmd , obj ) ;
return & radeon_fb - > base ;
2009-06-05 14:42:42 +02:00
}
2010-05-07 06:42:51 +00:00
static void radeon_output_poll_changed ( struct drm_device * dev )
{
struct radeon_device * rdev = dev - > dev_private ;
radeon_fb_output_poll_changed ( rdev ) ;
}
2009-06-05 14:42:42 +02:00
static const struct drm_mode_config_funcs radeon_mode_funcs = {
. fb_create = radeon_user_framebuffer_create ,
2010-05-07 06:42:51 +00:00
. output_poll_changed = radeon_output_poll_changed
2009-06-05 14:42:42 +02:00
} ;
2009-09-09 17:40:54 +10:00
struct drm_prop_enum_list {
int type ;
char * name ;
} ;
static struct drm_prop_enum_list radeon_tmds_pll_enum_list [ ] =
{ { 0 , " driver " } ,
{ 1 , " bios " } ,
} ;
static struct drm_prop_enum_list radeon_tv_std_enum_list [ ] =
{ { TV_STD_NTSC , " ntsc " } ,
{ TV_STD_PAL , " pal " } ,
{ TV_STD_PAL_M , " pal-m " } ,
{ TV_STD_PAL_60 , " pal-60 " } ,
{ TV_STD_NTSC_J , " ntsc-j " } ,
{ TV_STD_SCART_PAL , " scart-pal " } ,
{ TV_STD_PAL_CN , " pal-cn " } ,
{ TV_STD_SECAM , " secam " } ,
} ;
2010-08-03 19:59:20 -04:00
static struct drm_prop_enum_list radeon_underscan_enum_list [ ] =
{ { UNDERSCAN_OFF , " off " } ,
{ UNDERSCAN_ON , " on " } ,
{ UNDERSCAN_AUTO , " auto " } ,
} ;
2009-12-17 19:00:29 -05:00
static int radeon_modeset_create_props ( struct radeon_device * rdev )
2009-09-09 17:40:54 +10:00
{
int i , sz ;
if ( rdev - > is_atom_bios ) {
rdev - > mode_info . coherent_mode_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_RANGE ,
" coherent " , 2 ) ;
if ( ! rdev - > mode_info . coherent_mode_property )
return - ENOMEM ;
rdev - > mode_info . coherent_mode_property - > values [ 0 ] = 0 ;
2009-12-08 12:48:20 -05:00
rdev - > mode_info . coherent_mode_property - > values [ 1 ] = 1 ;
2009-09-09 17:40:54 +10:00
}
if ( ! ASIC_IS_AVIVO ( rdev ) ) {
sz = ARRAY_SIZE ( radeon_tmds_pll_enum_list ) ;
rdev - > mode_info . tmds_pll_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_ENUM ,
" tmds_pll " , sz ) ;
for ( i = 0 ; i < sz ; i + + ) {
drm_property_add_enum ( rdev - > mode_info . tmds_pll_property ,
i ,
radeon_tmds_pll_enum_list [ i ] . type ,
radeon_tmds_pll_enum_list [ i ] . name ) ;
}
}
rdev - > mode_info . load_detect_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_RANGE ,
" load detection " , 2 ) ;
if ( ! rdev - > mode_info . load_detect_property )
return - ENOMEM ;
rdev - > mode_info . load_detect_property - > values [ 0 ] = 0 ;
2009-12-08 12:48:20 -05:00
rdev - > mode_info . load_detect_property - > values [ 1 ] = 1 ;
2009-09-09 17:40:54 +10:00
drm_mode_create_scaling_mode_property ( rdev - > ddev ) ;
sz = ARRAY_SIZE ( radeon_tv_std_enum_list ) ;
rdev - > mode_info . tv_std_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_ENUM ,
" tv standard " , sz ) ;
for ( i = 0 ; i < sz ; i + + ) {
drm_property_add_enum ( rdev - > mode_info . tv_std_property ,
i ,
radeon_tv_std_enum_list [ i ] . type ,
radeon_tv_std_enum_list [ i ] . name ) ;
}
2010-08-03 19:59:20 -04:00
sz = ARRAY_SIZE ( radeon_underscan_enum_list ) ;
rdev - > mode_info . underscan_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_ENUM ,
" underscan " , sz ) ;
for ( i = 0 ; i < sz ; i + + ) {
drm_property_add_enum ( rdev - > mode_info . underscan_property ,
i ,
radeon_underscan_enum_list [ i ] . type ,
radeon_underscan_enum_list [ i ] . name ) ;
}
2010-09-21 21:30:59 +02:00
rdev - > mode_info . underscan_hborder_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_RANGE ,
" underscan hborder " , 2 ) ;
if ( ! rdev - > mode_info . underscan_hborder_property )
return - ENOMEM ;
rdev - > mode_info . underscan_hborder_property - > values [ 0 ] = 0 ;
rdev - > mode_info . underscan_hborder_property - > values [ 1 ] = 128 ;
rdev - > mode_info . underscan_vborder_property =
drm_property_create ( rdev - > ddev ,
DRM_MODE_PROP_RANGE ,
" underscan vborder " , 2 ) ;
if ( ! rdev - > mode_info . underscan_vborder_property )
return - ENOMEM ;
rdev - > mode_info . underscan_vborder_property - > values [ 0 ] = 0 ;
rdev - > mode_info . underscan_vborder_property - > values [ 1 ] = 128 ;
2009-09-09 17:40:54 +10:00
return 0 ;
}
2010-03-31 00:33:27 -04:00
void radeon_update_display_priority ( struct radeon_device * rdev )
{
/* adjustment options for the display watermarks */
if ( ( radeon_disp_priority = = 0 ) | | ( radeon_disp_priority > 2 ) ) {
/* set display priority to high for r3xx, rv515 chips
* this avoids flickering due to underflow to the
* display controllers during heavy acceleration .
2010-05-20 11:26:11 -04:00
* Don ' t force high on rs4xx igp chips as it seems to
* affect the sound card . See kernel bug 15982.
2010-03-31 00:33:27 -04:00
*/
2010-05-20 11:26:11 -04:00
if ( ( ASIC_IS_R300 ( rdev ) | | ( rdev - > family = = CHIP_RV515 ) ) & &
! ( rdev - > flags & RADEON_IS_IGP ) )
2010-03-31 00:33:27 -04:00
rdev - > disp_priority = 2 ;
else
rdev - > disp_priority = 0 ;
} else
rdev - > disp_priority = radeon_disp_priority ;
}
2009-06-05 14:42:42 +02:00
int radeon_modeset_init ( struct radeon_device * rdev )
{
2010-02-01 16:02:25 -05:00
int i ;
2009-06-05 14:42:42 +02:00
int ret ;
drm_mode_config_init ( rdev - > ddev ) ;
rdev - > mode_info . mode_config_initialized = true ;
rdev - > ddev - > mode_config . funcs = ( void * ) & radeon_mode_funcs ;
if ( ASIC_IS_AVIVO ( rdev ) ) {
rdev - > ddev - > mode_config . max_width = 8192 ;
rdev - > ddev - > mode_config . max_height = 8192 ;
} else {
rdev - > ddev - > mode_config . max_width = 4096 ;
rdev - > ddev - > mode_config . max_height = 4096 ;
}
rdev - > ddev - > mode_config . fb_base = rdev - > mc . aper_base ;
2009-09-09 17:40:54 +10:00
ret = radeon_modeset_create_props ( rdev ) ;
if ( ret ) {
return ret ;
}
2009-10-02 09:19:09 +10:00
2010-08-05 21:21:16 -04:00
/* init i2c buses */
radeon_i2c_init ( rdev ) ;
2010-02-05 04:21:19 -05:00
/* check combios for a valid hardcoded EDID - Sun servers */
if ( ! rdev - > is_atom_bios ) {
/* check for hardcoded EDID in BIOS */
radeon_combios_check_hardcoded_edid ( rdev ) ;
}
2009-10-02 09:19:09 +10:00
/* allocate crtcs */
2010-02-01 16:02:25 -05:00
for ( i = 0 ; i < rdev - > num_crtc ; i + + ) {
2009-06-05 14:42:42 +02:00
radeon_crtc_init ( rdev - > ddev , i ) ;
}
/* okay we should have all the bios connectors */
ret = radeon_setup_enc_conn ( rdev - > ddev ) ;
if ( ! ret ) {
return ret ;
}
2009-12-04 16:56:37 -05:00
/* initialize hpd */
radeon_hpd_init ( rdev ) ;
2010-03-30 05:34:13 +00:00
2010-05-07 15:10:16 -04:00
/* Initialize power management */
radeon_pm_init ( rdev ) ;
2010-03-30 05:34:13 +00:00
radeon_fbdev_init ( rdev ) ;
2010-05-07 06:42:51 +00:00
drm_kms_helper_poll_init ( rdev - > ddev ) ;
2009-06-05 14:42:42 +02:00
return 0 ;
}
void radeon_modeset_fini ( struct radeon_device * rdev )
{
2010-03-30 05:34:13 +00:00
radeon_fbdev_fini ( rdev ) ;
2010-02-05 04:21:19 -05:00
kfree ( rdev - > mode_info . bios_hardcoded_edid ) ;
2010-05-07 15:10:16 -04:00
radeon_pm_fini ( rdev ) ;
2010-02-05 04:21:19 -05:00
2009-06-05 14:42:42 +02:00
if ( rdev - > mode_info . mode_config_initialized ) {
2010-05-07 06:42:51 +00:00
drm_kms_helper_poll_fini ( rdev - > ddev ) ;
2009-12-04 16:56:37 -05:00
radeon_hpd_fini ( rdev ) ;
2009-06-05 14:42:42 +02:00
drm_mode_config_cleanup ( rdev - > ddev ) ;
rdev - > mode_info . mode_config_initialized = false ;
}
2010-08-05 21:21:16 -04:00
/* free i2c buses */
radeon_i2c_fini ( rdev ) ;
2009-06-05 14:42:42 +02:00
}
2010-08-20 11:57:19 -04:00
static bool is_hdtv_mode ( struct drm_display_mode * mode )
{
/* try and guess if this is a tv or a monitor */
if ( ( mode - > vdisplay = = 480 & & mode - > hdisplay = = 720 ) | | /* 480p */
( mode - > vdisplay = = 576 ) | | /* 576p */
( mode - > vdisplay = = 720 ) | | /* 720p */
( mode - > vdisplay = = 1080 ) ) /* 1080p */
return true ;
else
return false ;
}
2009-07-13 21:04:08 +02:00
bool radeon_crtc_scaling_mode_fixup ( struct drm_crtc * crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
2009-06-05 14:42:42 +02:00
{
2009-07-13 21:04:08 +02:00
struct drm_device * dev = crtc - > dev ;
2010-08-03 19:59:20 -04:00
struct radeon_device * rdev = dev - > dev_private ;
2009-07-13 21:04:08 +02:00
struct drm_encoder * encoder ;
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
struct radeon_encoder * radeon_encoder ;
2010-08-03 19:59:20 -04:00
struct drm_connector * connector ;
struct radeon_connector * radeon_connector ;
2009-07-13 21:04:08 +02:00
bool first = true ;
2010-08-03 19:58:49 -04:00
u32 src_v = 1 , dst_v = 1 ;
u32 src_h = 1 , dst_h = 1 ;
2009-06-05 14:42:42 +02:00
2010-08-03 19:59:20 -04:00
radeon_crtc - > h_border = 0 ;
radeon_crtc - > v_border = 0 ;
2009-07-13 21:04:08 +02:00
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
if ( encoder - > crtc ! = crtc )
continue ;
2010-08-03 19:58:49 -04:00
radeon_encoder = to_radeon_encoder ( encoder ) ;
2010-08-03 19:59:20 -04:00
connector = radeon_get_connector_for_encoder ( encoder ) ;
radeon_connector = to_radeon_connector ( connector ) ;
2009-07-13 21:04:08 +02:00
if ( first ) {
2009-11-12 14:55:14 -05:00
/* set scaling */
if ( radeon_encoder - > rmx_type = = RMX_OFF )
radeon_crtc - > rmx_type = RMX_OFF ;
else if ( mode - > hdisplay < radeon_encoder - > native_mode . hdisplay | |
mode - > vdisplay < radeon_encoder - > native_mode . vdisplay )
radeon_crtc - > rmx_type = radeon_encoder - > rmx_type ;
else
radeon_crtc - > rmx_type = RMX_OFF ;
/* copy native mode */
2009-07-13 21:04:08 +02:00
memcpy ( & radeon_crtc - > native_mode ,
2009-11-12 14:55:14 -05:00
& radeon_encoder - > native_mode ,
2009-10-09 15:14:30 -04:00
sizeof ( struct drm_display_mode ) ) ;
2010-09-07 13:26:39 -04:00
src_v = crtc - > mode . vdisplay ;
dst_v = radeon_crtc - > native_mode . vdisplay ;
src_h = crtc - > mode . hdisplay ;
dst_h = radeon_crtc - > native_mode . hdisplay ;
2010-08-03 19:59:20 -04:00
/* fix up for overscan on hdmi */
if ( ASIC_IS_AVIVO ( rdev ) & &
2010-09-10 03:19:05 -04:00
( ! ( mode - > flags & DRM_MODE_FLAG_INTERLACE ) ) & &
2010-08-03 19:59:20 -04:00
( ( radeon_encoder - > underscan_type = = UNDERSCAN_ON ) | |
( ( radeon_encoder - > underscan_type = = UNDERSCAN_AUTO ) & &
2010-08-20 11:57:19 -04:00
drm_detect_hdmi_monitor ( radeon_connector - > edid ) & &
is_hdtv_mode ( mode ) ) ) ) {
2010-09-21 21:30:59 +02:00
if ( radeon_encoder - > underscan_hborder ! = 0 )
radeon_crtc - > h_border = radeon_encoder - > underscan_hborder ;
else
radeon_crtc - > h_border = ( mode - > hdisplay > > 5 ) + 16 ;
if ( radeon_encoder - > underscan_vborder ! = 0 )
radeon_crtc - > v_border = radeon_encoder - > underscan_vborder ;
else
radeon_crtc - > v_border = ( mode - > vdisplay > > 5 ) + 16 ;
2010-08-03 19:59:20 -04:00
radeon_crtc - > rmx_type = RMX_FULL ;
src_v = crtc - > mode . vdisplay ;
dst_v = crtc - > mode . vdisplay - ( radeon_crtc - > v_border * 2 ) ;
src_h = crtc - > mode . hdisplay ;
dst_h = crtc - > mode . hdisplay - ( radeon_crtc - > h_border * 2 ) ;
}
2009-07-13 21:04:08 +02:00
first = false ;
} else {
if ( radeon_crtc - > rmx_type ! = radeon_encoder - > rmx_type ) {
/* WARNING: Right now this can't happen but
* in the future we need to check that scaling
2010-08-03 19:58:49 -04:00
* are consistent across different encoder
2009-07-13 21:04:08 +02:00
* ( ie all encoder can work with the same
* scaling ) .
*/
2010-08-03 19:58:49 -04:00
DRM_ERROR ( " Scaling not consistent across encoder. \n " ) ;
2009-07-13 21:04:08 +02:00
return false ;
}
2009-06-05 14:42:42 +02:00
}
}
2009-07-13 21:04:08 +02:00
if ( radeon_crtc - > rmx_type ! = RMX_OFF ) {
fixed20_12 a , b ;
2010-08-03 19:58:49 -04:00
a . full = dfixed_const ( src_v ) ;
b . full = dfixed_const ( dst_v ) ;
2010-04-28 11:46:42 +10:00
radeon_crtc - > vsc . full = dfixed_div ( a , b ) ;
2010-08-03 19:58:49 -04:00
a . full = dfixed_const ( src_h ) ;
b . full = dfixed_const ( dst_h ) ;
2010-04-28 11:46:42 +10:00
radeon_crtc - > hsc . full = dfixed_div ( a , b ) ;
2009-06-05 14:42:42 +02:00
} else {
2010-04-28 11:46:42 +10:00
radeon_crtc - > vsc . full = dfixed_const ( 1 ) ;
radeon_crtc - > hsc . full = dfixed_const ( 1 ) ;
2009-06-05 14:42:42 +02:00
}
2009-07-13 21:04:08 +02:00
return true ;
2009-06-05 14:42:42 +02:00
}
2010-10-05 19:57:36 -04:00
/*
* Retrieve current video scanout position of crtc on a given gpu .
*
2010-10-23 04:42:17 +02:00
* \ param dev Device to query .
2010-10-05 19:57:36 -04:00
* \ param crtc Crtc to query .
* \ param * vpos Location where vertical scanout position should be stored .
* \ param * hpos Location where horizontal scanout position should go .
*
* Returns vpos as a positive number while in active scanout area .
* Returns vpos as a negative number inside vblank , counting the number
* of scanlines to go until end of vblank , e . g . , - 1 means " one scanline
* until start of active scanout / end of vblank . "
*
* \ return Flags , or ' ed together as follows :
*
2010-10-23 04:42:17 +02:00
* DRM_SCANOUTPOS_VALID = Query successfull .
* DRM_SCANOUTPOS_INVBL = Inside vblank .
* DRM_SCANOUTPOS_ACCURATE = Returned position is accurate . A lack of
2010-10-05 19:57:36 -04:00
* this flag means that returned position may be offset by a constant but
* unknown small number of scanlines wrt . real scanout position .
*
*/
2010-10-23 04:42:17 +02:00
int radeon_get_crtc_scanoutpos ( struct drm_device * dev , int crtc , int * vpos , int * hpos )
2010-10-05 19:57:36 -04:00
{
u32 stat_crtc = 0 , vbl = 0 , position = 0 ;
int vbl_start , vbl_end , vtotal , ret = 0 ;
bool in_vbl = true ;
2010-10-23 04:42:17 +02:00
struct radeon_device * rdev = dev - > dev_private ;
2010-10-05 19:57:36 -04:00
if ( ASIC_IS_DCE4 ( rdev ) ) {
if ( crtc = = 0 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC0_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC0_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 1 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC1_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC1_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 2 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC2_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC2_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 3 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC3_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC3_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 4 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC4_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC4_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 5 ) {
vbl = RREG32 ( EVERGREEN_CRTC_V_BLANK_START_END +
EVERGREEN_CRTC5_REGISTER_OFFSET ) ;
position = RREG32 ( EVERGREEN_CRTC_STATUS_POSITION +
EVERGREEN_CRTC5_REGISTER_OFFSET ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
} else if ( ASIC_IS_AVIVO ( rdev ) ) {
if ( crtc = = 0 ) {
vbl = RREG32 ( AVIVO_D1CRTC_V_BLANK_START_END ) ;
position = RREG32 ( AVIVO_D1CRTC_STATUS_POSITION ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 1 ) {
vbl = RREG32 ( AVIVO_D2CRTC_V_BLANK_START_END ) ;
position = RREG32 ( AVIVO_D2CRTC_STATUS_POSITION ) ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
} else {
/* Pre-AVIVO: Different encoding of scanout pos and vblank interval. */
if ( crtc = = 0 ) {
/* Assume vbl_end == 0, get vbl_start from
* upper 16 bits .
*/
vbl = ( RREG32 ( RADEON_CRTC_V_TOTAL_DISP ) &
RADEON_CRTC_V_DISP ) > > RADEON_CRTC_V_DISP_SHIFT ;
/* Only retrieve vpos from upper 16 bits, set hpos == 0. */
position = ( RREG32 ( RADEON_CRTC_VLINE_CRNT_VLINE ) > > 16 ) & RADEON_CRTC_V_TOTAL ;
stat_crtc = RREG32 ( RADEON_CRTC_STATUS ) ;
if ( ! ( stat_crtc & 1 ) )
in_vbl = false ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
if ( crtc = = 1 ) {
vbl = ( RREG32 ( RADEON_CRTC2_V_TOTAL_DISP ) &
RADEON_CRTC_V_DISP ) > > RADEON_CRTC_V_DISP_SHIFT ;
position = ( RREG32 ( RADEON_CRTC2_VLINE_CRNT_VLINE ) > > 16 ) & RADEON_CRTC_V_TOTAL ;
stat_crtc = RREG32 ( RADEON_CRTC2_STATUS ) ;
if ( ! ( stat_crtc & 1 ) )
in_vbl = false ;
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_VALID ;
2010-10-05 19:57:36 -04:00
}
}
/* Decode into vertical and horizontal scanout position. */
* vpos = position & 0x1fff ;
* hpos = ( position > > 16 ) & 0x1fff ;
/* Valid vblank area boundaries from gpu retrieved? */
if ( vbl > 0 ) {
/* Yes: Decode. */
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_ACCURATE ;
2010-10-05 19:57:36 -04:00
vbl_start = vbl & 0x1fff ;
vbl_end = ( vbl > > 16 ) & 0x1fff ;
}
else {
/* No: Fake something reasonable which gives at least ok results. */
2010-10-23 04:42:17 +02:00
vbl_start = rdev - > mode_info . crtcs [ crtc ] - > base . hwmode . crtc_vdisplay ;
2010-10-05 19:57:36 -04:00
vbl_end = 0 ;
}
/* Test scanout position against vblank region. */
if ( ( * vpos < vbl_start ) & & ( * vpos > = vbl_end ) )
in_vbl = false ;
/* Check if inside vblank area and apply corrective offsets:
* vpos will then be > = 0 in video scanout area , but negative
* within vblank area , counting down the number of lines until
* start of scanout .
*/
/* Inside "upper part" of vblank area? Apply corrective offset if so: */
if ( in_vbl & & ( * vpos > = vbl_start ) ) {
2010-10-23 04:42:17 +02:00
vtotal = rdev - > mode_info . crtcs [ crtc ] - > base . hwmode . crtc_vtotal ;
2010-10-05 19:57:36 -04:00
* vpos = * vpos - vtotal ;
}
/* Correct for shifted end of vbl at vbl_end. */
* vpos = * vpos - vbl_end ;
/* In vblank? */
if ( in_vbl )
2010-10-23 04:42:17 +02:00
ret | = DRM_SCANOUTPOS_INVBL ;
2010-10-05 19:57:36 -04:00
return ret ;
}