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
*/
2012-10-02 18:01:07 +01:00
# include <drm/drmP.h>
# include <drm/radeon_drm.h>
2009-06-05 14:42:42 +02:00
# include "radeon.h"
# include "atom.h"
# include <asm/div64.h>
2012-09-17 14:40:31 +10:00
# include <linux/pm_runtime.h>
2012-10-02 18:01:07 +01:00
# include <drm/drm_crtc_helper.h>
2014-10-29 10:03:57 +01:00
# include <drm/drm_plane_helper.h>
2012-10-02 18:01:07 +01:00
# include <drm/drm_edid.h>
2009-06-05 14:42:42 +02:00
2014-03-28 18:55:10 +01:00
# include <linux/gcd.h>
2009-06-05 14:42:42 +02:00
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 ) ) ;
}
2014-06-07 03:38:11 +02:00
/* Only change bit 0 of LUT_SEL, other bits are set elsewhere */
WREG32_P ( AVIVO_D1GRPH_LUT_SEL + radeon_crtc - > crtc_offset , radeon_crtc - > crtc_id , ~ 1 ) ;
2009-06-05 14:42:42 +02:00
}
2011-01-06 21:19:30 -05:00
static void dce4_crtc_load_lut ( struct drm_crtc * crtc )
2010-01-12 17:54:34 -05:00
{
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 ) ) ;
}
}
2011-01-06 21:19:30 -05:00
static void dce5_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 ;
DRM_DEBUG_KMS ( " %d \n " , radeon_crtc - > crtc_id ) ;
WREG32 ( NI_INPUT_CSC_CONTROL + radeon_crtc - > crtc_offset ,
( NI_INPUT_CSC_GRPH_MODE ( NI_INPUT_CSC_BYPASS ) |
NI_INPUT_CSC_OVL_MODE ( NI_INPUT_CSC_BYPASS ) ) ) ;
WREG32 ( NI_PRESCALE_GRPH_CONTROL + radeon_crtc - > crtc_offset ,
NI_GRPH_PRESCALE_BYPASS ) ;
WREG32 ( NI_PRESCALE_OVL_CONTROL + radeon_crtc - > crtc_offset ,
NI_OVL_PRESCALE_BYPASS ) ;
WREG32 ( NI_INPUT_GAMMA_CONTROL + radeon_crtc - > crtc_offset ,
( NI_GRPH_INPUT_GAMMA_MODE ( NI_INPUT_GAMMA_USE_LUT ) |
NI_OVL_INPUT_GAMMA_MODE ( NI_INPUT_GAMMA_USE_LUT ) ) ) ;
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 ) ;
WREG32 ( EVERGREEN_DC_LUT_RW_MODE + radeon_crtc - > crtc_offset , 0 ) ;
WREG32 ( EVERGREEN_DC_LUT_WRITE_EN_MASK + radeon_crtc - > crtc_offset , 0x00000007 ) ;
WREG32 ( EVERGREEN_DC_LUT_RW_INDEX + radeon_crtc - > crtc_offset , 0 ) ;
for ( i = 0 ; i < 256 ; i + + ) {
WREG32 ( EVERGREEN_DC_LUT_30_COLOR + radeon_crtc - > crtc_offset ,
( radeon_crtc - > lut_r [ i ] < < 20 ) |
( radeon_crtc - > lut_g [ i ] < < 10 ) |
( radeon_crtc - > lut_b [ i ] < < 0 ) ) ;
}
WREG32 ( NI_DEGAMMA_CONTROL + radeon_crtc - > crtc_offset ,
( NI_GRPH_DEGAMMA_MODE ( NI_DEGAMMA_BYPASS ) |
NI_OVL_DEGAMMA_MODE ( NI_DEGAMMA_BYPASS ) |
NI_ICON_DEGAMMA_MODE ( NI_DEGAMMA_BYPASS ) |
NI_CURSOR_DEGAMMA_MODE ( NI_DEGAMMA_BYPASS ) ) ) ;
WREG32 ( NI_GAMUT_REMAP_CONTROL + radeon_crtc - > crtc_offset ,
( NI_GRPH_GAMUT_REMAP_MODE ( NI_GAMUT_REMAP_BYPASS ) |
NI_OVL_GAMUT_REMAP_MODE ( NI_GAMUT_REMAP_BYPASS ) ) ) ;
WREG32 ( NI_REGAMMA_CONTROL + radeon_crtc - > crtc_offset ,
( NI_GRPH_REGAMMA_MODE ( NI_REGAMMA_BYPASS ) |
NI_OVL_REGAMMA_MODE ( NI_REGAMMA_BYPASS ) ) ) ;
WREG32 ( NI_OUTPUT_CSC_CONTROL + radeon_crtc - > crtc_offset ,
2015-02-23 10:59:36 -05:00
( NI_OUTPUT_CSC_GRPH_MODE ( radeon_crtc - > output_csc ) |
2011-01-06 21:19:30 -05:00
NI_OUTPUT_CSC_OVL_MODE ( NI_OUTPUT_CSC_BYPASS ) ) ) ;
/* XXX match this to the depth of the crtc fmt block, move to modeset? */
WREG32 ( 0x6940 + radeon_crtc - > crtc_offset , 0 ) ;
2013-01-24 10:06:33 -05:00
if ( ASIC_IS_DCE8 ( rdev ) ) {
/* XXX this only needs to be programmed once per crtc at startup,
* not sure where the best place for it is
*/
WREG32 ( CIK_ALPHA_CONTROL + radeon_crtc - > crtc_offset ,
CIK_CURSOR_ALPHA_BLND_ENA ) ;
}
2011-01-06 21:19:30 -05:00
}
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 ;
2011-01-06 21:19:30 -05:00
if ( ASIC_IS_DCE5 ( rdev ) )
dce5_crtc_load_lut ( crtc ) ;
else if ( ASIC_IS_DCE4 ( rdev ) )
dce4_crtc_load_lut ( crtc ) ;
2010-01-12 17:54:34 -05:00
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 ) ;
2014-06-03 18:13:21 -04:00
destroy_workqueue ( radeon_crtc - > flip_queue ) ;
2009-06-05 14:42:42 +02:00
kfree ( radeon_crtc ) ;
}
2014-06-03 18:13:21 -04:00
/**
* radeon_unpin_work_func - unpin old buffer object
*
* @ __work - kernel work item
*
* Unpin the old frame buffer object outside of the interrupt handler
2010-11-21 10:59:01 -05:00
*/
static void radeon_unpin_work_func ( struct work_struct * __work )
{
2014-06-03 18:13:21 -04:00
struct radeon_flip_work * work =
container_of ( __work , struct radeon_flip_work , unpin_work ) ;
2010-11-21 10:59:01 -05:00
int r ;
/* unpin of the old buffer */
r = radeon_bo_reserve ( work - > old_rbo , false ) ;
if ( likely ( r = = 0 ) ) {
r = radeon_bo_unpin ( work - > old_rbo ) ;
if ( unlikely ( r ! = 0 ) ) {
DRM_ERROR ( " failed to unpin buffer after flip \n " ) ;
}
radeon_bo_unreserve ( work - > old_rbo ) ;
} else
DRM_ERROR ( " failed to reserve buffer after flip \n " ) ;
2011-05-29 17:48:32 +10:00
drm_gem_object_unreference_unlocked ( & work - > old_rbo - > gem_base ) ;
2010-11-21 10:59:01 -05:00
kfree ( work ) ;
}
2014-05-27 16:49:21 +02:00
void radeon_crtc_handle_vblank ( struct radeon_device * rdev , int crtc_id )
2010-11-21 10:59:01 -05:00
{
struct radeon_crtc * radeon_crtc = rdev - > mode_info . crtcs [ crtc_id ] ;
unsigned long flags ;
u32 update_pending ;
int vpos , hpos ;
2014-04-23 20:46:06 +02:00
/* can happen during initialization */
if ( radeon_crtc = = NULL )
return ;
2010-11-21 10:59:01 -05:00
2014-07-29 06:21:44 +02:00
/* Skip the pageflip completion check below (based on polling) on
* asics which reliably support hw pageflip completion irqs . pflip
* irqs are a reliable and race - free method of handling pageflip
* completion detection . A use_pflipirq module parameter < 2 allows
* to override this in case of asics with faulty pflip irqs .
* A module parameter of 0 would only use this polling based path ,
* a parameter of 1 would use pflip irq only as a backup to this
* path , as in Linux 3.16 .
*/
if ( ( radeon_use_pflipirq = = 2 ) & & ASIC_IS_DCE4 ( rdev ) )
return ;
2010-11-21 10:59:01 -05:00
spin_lock_irqsave ( & rdev - > ddev - > event_lock , flags ) ;
2014-06-30 18:12:34 +09:00
if ( radeon_crtc - > flip_status ! = RADEON_FLIP_SUBMITTED ) {
DRM_DEBUG_DRIVER ( " radeon_crtc->flip_status = %d != "
" RADEON_FLIP_SUBMITTED(%d) \n " ,
radeon_crtc - > flip_status ,
RADEON_FLIP_SUBMITTED ) ;
2010-11-21 10:59:01 -05:00
spin_unlock_irqrestore ( & rdev - > ddev - > event_lock , flags ) ;
return ;
}
2014-06-03 18:13:21 -04:00
update_pending = radeon_page_flip_pending ( rdev , crtc_id ) ;
2010-11-21 10:59:01 -05:00
/* Has the pageflip already completed in crtc, or is it certain
* to complete in this vblank ?
*/
if ( update_pending & &
2013-10-28 20:50:48 +02:00
( DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos ( rdev - > ddev , crtc_id , 0 ,
2013-10-30 05:13:07 +01:00
& vpos , & hpos , NULL , NULL ) ) & &
2012-02-23 19:16:12 -05:00
( ( vpos > = ( 99 * rdev - > mode_info . crtcs [ crtc_id ] - > base . hwmode . crtc_vdisplay ) / 100 ) | |
( vpos < 0 & & ! ASIC_IS_AVIVO ( rdev ) ) ) ) {
/* crtc didn't flip in this target vblank interval,
* but flip is pending in crtc . Based on the current
* scanout position we know that the current frame is
* ( nearly ) complete and the flip will ( likely )
* complete before the start of the next frame .
*/
update_pending = 0 ;
}
2014-06-03 18:13:21 -04:00
spin_unlock_irqrestore ( & rdev - > ddev - > event_lock , flags ) ;
if ( ! update_pending )
2014-05-27 16:49:21 +02:00
radeon_crtc_handle_flip ( rdev , crtc_id ) ;
}
/**
* radeon_crtc_handle_flip - page flip completed
*
* @ rdev : radeon device pointer
* @ crtc_id : crtc number this event is for
*
* Called when we are sure that a page flip for this crtc is completed .
*/
void radeon_crtc_handle_flip ( struct radeon_device * rdev , int crtc_id )
{
struct radeon_crtc * radeon_crtc = rdev - > mode_info . crtcs [ crtc_id ] ;
2014-06-03 18:13:21 -04:00
struct radeon_flip_work * work ;
2014-05-27 16:49:21 +02:00
unsigned long flags ;
/* this can happen at init */
if ( radeon_crtc = = NULL )
return ;
spin_lock_irqsave ( & rdev - > ddev - > event_lock , flags ) ;
2014-06-03 18:13:21 -04:00
work = radeon_crtc - > flip_work ;
2014-06-30 18:12:34 +09:00
if ( radeon_crtc - > flip_status ! = RADEON_FLIP_SUBMITTED ) {
DRM_DEBUG_DRIVER ( " radeon_crtc->flip_status = %d != "
" RADEON_FLIP_SUBMITTED(%d) \n " ,
radeon_crtc - > flip_status ,
RADEON_FLIP_SUBMITTED ) ;
2014-05-27 16:49:21 +02:00
spin_unlock_irqrestore ( & rdev - > ddev - > event_lock , flags ) ;
return ;
2010-11-21 10:59:01 -05:00
}
2014-06-03 18:13:21 -04:00
/* Pageflip completed. Clean up. */
2014-06-30 18:12:34 +09:00
radeon_crtc - > flip_status = RADEON_FLIP_NONE ;
2014-06-03 18:13:21 -04:00
radeon_crtc - > flip_work = NULL ;
2010-11-21 10:59:01 -05:00
/* wakeup userspace */
2012-10-08 19:50:42 +00:00
if ( work - > event )
drm_send_vblank_event ( rdev - > ddev , crtc_id , work - > event ) ;
2010-11-21 10:59:01 -05:00
spin_unlock_irqrestore ( & rdev - > ddev - > event_lock , flags ) ;
2014-06-17 19:12:03 +09:00
drm_vblank_put ( rdev - > ddev , radeon_crtc - > crtc_id ) ;
2014-06-17 19:12:04 +09:00
radeon_irq_kms_pflip_irq_put ( rdev , work - > crtc_id ) ;
2014-06-03 18:13:21 -04:00
queue_work ( radeon_crtc - > flip_queue , & work - > unpin_work ) ;
2010-11-21 10:59:01 -05:00
}
2014-06-03 18:13:21 -04:00
/**
* radeon_flip_work_func - page flip framebuffer
*
* @ work - kernel work item
*
* Wait for the buffer object to become idle and do the actual page flip
*/
static void radeon_flip_work_func ( struct work_struct * __work )
2010-11-21 10:59:01 -05:00
{
2014-06-03 18:13:21 -04:00
struct radeon_flip_work * work =
container_of ( __work , struct radeon_flip_work , flip_work ) ;
struct radeon_device * rdev = work - > rdev ;
struct radeon_crtc * radeon_crtc = rdev - > mode_info . crtcs [ work - > crtc_id ] ;
2010-11-21 10:59:01 -05:00
2014-06-03 18:13:21 -04:00
struct drm_crtc * crtc = & radeon_crtc - > base ;
unsigned long flags ;
int r ;
2012-12-11 23:42:24 +01:00
2014-06-03 18:13:21 -04:00
down_read ( & rdev - > exclusive_lock ) ;
2014-07-14 15:58:03 +09:00
if ( work - > fence ) {
2014-09-17 14:35:02 +02:00
struct radeon_fence * fence ;
fence = to_radeon_fence ( work - > fence ) ;
if ( fence & & fence - > rdev = = rdev ) {
r = radeon_fence_wait ( fence , false ) ;
if ( r = = - EDEADLK ) {
up_read ( & rdev - > exclusive_lock ) ;
do {
r = radeon_gpu_reset ( rdev ) ;
} while ( r = = - EAGAIN ) ;
down_read ( & rdev - > exclusive_lock ) ;
}
} else
r = fence_wait ( work - > fence , false ) ;
2014-07-14 15:58:03 +09:00
if ( r )
DRM_ERROR ( " failed to wait on page flip fence (%d)! \n " , r ) ;
2010-11-21 10:59:01 -05:00
2014-07-14 15:58:03 +09:00
/* We continue with the page flip even if we failed to wait on
* the fence , otherwise the DRM core and userspace will be
* confused about which BO the CRTC is scanning out
*/
2014-09-17 14:35:02 +02:00
fence_put ( work - > fence ) ;
work - > fence = NULL ;
2010-11-21 10:59:01 -05:00
}
2014-07-14 15:48:42 +09:00
/* We borrow the event spin lock for protecting flip_status */
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
/* set the proper interrupt */
radeon_irq_kms_pflip_irq_get ( rdev , radeon_crtc - > crtc_id ) ;
2014-07-17 02:24:45 +02:00
/* do the flip (mmio) */
radeon_page_flip ( rdev , radeon_crtc - > crtc_id , work - > base ) ;
2014-07-14 15:48:42 +09:00
radeon_crtc - > flip_status = RADEON_FLIP_SUBMITTED ;
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
up_read ( & rdev - > exclusive_lock ) ;
}
static int radeon_crtc_page_flip ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
struct drm_pending_vblank_event * event ,
uint32_t page_flip_flags )
{
struct drm_device * dev = crtc - > dev ;
struct radeon_device * rdev = dev - > dev_private ;
struct radeon_crtc * radeon_crtc = to_radeon_crtc ( crtc ) ;
struct radeon_framebuffer * old_radeon_fb ;
struct radeon_framebuffer * new_radeon_fb ;
struct drm_gem_object * obj ;
struct radeon_flip_work * work ;
struct radeon_bo * new_rbo ;
uint32_t tiling_flags , pitch_pixels ;
uint64_t base ;
unsigned long flags ;
int r ;
work = kzalloc ( sizeof * work , GFP_KERNEL ) ;
if ( work = = NULL )
return - ENOMEM ;
INIT_WORK ( & work - > flip_work , radeon_flip_work_func ) ;
INIT_WORK ( & work - > unpin_work , radeon_unpin_work_func ) ;
work - > rdev = rdev ;
work - > crtc_id = radeon_crtc - > crtc_id ;
work - > event = event ;
/* schedule unpin of the old buffer */
old_radeon_fb = to_radeon_framebuffer ( crtc - > primary - > fb ) ;
obj = old_radeon_fb - > obj ;
/* take a reference to the old object */
drm_gem_object_reference ( obj ) ;
work - > old_rbo = gem_to_radeon_bo ( obj ) ;
new_radeon_fb = to_radeon_framebuffer ( fb ) ;
obj = new_radeon_fb - > obj ;
new_rbo = gem_to_radeon_bo ( obj ) ;
2010-11-21 10:59:01 -05:00
/* pin the new buffer */
2014-07-14 15:48:42 +09:00
DRM_DEBUG_DRIVER ( " flip-ioctl() cur_rbo = %p, new_rbo = %p \n " ,
work - > old_rbo , new_rbo ) ;
2010-11-21 10:59:01 -05:00
2014-07-14 15:48:42 +09:00
r = radeon_bo_reserve ( new_rbo , false ) ;
2010-11-21 10:59:01 -05:00
if ( unlikely ( r ! = 0 ) ) {
DRM_ERROR ( " failed to reserve new rbo buffer before flip \n " ) ;
2014-06-03 18:13:21 -04:00
goto cleanup ;
2010-11-21 10:59:01 -05:00
}
2012-03-14 17:12:42 +01:00
/* Only 27 bit offset for legacy CRTC */
2014-07-14 15:48:42 +09:00
r = radeon_bo_pin_restricted ( new_rbo , RADEON_GEM_DOMAIN_VRAM ,
2012-03-14 17:12:42 +01:00
ASIC_IS_AVIVO ( rdev ) ? 0 : 1 < < 27 , & base ) ;
2010-11-21 10:59:01 -05:00
if ( unlikely ( r ! = 0 ) ) {
2014-07-14 15:48:42 +09:00
radeon_bo_unreserve ( new_rbo ) ;
2010-11-21 10:59:01 -05:00
r = - EINVAL ;
DRM_ERROR ( " failed to pin new rbo buffer before flip \n " ) ;
2014-06-03 18:13:21 -04:00
goto cleanup ;
2010-11-21 10:59:01 -05:00
}
2014-09-17 14:35:02 +02:00
work - > fence = fence_get ( reservation_object_get_excl ( new_rbo - > tbo . resv ) ) ;
2014-07-14 15:48:42 +09:00
radeon_bo_get_tiling_flags ( new_rbo , & tiling_flags , NULL ) ;
radeon_bo_unreserve ( new_rbo ) ;
2010-11-21 10:59:01 -05:00
if ( ! ASIC_IS_AVIVO ( rdev ) ) {
/* crtc offset is from display base addr not FB location */
base - = radeon_crtc - > legacy_display_base_addr ;
2011-12-20 00:06:49 +02:00
pitch_pixels = fb - > pitches [ 0 ] / ( fb - > bits_per_pixel / 8 ) ;
2010-11-21 10:59:01 -05:00
if ( tiling_flags & RADEON_TILING_MACRO ) {
if ( ASIC_IS_R300 ( rdev ) ) {
base & = ~ 0x7ff ;
} else {
int byteshift = fb - > bits_per_pixel > > 4 ;
int tile_addr = ( ( ( crtc - > y > > 3 ) * pitch_pixels + crtc - > x ) > > ( 8 - byteshift ) ) < < 11 ;
base + = tile_addr + ( ( crtc - > x < < byteshift ) % 256 ) + ( ( crtc - > y % 8 ) < < 8 ) ;
}
} else {
int offset = crtc - > y * pitch_pixels + crtc - > x ;
switch ( fb - > bits_per_pixel ) {
case 8 :
default :
offset * = 1 ;
break ;
case 15 :
case 16 :
offset * = 2 ;
break ;
case 24 :
offset * = 3 ;
break ;
case 32 :
offset * = 4 ;
break ;
}
base + = offset ;
}
base & = ~ 7 ;
}
2014-07-14 15:48:42 +09:00
work - > base = base ;
2010-11-21 10:59:01 -05:00
2014-06-17 19:12:03 +09:00
r = drm_vblank_get ( crtc - > dev , radeon_crtc - > crtc_id ) ;
if ( r ) {
DRM_ERROR ( " failed to get vblank before flip \n " ) ;
goto pflip_cleanup ;
}
2014-06-03 18:13:21 -04:00
/* We borrow the event spin lock for protecting flip_work */
spin_lock_irqsave ( & crtc - > dev - > event_lock , flags ) ;
2014-06-04 11:59:31 +10:00
2014-07-14 15:48:42 +09:00
if ( radeon_crtc - > flip_status ! = RADEON_FLIP_NONE ) {
DRM_DEBUG_DRIVER ( " flip queue: crtc already busy \n " ) ;
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
r = - EBUSY ;
2014-07-17 01:37:53 +02:00
goto vblank_cleanup ;
2014-07-14 15:48:42 +09:00
}
radeon_crtc - > flip_status = RADEON_FLIP_PENDING ;
radeon_crtc - > flip_work = work ;
2010-11-21 10:59:01 -05:00
2014-07-14 15:48:42 +09:00
/* update crtc fb */
crtc - > primary - > fb = fb ;
2014-06-03 18:13:21 -04:00
spin_unlock_irqrestore ( & crtc - > dev - > event_lock , flags ) ;
2014-07-14 15:48:42 +09:00
queue_work ( radeon_crtc - > flip_queue , & work - > flip_work ) ;
return 0 ;
2014-05-27 16:49:22 +02:00
2014-07-17 01:37:53 +02:00
vblank_cleanup :
drm_vblank_put ( crtc - > dev , radeon_crtc - > crtc_id ) ;
2014-06-17 19:12:03 +09:00
pflip_cleanup :
2014-07-14 15:48:42 +09:00
if ( unlikely ( radeon_bo_reserve ( new_rbo , false ) ! = 0 ) ) {
2014-06-17 19:12:03 +09:00
DRM_ERROR ( " failed to reserve new rbo in error path \n " ) ;
goto cleanup ;
}
2014-07-14 15:48:42 +09:00
if ( unlikely ( radeon_bo_unpin ( new_rbo ) ! = 0 ) ) {
2014-06-17 19:12:03 +09:00
DRM_ERROR ( " failed to unpin new rbo in error path \n " ) ;
}
2014-07-14 15:48:42 +09:00
radeon_bo_unreserve ( new_rbo ) ;
2014-06-17 19:12:03 +09:00
2014-06-03 18:13:21 -04:00
cleanup :
drm_gem_object_unreference_unlocked ( & work - > old_rbo - > gem_base ) ;
2014-09-17 14:35:02 +02:00
fence_put ( work - > fence ) ;
2010-11-21 10:59:01 -05:00
kfree ( work ) ;
2014-07-14 15:48:42 +09:00
return r ;
2010-11-21 10:59:01 -05:00
}
2012-09-17 14:40:31 +10:00
static int
radeon_crtc_set_config ( struct drm_mode_set * set )
{
struct drm_device * dev ;
struct radeon_device * rdev ;
struct drm_crtc * crtc ;
bool active = false ;
int ret ;
if ( ! set | | ! set - > crtc )
return - EINVAL ;
dev = set - > crtc - > dev ;
ret = pm_runtime_get_sync ( dev - > dev ) ;
if ( ret < 0 )
return ret ;
ret = drm_crtc_helper_set_config ( set ) ;
list_for_each_entry ( crtc , & dev - > mode_config . crtc_list , head )
if ( crtc - > enabled )
active = true ;
pm_runtime_mark_last_busy ( dev - > dev ) ;
rdev = dev - > dev_private ;
/* if we have active crtcs and we don't have a power ref,
take the current one */
if ( active & & ! rdev - > have_disp_power_ref ) {
rdev - > have_disp_power_ref = true ;
return ret ;
}
/* if we have no active crtcs, then drop the power ref
we got before */
if ( ! active & & rdev - > have_disp_power_ref ) {
pm_runtime_put_autosuspend ( dev - > dev ) ;
rdev - > have_disp_power_ref = false ;
}
/* drop the power reference we got coming in here */
pm_runtime_put_autosuspend ( dev - > dev ) ;
return ret ;
}
2009-06-05 14:42:42 +02:00
static const struct drm_crtc_funcs radeon_crtc_funcs = {
2014-11-18 18:00:08 +09:00
. cursor_set2 = radeon_crtc_cursor_set2 ,
2009-06-05 14:42:42 +02:00
. cursor_move = radeon_crtc_cursor_move ,
. gamma_set = radeon_crtc_gamma_set ,
2012-09-17 14:40:31 +10:00
. set_config = radeon_crtc_set_config ,
2009-06-05 14:42:42 +02:00
. destroy = radeon_crtc_destroy ,
2010-11-21 10:59:01 -05:00
. page_flip = radeon_crtc_page_flip ,
2009-06-05 14:42:42 +02:00
} ;
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 ;
2014-06-03 18:13:21 -04:00
radeon_crtc - > flip_queue = create_singlethread_workqueue ( " radeon-crtc " ) ;
2009-07-13 21:04:08 +02:00
rdev - > mode_info . crtcs [ index ] = radeon_crtc ;
2009-06-05 14:42:42 +02:00
2013-01-24 10:06:33 -05:00
if ( rdev - > family > = CHIP_BONAIRE ) {
radeon_crtc - > max_cursor_width = CIK_CURSOR_WIDTH ;
radeon_crtc - > max_cursor_height = CIK_CURSOR_HEIGHT ;
} else {
radeon_crtc - > max_cursor_width = CURSOR_WIDTH ;
radeon_crtc - > max_cursor_height = CURSOR_HEIGHT ;
}
2014-02-12 12:56:53 -05:00
dev - > mode_config . cursor_width = radeon_crtc - > max_cursor_width ;
dev - > mode_config . cursor_height = radeon_crtc - > max_cursor_height ;
2013-01-24 10:06:33 -05: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 ) ;
}
2012-09-06 14:32:06 -04:00
static const char * encoder_names [ 38 ] = {
2009-06-05 14:42:42 +02:00
" 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-11-22 17:56:24 -05:00
" NUTMEG " ,
" TRAVIS " ,
2012-09-06 14:32:06 -04:00
" INTERNAL_VCE " ,
" INTERNAL_UNIPHY3 " ,
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 ) ;
2014-06-03 14:56:19 +03:00
DRM_INFO ( " %s \n " , connector - > name ) ;
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 ;
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 ) ;
}
return ret ;
}
2011-01-31 16:48:52 -05:00
/* avivo */
2014-03-28 18:55:10 +01:00
/**
* avivo_reduce_ratio - fractional number reduction
*
* @ nom : nominator
* @ den : denominator
* @ nom_min : minimum value for nominator
* @ den_min : minimum value for denominator
*
* Find the greatest common divisor and apply it on both nominator and
* denominator , but make nominator and denominator are at least as large
* as their minimum values .
*/
static void avivo_reduce_ratio ( unsigned * nom , unsigned * den ,
unsigned nom_min , unsigned den_min )
2011-01-31 16:48:52 -05:00
{
2014-03-28 18:55:10 +01:00
unsigned tmp ;
/* reduce the numbers to a simpler ratio */
tmp = gcd ( * nom , * den ) ;
* nom / = tmp ;
* den / = tmp ;
/* make sure nominator is large enough */
if ( * nom < nom_min ) {
2014-04-24 18:39:59 +02:00
tmp = DIV_ROUND_UP ( nom_min , * nom ) ;
2014-03-28 18:55:10 +01:00
* nom * = tmp ;
* den * = tmp ;
2011-01-31 16:48:52 -05:00
}
2014-03-28 18:55:10 +01:00
/* make sure the denominator is large enough */
if ( * den < den_min ) {
2014-04-24 18:39:59 +02:00
tmp = DIV_ROUND_UP ( den_min , * den ) ;
2014-03-28 18:55:10 +01:00
* nom * = tmp ;
* den * = tmp ;
2011-01-31 16:48:52 -05:00
}
}
2014-04-20 13:24:32 +02:00
/**
* avivo_get_fb_ref_div - feedback and ref divider calculation
*
* @ nom : nominator
* @ den : denominator
* @ post_div : post divider
* @ fb_div_max : feedback divider maximum
* @ ref_div_max : reference divider maximum
* @ fb_div : resulting feedback divider
* @ ref_div : resulting reference divider
*
* Calculate feedback and reference divider for a given post divider . Makes
* sure we stay within the limits .
*/
static void avivo_get_fb_ref_div ( unsigned nom , unsigned den , unsigned post_div ,
unsigned fb_div_max , unsigned ref_div_max ,
unsigned * fb_div , unsigned * ref_div )
{
/* limit reference * post divider to a maximum */
2014-05-21 15:25:41 +02:00
ref_div_max = max ( min ( 100 / post_div , ref_div_max ) , 1u ) ;
2014-04-20 13:24:32 +02:00
/* get matching reference and feedback divider */
* ref_div = min ( max ( DIV_ROUND_CLOSEST ( den , post_div ) , 1u ) , ref_div_max ) ;
* fb_div = DIV_ROUND_CLOSEST ( nom * * ref_div * post_div , den ) ;
/* limit fb divider to its maximum */
if ( * fb_div > fb_div_max ) {
* ref_div = DIV_ROUND_CLOSEST ( * ref_div * fb_div_max , * fb_div ) ;
* fb_div = fb_div_max ;
}
}
2014-03-28 18:55:10 +01:00
/**
* radeon_compute_pll_avivo - compute PLL paramaters
*
* @ pll : information about the PLL
* @ dot_clock_p : resulting pixel clock
* fb_div_p : resulting feedback divider
* frac_fb_div_p : fractional part of the feedback divider
* ref_div_p : resulting reference divider
* post_div_p : resulting reference divider
*
* Try to calculate the PLL parameters to generate the given frequency :
* dot_clock = ( ref_freq * feedback_div ) / ( ref_div * post_div )
*/
2011-01-31 16:48:52 -05:00
void radeon_compute_pll_avivo ( struct radeon_pll * pll ,
u32 freq ,
u32 * dot_clock_p ,
u32 * fb_div_p ,
u32 * frac_fb_div_p ,
u32 * ref_div_p ,
u32 * post_div_p )
{
2014-04-20 13:24:32 +02:00
unsigned target_clock = pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV ?
freq : freq / 10 ;
2014-03-28 18:55:10 +01:00
unsigned fb_div_min , fb_div_max , fb_div ;
unsigned post_div_min , post_div_max , post_div ;
unsigned ref_div_min , ref_div_max , ref_div ;
unsigned post_div_best , diff_best ;
2014-04-16 11:54:21 +02:00
unsigned nom , den ;
2011-01-31 16:48:52 -05:00
2014-03-28 18:55:10 +01:00
/* determine allowed feedback divider range */
fb_div_min = pll - > min_feedback_div ;
fb_div_max = pll - > max_feedback_div ;
2011-01-31 16:48:52 -05:00
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV ) {
2014-03-28 18:55:10 +01:00
fb_div_min * = 10 ;
fb_div_max * = 10 ;
}
/* determine allowed ref divider range */
if ( pll - > flags & RADEON_PLL_USE_REF_DIV )
ref_div_min = pll - > reference_div ;
else
ref_div_min = pll - > min_ref_div ;
2014-04-19 18:57:14 +02:00
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV & &
pll - > flags & RADEON_PLL_USE_REF_DIV )
ref_div_max = pll - > reference_div ;
2015-01-29 16:01:03 +01:00
else if ( pll - > flags & RADEON_PLL_PREFER_MINM_OVER_MAXP )
/* fix for problems on RS880 */
ref_div_max = min ( pll - > max_ref_div , 7u ) ;
2014-04-19 18:57:14 +02:00
else
ref_div_max = pll - > max_ref_div ;
2014-03-28 18:55:10 +01:00
/* determine allowed post divider range */
if ( pll - > flags & RADEON_PLL_USE_POST_DIV ) {
post_div_min = pll - > post_div ;
post_div_max = pll - > post_div ;
} else {
unsigned vco_min , vco_max ;
if ( pll - > flags & RADEON_PLL_IS_LCD ) {
vco_min = pll - > lcd_pll_out_min ;
vco_max = pll - > lcd_pll_out_max ;
} else {
vco_min = pll - > pll_out_min ;
vco_max = pll - > pll_out_max ;
2011-01-31 16:48:52 -05:00
}
2014-03-28 18:55:10 +01:00
2014-04-20 13:24:32 +02:00
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV ) {
vco_min * = 10 ;
vco_max * = 10 ;
}
2014-03-28 18:55:10 +01:00
post_div_min = vco_min / target_clock ;
if ( ( target_clock * post_div_min ) < vco_min )
+ + post_div_min ;
if ( post_div_min < pll - > min_post_div )
post_div_min = pll - > min_post_div ;
post_div_max = vco_max / target_clock ;
if ( ( target_clock * post_div_max ) > vco_max )
- - post_div_max ;
if ( post_div_max > pll - > max_post_div )
post_div_max = pll - > max_post_div ;
}
/* represent the searched ratio as fractional number */
2014-04-20 13:24:32 +02:00
nom = target_clock ;
2014-03-28 18:55:10 +01:00
den = pll - > reference_freq ;
/* reduce the numbers to a simpler ratio */
avivo_reduce_ratio ( & nom , & den , fb_div_min , post_div_min ) ;
/* now search for a post divider */
if ( pll - > flags & RADEON_PLL_PREFER_MINM_OVER_MAXP )
post_div_best = post_div_min ;
else
post_div_best = post_div_max ;
diff_best = ~ 0 ;
for ( post_div = post_div_min ; post_div < = post_div_max ; + + post_div ) {
2014-04-20 13:24:32 +02:00
unsigned diff ;
avivo_get_fb_ref_div ( nom , den , post_div , fb_div_max ,
ref_div_max , & fb_div , & ref_div ) ;
diff = abs ( target_clock - ( pll - > reference_freq * fb_div ) /
( ref_div * post_div ) ) ;
2014-03-28 18:55:10 +01:00
if ( diff < diff_best | | ( diff = = diff_best & &
! ( pll - > flags & RADEON_PLL_PREFER_MINM_OVER_MAXP ) ) ) {
post_div_best = post_div ;
diff_best = diff ;
2011-01-31 16:48:52 -05:00
}
2014-03-28 18:55:10 +01:00
}
post_div = post_div_best ;
2014-04-20 13:24:32 +02:00
/* get the feedback and reference divider for the optimal value */
avivo_get_fb_ref_div ( nom , den , post_div , fb_div_max , ref_div_max ,
& fb_div , & ref_div ) ;
2014-03-28 18:55:10 +01:00
/* reduce the numbers to a simpler ratio once more */
/* this also makes sure that the reference divider is large enough */
avivo_reduce_ratio ( & fb_div , & ref_div , fb_div_min , ref_div_min ) ;
2014-04-24 18:39:59 +02:00
/* avoid high jitter with small fractional dividers */
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV & & ( fb_div % 10 ) ) {
2014-05-13 12:50:54 +02:00
fb_div_min = max ( fb_div_min , ( 9 - ( fb_div % 10 ) ) * 20 + 50 ) ;
2014-04-24 18:39:59 +02:00
if ( fb_div < fb_div_min ) {
unsigned tmp = DIV_ROUND_UP ( fb_div_min , fb_div ) ;
fb_div * = tmp ;
ref_div * = tmp ;
}
}
2014-03-28 18:55:10 +01:00
/* and finally save the result */
if ( pll - > flags & RADEON_PLL_USE_FRAC_FB_DIV ) {
* fb_div_p = fb_div / 10 ;
* frac_fb_div_p = fb_div % 10 ;
2011-01-31 16:48:52 -05:00
} else {
2014-03-28 18:55:10 +01:00
* fb_div_p = fb_div ;
* frac_fb_div_p = 0 ;
2011-01-31 16:48:52 -05:00
}
2014-03-28 18:55:10 +01:00
* dot_clock_p = ( ( pll - > reference_freq * * fb_div_p * 10 ) +
( pll - > reference_freq * * frac_fb_div_p ) ) /
( ref_div * post_div * 10 ) ;
2011-01-31 16:48:52 -05:00
* ref_div_p = ref_div ;
* post_div_p = post_div ;
2014-03-28 18:55:10 +01:00
DRM_DEBUG_KMS ( " %d - %d, pll dividers - fb: %d.%d ref: %d, post %d \n " ,
2014-04-20 13:24:32 +02:00
freq , * dot_clock_p * 10 , * fb_div_p , * frac_fb_div_p ,
2014-03-28 18:55:10 +01:00
ref_div , post_div ) ;
2011-01-31 16:48:52 -05:00
}
/* pre-avivo */
2009-06-05 14:42:42 +02:00
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 ;
}
2011-01-31 16:48:52 -05:00
void radeon_compute_pll_legacy ( 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 ;
}
2011-01-31 16:48:53 -05:00
if ( pll_out_min > 64800 )
pll_out_min = 64800 ;
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 ;
}
2011-02-21 01:11:59 -05: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 ;
2011-04-17 20:35:52 -07:00
DRM_DEBUG_KMS ( " %lld %d, pll dividers - fb: %d.%d ref: %d, post %d \n " ,
( long long ) freq ,
best_freq / 1000 , best_feedback_div , best_frac_feedback_div ,
2011-01-31 16:48:51 -05:00
best_ref_div , best_post_div ) ;
2009-06-05 14:42:42 +02:00
}
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 ,
} ;
2012-03-06 10:44:40 +00:00
int
2010-03-30 05:34:13 +00:00
radeon_framebuffer_init ( struct drm_device * dev ,
struct radeon_framebuffer * rfb ,
2011-11-14 14:51:28 -08:00
struct drm_mode_fb_cmd2 * mode_cmd ,
2010-03-30 05:34:13 +00:00
struct drm_gem_object * obj )
2009-06-05 14:42:42 +02:00
{
2012-03-06 10:44:40 +00:00
int ret ;
2010-03-30 05:34:13 +00:00
rfb - > obj = obj ;
2012-12-13 23:38:38 +01:00
drm_helper_mode_fill_fb_struct ( & rfb - > base , mode_cmd ) ;
2012-03-06 10:44:40 +00:00
ret = drm_framebuffer_init ( dev , & rfb - > base , & radeon_fb_funcs ) ;
if ( ret ) {
rfb - > obj = NULL ;
return ret ;
}
return 0 ;
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 ,
2011-11-14 14:51:28 -08:00
struct drm_mode_fb_cmd2 * mode_cmd )
2009-06-05 14:42:42 +02:00
{
struct drm_gem_object * obj ;
2010-03-30 05:34:13 +00:00
struct radeon_framebuffer * radeon_fb ;
2012-03-06 10:44:40 +00:00
int ret ;
2009-06-05 14:42:42 +02:00
2011-11-14 14:51:28 -08:00
obj = drm_gem_object_lookup ( dev , file_priv , mode_cmd - > handles [ 0 ] ) ;
2010-01-17 21:21:41 +01:00
if ( obj = = NULL ) {
dev_err ( & dev - > pdev - > dev , " No GEM object associated to handle 0x%08X, "
2011-11-14 14:51:28 -08:00
" can't create framebuffer \n " , mode_cmd - > handles [ 0 ] ) ;
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 ) ;
2013-01-31 22:13:00 +08:00
if ( radeon_fb = = NULL ) {
drm_gem_object_unreference_unlocked ( obj ) ;
2010-08-08 13:36:38 +01:00
return ERR_PTR ( - ENOMEM ) ;
2013-01-31 22:13:00 +08:00
}
2010-03-30 05:34:13 +00:00
2012-03-06 10:44:40 +00:00
ret = radeon_framebuffer_init ( dev , radeon_fb , mode_cmd , obj ) ;
if ( ret ) {
kfree ( radeon_fb ) ;
drm_gem_object_unreference_unlocked ( obj ) ;
2013-01-22 22:16:53 +08:00
return ERR_PTR ( ret ) ;
2012-03-06 10:44:40 +00:00
}
2010-03-30 05:34:13 +00:00
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
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 " } ,
} ;
2013-09-03 14:58:44 -04:00
static struct drm_prop_enum_list radeon_audio_enum_list [ ] =
{ { RADEON_AUDIO_DISABLE , " off " } ,
{ RADEON_AUDIO_ENABLE , " on " } ,
{ RADEON_AUDIO_AUTO , " auto " } ,
} ;
2013-09-24 17:26:26 -04:00
/* XXX support different dither options? spatial, temporal, both, etc. */
static struct drm_prop_enum_list radeon_dither_enum_list [ ] =
{ { RADEON_FMT_DITHER_DISABLE , " off " } ,
{ RADEON_FMT_DITHER_ENABLE , " on " } ,
} ;
2015-02-23 10:11:49 -05:00
static struct drm_prop_enum_list radeon_output_csc_enum_list [ ] =
{ { RADEON_OUTPUT_CSC_BYPASS , " bypass " } ,
{ RADEON_OUTPUT_CSC_TVRGB , " tvrgb " } ,
{ RADEON_OUTPUT_CSC_YCBCR601 , " ycbcr601 " } ,
{ RADEON_OUTPUT_CSC_YCBCR709 , " ycbcr709 " } ,
} ;
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
{
2012-02-06 10:58:17 +01:00
int sz ;
2009-09-09 17:40:54 +10:00
if ( rdev - > is_atom_bios ) {
rdev - > mode_info . coherent_mode_property =
2012-02-06 10:58:18 +01:00
drm_property_create_range ( rdev - > ddev , 0 , " coherent " , 0 , 1 ) ;
2009-09-09 17:40:54 +10:00
if ( ! rdev - > mode_info . coherent_mode_property )
return - ENOMEM ;
}
if ( ! ASIC_IS_AVIVO ( rdev ) ) {
sz = ARRAY_SIZE ( radeon_tmds_pll_enum_list ) ;
rdev - > mode_info . tmds_pll_property =
2012-02-06 10:58:17 +01:00
drm_property_create_enum ( rdev - > ddev , 0 ,
" tmds_pll " ,
radeon_tmds_pll_enum_list , sz ) ;
2009-09-09 17:40:54 +10:00
}
rdev - > mode_info . load_detect_property =
2012-02-06 10:58:18 +01:00
drm_property_create_range ( rdev - > ddev , 0 , " load detection " , 0 , 1 ) ;
2009-09-09 17:40:54 +10:00
if ( ! rdev - > mode_info . load_detect_property )
return - ENOMEM ;
drm_mode_create_scaling_mode_property ( rdev - > ddev ) ;
sz = ARRAY_SIZE ( radeon_tv_std_enum_list ) ;
rdev - > mode_info . tv_std_property =
2012-02-06 10:58:17 +01:00
drm_property_create_enum ( rdev - > ddev , 0 ,
" tv standard " ,
radeon_tv_std_enum_list , sz ) ;
2009-09-09 17:40:54 +10:00
2010-08-03 19:59:20 -04:00
sz = ARRAY_SIZE ( radeon_underscan_enum_list ) ;
rdev - > mode_info . underscan_property =
2012-02-06 10:58:17 +01:00
drm_property_create_enum ( rdev - > ddev , 0 ,
" underscan " ,
radeon_underscan_enum_list , sz ) ;
2010-08-03 19:59:20 -04:00
2010-09-21 21:30:59 +02:00
rdev - > mode_info . underscan_hborder_property =
2012-02-06 10:58:18 +01:00
drm_property_create_range ( rdev - > ddev , 0 ,
" underscan hborder " , 0 , 128 ) ;
2010-09-21 21:30:59 +02:00
if ( ! rdev - > mode_info . underscan_hborder_property )
return - ENOMEM ;
rdev - > mode_info . underscan_vborder_property =
2012-02-06 10:58:18 +01:00
drm_property_create_range ( rdev - > ddev , 0 ,
" underscan vborder " , 0 , 128 ) ;
2010-09-21 21:30:59 +02:00
if ( ! rdev - > mode_info . underscan_vborder_property )
return - ENOMEM ;
2013-09-03 14:58:44 -04:00
sz = ARRAY_SIZE ( radeon_audio_enum_list ) ;
rdev - > mode_info . audio_property =
drm_property_create_enum ( rdev - > ddev , 0 ,
" audio " ,
radeon_audio_enum_list , sz ) ;
2013-09-24 17:26:26 -04:00
sz = ARRAY_SIZE ( radeon_dither_enum_list ) ;
rdev - > mode_info . dither_property =
drm_property_create_enum ( rdev - > ddev , 0 ,
" dither " ,
radeon_dither_enum_list , sz ) ;
2015-02-23 10:11:49 -05:00
sz = ARRAY_SIZE ( radeon_output_csc_enum_list ) ;
rdev - > mode_info . output_csc_property =
drm_property_create_enum ( rdev - > ddev , 0 ,
" output_csc " ,
radeon_output_csc_enum_list , sz ) ;
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 ;
}
2012-05-14 16:52:29 +02:00
/*
* Allocate hdmi structs and determine register offsets
*/
static void radeon_afmt_init ( struct radeon_device * rdev )
{
int i ;
for ( i = 0 ; i < RADEON_MAX_AFMT_BLOCKS ; i + + )
rdev - > mode_info . afmt [ i ] = NULL ;
2013-07-31 16:51:33 -04:00
if ( ASIC_IS_NODCE ( rdev ) ) {
/* nothing to do */
2012-05-14 16:52:29 +02:00
} else if ( ASIC_IS_DCE4 ( rdev ) ) {
2013-08-01 17:29:16 +02:00
static uint32_t eg_offsets [ ] = {
EVERGREEN_CRTC0_REGISTER_OFFSET ,
EVERGREEN_CRTC1_REGISTER_OFFSET ,
EVERGREEN_CRTC2_REGISTER_OFFSET ,
EVERGREEN_CRTC3_REGISTER_OFFSET ,
EVERGREEN_CRTC4_REGISTER_OFFSET ,
EVERGREEN_CRTC5_REGISTER_OFFSET ,
2013-07-31 16:51:33 -04:00
0x13830 - 0x7030 ,
2013-08-01 17:29:16 +02:00
} ;
int num_afmt ;
2013-07-31 16:51:33 -04:00
/* DCE8 has 7 audio blocks tied to DIG encoders */
/* DCE6 has 6 audio blocks tied to DIG encoders */
2012-05-14 16:52:29 +02:00
/* DCE4/5 has 6 audio blocks tied to DIG encoders */
/* DCE4.1 has 2 audio blocks tied to DIG encoders */
2013-07-31 16:51:33 -04:00
if ( ASIC_IS_DCE8 ( rdev ) )
num_afmt = 7 ;
else if ( ASIC_IS_DCE6 ( rdev ) )
num_afmt = 6 ;
else if ( ASIC_IS_DCE5 ( rdev ) )
2013-08-01 17:29:16 +02:00
num_afmt = 6 ;
else if ( ASIC_IS_DCE41 ( rdev ) )
num_afmt = 2 ;
else /* DCE4 */
num_afmt = 6 ;
BUG_ON ( num_afmt > ARRAY_SIZE ( eg_offsets ) ) ;
for ( i = 0 ; i < num_afmt ; i + + ) {
rdev - > mode_info . afmt [ i ] = kzalloc ( sizeof ( struct radeon_afmt ) , GFP_KERNEL ) ;
if ( rdev - > mode_info . afmt [ i ] ) {
rdev - > mode_info . afmt [ i ] - > offset = eg_offsets [ i ] ;
rdev - > mode_info . afmt [ i ] - > id = i ;
2012-05-14 16:52:29 +02:00
}
}
} else if ( ASIC_IS_DCE3 ( rdev ) ) {
/* DCE3.x has 2 audio blocks tied to DIG encoders */
rdev - > mode_info . afmt [ 0 ] = kzalloc ( sizeof ( struct radeon_afmt ) , GFP_KERNEL ) ;
if ( rdev - > mode_info . afmt [ 0 ] ) {
rdev - > mode_info . afmt [ 0 ] - > offset = DCE3_HDMI_OFFSET0 ;
rdev - > mode_info . afmt [ 0 ] - > id = 0 ;
}
rdev - > mode_info . afmt [ 1 ] = kzalloc ( sizeof ( struct radeon_afmt ) , GFP_KERNEL ) ;
if ( rdev - > mode_info . afmt [ 1 ] ) {
rdev - > mode_info . afmt [ 1 ] - > offset = DCE3_HDMI_OFFSET1 ;
rdev - > mode_info . afmt [ 1 ] - > id = 1 ;
}
} else if ( ASIC_IS_DCE2 ( rdev ) ) {
/* DCE2 has at least 1 routable audio block */
rdev - > mode_info . afmt [ 0 ] = kzalloc ( sizeof ( struct radeon_afmt ) , GFP_KERNEL ) ;
if ( rdev - > mode_info . afmt [ 0 ] ) {
rdev - > mode_info . afmt [ 0 ] - > offset = DCE2_HDMI_OFFSET0 ;
rdev - > mode_info . afmt [ 0 ] - > id = 0 ;
}
/* r6xx has 2 routable audio blocks */
if ( rdev - > family > = CHIP_R600 ) {
rdev - > mode_info . afmt [ 1 ] = kzalloc ( sizeof ( struct radeon_afmt ) , GFP_KERNEL ) ;
if ( rdev - > mode_info . afmt [ 1 ] ) {
rdev - > mode_info . afmt [ 1 ] - > offset = DCE2_HDMI_OFFSET1 ;
rdev - > mode_info . afmt [ 1 ] - > id = 1 ;
}
}
}
}
static void radeon_afmt_fini ( struct radeon_device * rdev )
{
int i ;
for ( i = 0 ; i < RADEON_MAX_AFMT_BLOCKS ; i + + ) {
kfree ( rdev - > mode_info . afmt [ i ] ) ;
rdev - > mode_info . afmt [ i ] = NULL ;
}
}
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 ;
2012-05-17 13:27:23 +02:00
rdev - > ddev - > mode_config . funcs = & radeon_mode_funcs ;
2009-06-05 14:42:42 +02:00
2011-01-06 21:19:14 -05:00
if ( ASIC_IS_DCE5 ( rdev ) ) {
rdev - > ddev - > mode_config . max_width = 16384 ;
rdev - > ddev - > mode_config . max_height = 16384 ;
} else if ( ASIC_IS_AVIVO ( rdev ) ) {
2009-06-05 14:42:42 +02:00
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 ;
}
2011-09-29 16:20:42 +01:00
rdev - > ddev - > mode_config . preferred_depth = 24 ;
rdev - > ddev - > mode_config . prefer_shadow = 1 ;
2009-06-05 14:42:42 +02:00
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 ;
}
2011-05-22 13:20:36 -04:00
2012-01-20 14:56:39 -05:00
/* init dig PHYs, disp eng pll */
if ( rdev - > is_atom_bios ) {
2011-05-22 13:20:36 -04:00
radeon_atom_encoder_init ( rdev ) ;
2012-03-20 17:18:04 -04:00
radeon_atom_disp_eng_pll_init ( rdev ) ;
2012-01-20 14:56:39 -05:00
}
2011-05-22 13:20:36 -04:00
2009-12-04 16:56:37 -05:00
/* initialize hpd */
radeon_hpd_init ( rdev ) ;
2010-03-30 05:34:13 +00:00
2012-05-14 16:52:29 +02:00
/* setup afmt */
radeon_afmt_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 ) ;
2013-12-18 14:07:14 -05:00
if ( rdev - > pm . dpm_enabled ) {
/* do dpm late init */
ret = radeon_pm_late_init ( rdev ) ;
if ( ret ) {
rdev - > pm . dpm_enabled = false ;
DRM_ERROR ( " radeon_pm_late_init failed, disabling dpm \n " ) ;
}
/* set the dpm state for PX since there won't be
* a modeset to call this .
*/
radeon_pm_compute_clocks ( rdev ) ;
}
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 ) ;
2009-06-05 14:42:42 +02:00
if ( rdev - > mode_info . mode_config_initialized ) {
2012-05-14 16:52:29 +02:00
radeon_afmt_fini ( rdev ) ;
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
}
2012-07-17 17:56:50 +02:00
static bool is_hdtv_mode ( const struct drm_display_mode * mode )
2010-08-20 11:57:19 -04:00
{
/* 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 ,
2012-07-17 17:56:50 +02:00
const struct drm_display_mode * mode ,
2009-07-13 21:04:08 +02:00
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 ) & &
2014-07-15 11:00:47 -04:00
drm_detect_hdmi_monitor ( radeon_connector_edid ( connector ) ) & &
2010-08-20 11:57:19 -04:00
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
/*
2013-10-30 05:13:07 +01:00
* Retrieve current video scanout position of crtc on a given gpu , and
* an optional accurate timestamp of when query happened .
2010-10-05 19:57:36 -04:00
*
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 .
2013-10-28 20:50:48 +02:00
* \ param flags Flags from caller ( DRM_CALLED_FROM_VBLIRQ or 0 ) .
2010-10-05 19:57:36 -04:00
* \ param * vpos Location where vertical scanout position should be stored .
* \ param * hpos Location where horizontal scanout position should go .
2013-10-30 05:13:07 +01:00
* \ param * stime Target location for timestamp taken immediately before
* scanout position query . Can be NULL to skip timestamp .
* \ param * etime Target location for timestamp taken immediately after
* scanout position query . Can be NULL to skip timestamp .
2010-10-05 19:57:36 -04:00
*
* 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 :
*
2011-03-30 22:57:33 -03:00
* DRM_SCANOUTPOS_VALID = Query successful .
2010-10-23 04:42:17 +02:00
* 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 .
*
*/
2013-10-28 20:50:48 +02:00
int radeon_get_crtc_scanoutpos ( struct drm_device * dev , int crtc , unsigned int flags ,
int * vpos , int * hpos , ktime_t * stime , ktime_t * etime )
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 ;
2013-10-30 05:13:07 +01:00
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
/* Get optional system timestamp before query. */
if ( stime )
* stime = ktime_get ( ) ;
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
}
}
2013-10-30 05:13:07 +01:00
/* Get optional system timestamp after query. */
if ( etime )
* etime = ktime_get ( ) ;
/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
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 )
2014-09-10 17:36:11 +02:00
ret | = DRM_SCANOUTPOS_IN_VBLANK ;
2010-10-05 19:57:36 -04:00
2013-10-28 21:22:52 +02:00
/* Is vpos outside nominal vblank area, but less than
* 1 / 100 of a frame height away from start of vblank ?
* If so , assume this isn ' t a massively delayed vblank
* interrupt , but a vblank interrupt that fired a few
* microseconds before true start of vblank . Compensate
* by adding a full frame duration to the final timestamp .
* Happens , e . g . , on ATI R500 , R600 .
*
* We only do this if DRM_CALLED_FROM_VBLIRQ .
*/
if ( ( flags & DRM_CALLED_FROM_VBLIRQ ) & & ! in_vbl ) {
vbl_start = rdev - > mode_info . crtcs [ crtc ] - > base . hwmode . crtc_vdisplay ;
vtotal = rdev - > mode_info . crtcs [ crtc ] - > base . hwmode . crtc_vtotal ;
if ( vbl_start - * vpos < vtotal / 100 ) {
* vpos - = vtotal ;
/* Signal this correction as "applied". */
ret | = 0x8 ;
}
}
2010-10-05 19:57:36 -04:00
return ret ;
}