2016-01-19 14:03:24 +08:00
/*
* Copyright 2015 Advanced Micro Devices , Inc .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE 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 .
*
*/
2019-06-10 00:07:57 +02:00
# include <linux/pci.h>
# include <drm/drm_fourcc.h>
# include <drm/drm_vblank.h>
2016-01-19 14:03:24 +08:00
# include "amdgpu.h"
# include "amdgpu_pm.h"
# include "amdgpu_i2c.h"
# include "atom.h"
# include "amdgpu_atombios.h"
# include "atombios_crtc.h"
# include "atombios_encoders.h"
# include "amdgpu_pll.h"
# include "amdgpu_connectors.h"
2018-08-09 09:50:12 -05:00
# include "amdgpu_display.h"
2016-11-14 13:55:17 -05:00
# include "bif/bif_3_0_d.h"
# include "bif/bif_3_0_sh_mask.h"
# include "oss/oss_1_0_d.h"
# include "oss/oss_1_0_sh_mask.h"
# include "gca/gfx_6_0_d.h"
# include "gca/gfx_6_0_sh_mask.h"
# include "gmc/gmc_6_0_d.h"
# include "gmc/gmc_6_0_sh_mask.h"
# include "dce/dce_6_0_d.h"
# include "dce/dce_6_0_sh_mask.h"
# include "gca/gfx_7_2_enum.h"
2017-07-30 13:26:14 +02:00
# include "dce_v6_0.h"
2016-11-14 13:55:17 -05:00
# include "si_enums.h"
2016-01-19 14:03:24 +08:00
static void dce_v6_0_set_display_funcs ( struct amdgpu_device * adev ) ;
static void dce_v6_0_set_irq_funcs ( struct amdgpu_device * adev ) ;
static const u32 crtc_offsets [ 6 ] =
{
SI_CRTC0_REGISTER_OFFSET ,
SI_CRTC1_REGISTER_OFFSET ,
SI_CRTC2_REGISTER_OFFSET ,
SI_CRTC3_REGISTER_OFFSET ,
SI_CRTC4_REGISTER_OFFSET ,
SI_CRTC5_REGISTER_OFFSET
} ;
2016-09-28 14:15:24 -04:00
static const u32 hpd_offsets [ ] =
{
2016-11-14 13:55:17 -05:00
mmDC_HPD1_INT_STATUS - mmDC_HPD1_INT_STATUS ,
mmDC_HPD2_INT_STATUS - mmDC_HPD1_INT_STATUS ,
mmDC_HPD3_INT_STATUS - mmDC_HPD1_INT_STATUS ,
mmDC_HPD4_INT_STATUS - mmDC_HPD1_INT_STATUS ,
mmDC_HPD5_INT_STATUS - mmDC_HPD1_INT_STATUS ,
mmDC_HPD6_INT_STATUS - mmDC_HPD1_INT_STATUS ,
2016-09-28 14:15:24 -04:00
} ;
2016-01-19 14:03:24 +08:00
static const uint32_t dig_offsets [ ] = {
SI_CRTC0_REGISTER_OFFSET ,
SI_CRTC1_REGISTER_OFFSET ,
SI_CRTC2_REGISTER_OFFSET ,
SI_CRTC3_REGISTER_OFFSET ,
SI_CRTC4_REGISTER_OFFSET ,
SI_CRTC5_REGISTER_OFFSET ,
( 0x13830 - 0x7030 ) > > 2 ,
} ;
static const struct {
uint32_t reg ;
uint32_t vblank ;
uint32_t vline ;
uint32_t hpd ;
} interrupt_status_offsets [ 6 ] = { {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS__LB_D1_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS__LB_D1_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS__DC_HPD1_INTERRUPT_MASK
} , {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS_CONTINUE ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS_CONTINUE__DC_HPD2_INTERRUPT_MASK
} , {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS_CONTINUE2 ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS_CONTINUE2__DC_HPD3_INTERRUPT_MASK
} , {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS_CONTINUE3 ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS_CONTINUE3__DC_HPD4_INTERRUPT_MASK
} , {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS_CONTINUE4 ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS_CONTINUE4__DC_HPD5_INTERRUPT_MASK
} , {
2016-11-14 13:55:17 -05:00
. reg = mmDISP_INTERRUPT_STATUS_CONTINUE5 ,
2016-01-19 14:03:24 +08:00
. vblank = DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VBLANK_INTERRUPT_MASK ,
. vline = DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VLINE_INTERRUPT_MASK ,
. hpd = DISP_INTERRUPT_STATUS_CONTINUE5__DC_HPD6_INTERRUPT_MASK
} } ;
static u32 dce_v6_0_audio_endpt_rreg ( struct amdgpu_device * adev ,
u32 block_offset , u32 reg )
{
2017-02-19 19:56:17 +08:00
unsigned long flags ;
u32 r ;
spin_lock_irqsave ( & adev - > audio_endpt_idx_lock , flags ) ;
WREG32 ( mmAZALIA_F0_CODEC_ENDPOINT_INDEX + block_offset , reg ) ;
r = RREG32 ( mmAZALIA_F0_CODEC_ENDPOINT_DATA + block_offset ) ;
spin_unlock_irqrestore ( & adev - > audio_endpt_idx_lock , flags ) ;
return r ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_audio_endpt_wreg ( struct amdgpu_device * adev ,
u32 block_offset , u32 reg , u32 v )
{
2017-02-19 19:56:17 +08:00
unsigned long flags ;
spin_lock_irqsave ( & adev - > audio_endpt_idx_lock , flags ) ;
WREG32 ( mmAZALIA_F0_CODEC_ENDPOINT_INDEX + block_offset ,
reg | AZALIA_F0_CODEC_ENDPOINT_INDEX__AZALIA_ENDPOINT_REG_WRITE_EN_MASK ) ;
WREG32 ( mmAZALIA_F0_CODEC_ENDPOINT_DATA + block_offset , v ) ;
spin_unlock_irqrestore ( & adev - > audio_endpt_idx_lock , flags ) ;
2016-01-19 14:03:24 +08:00
}
static u32 dce_v6_0_vblank_get_counter ( struct amdgpu_device * adev , int crtc )
{
if ( crtc > = adev - > mode_info . num_crtc )
return 0 ;
else
2016-11-14 13:55:17 -05:00
return RREG32 ( mmCRTC_STATUS_FRAME_COUNT + crtc_offsets [ crtc ] ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_pageflip_interrupt_init ( struct amdgpu_device * adev )
{
unsigned i ;
/* Enable pflip interrupts */
2016-09-19 09:30:21 -04:00
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + )
2016-01-19 14:03:24 +08:00
amdgpu_irq_get ( adev , & adev - > pageflip_irq , i ) ;
}
static void dce_v6_0_pageflip_interrupt_fini ( struct amdgpu_device * adev )
{
unsigned i ;
/* Disable pflip interrupts */
2016-09-19 09:30:21 -04:00
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + )
2016-01-19 14:03:24 +08:00
amdgpu_irq_put ( adev , & adev - > pageflip_irq , i ) ;
}
/**
* dce_v6_0_page_flip - pageflip callback .
*
* @ adev : amdgpu_device pointer
* @ crtc_id : crtc to cleanup pageflip on
* @ crtc_base : new address of the crtc ( GPU MC address )
*
* Does the actual pageflip ( evergreen + ) .
* During vblank we take the crtc lock and wait for the update_pending
* bit to go high , when it does , we release the lock , and allow the
* double buffered update to take place .
* Returns the current update pending status .
*/
static void dce_v6_0_page_flip ( struct amdgpu_device * adev ,
int crtc_id , u64 crtc_base , bool async )
{
struct amdgpu_crtc * amdgpu_crtc = adev - > mode_info . crtcs [ crtc_id ] ;
2019-07-24 17:56:28 +02:00
struct drm_framebuffer * fb = amdgpu_crtc - > base . primary - > fb ;
2016-01-19 14:03:24 +08:00
/* flip at hsync for async, default is vsync */
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_FLIP_CONTROL + amdgpu_crtc - > crtc_offset , async ?
GRPH_FLIP_CONTROL__GRPH_SURFACE_UPDATE_H_RETRACE_EN_MASK : 0 ) ;
2019-07-24 17:56:28 +02:00
/* update pitch */
WREG32 ( mmGRPH_PITCH + amdgpu_crtc - > crtc_offset ,
fb - > pitches [ 0 ] / fb - > format - > cpp [ 0 ] ) ;
2016-01-19 14:03:24 +08:00
/* update the scanout addresses */
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
upper_32_bits ( crtc_base ) ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
( u32 ) crtc_base ) ;
/* post the write */
2016-11-14 13:55:17 -05:00
RREG32 ( mmGRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc - > crtc_offset ) ;
2016-01-19 14:03:24 +08:00
}
static int dce_v6_0_crtc_get_scanoutpos ( struct amdgpu_device * adev , int crtc ,
u32 * vbl , u32 * position )
{
if ( ( crtc < 0 ) | | ( crtc > = adev - > mode_info . num_crtc ) )
return - EINVAL ;
2016-11-14 13:55:17 -05:00
* vbl = RREG32 ( mmCRTC_V_BLANK_START_END + crtc_offsets [ crtc ] ) ;
* position = RREG32 ( mmCRTC_STATUS_POSITION + crtc_offsets [ crtc ] ) ;
2016-01-19 14:03:24 +08:00
return 0 ;
}
/**
* dce_v6_0_hpd_sense - hpd sense callback .
*
* @ adev : amdgpu_device pointer
* @ hpd : hpd ( hotplug detect ) pin
*
* Checks if a digital monitor is connected ( evergreen + ) .
* Returns true if connected , false if not connected .
*/
static bool dce_v6_0_hpd_sense ( struct amdgpu_device * adev ,
enum amdgpu_hpd_id hpd )
{
bool connected = false ;
2016-09-28 14:15:24 -04:00
if ( hpd > = adev - > mode_info . num_hpd )
return connected ;
2016-11-14 13:55:17 -05:00
if ( RREG32 ( mmDC_HPD1_INT_STATUS + hpd_offsets [ hpd ] ) & DC_HPD1_INT_STATUS__DC_HPD1_SENSE_MASK )
2016-09-28 14:15:24 -04:00
connected = true ;
2016-01-19 14:03:24 +08:00
return connected ;
}
/**
* dce_v6_0_hpd_set_polarity - hpd set polarity callback .
*
* @ adev : amdgpu_device pointer
* @ hpd : hpd ( hotplug detect ) pin
*
* Set the polarity of the hpd pin ( evergreen + ) .
*/
static void dce_v6_0_hpd_set_polarity ( struct amdgpu_device * adev ,
enum amdgpu_hpd_id hpd )
{
u32 tmp ;
bool connected = dce_v6_0_hpd_sense ( adev , hpd ) ;
2016-09-28 14:15:24 -04:00
if ( hpd > = adev - > mode_info . num_hpd )
return ;
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ hpd ] ) ;
2016-09-28 14:15:24 -04:00
if ( connected )
2016-11-14 13:55:17 -05:00
tmp & = ~ DC_HPD1_INT_CONTROL__DC_HPD1_INT_POLARITY_MASK ;
2016-09-28 14:15:24 -04:00
else
2016-11-14 13:55:17 -05:00
tmp | = DC_HPD1_INT_CONTROL__DC_HPD1_INT_POLARITY_MASK ;
WREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ hpd ] , tmp ) ;
2016-01-19 14:03:24 +08:00
}
/**
* dce_v6_0_hpd_init - hpd setup callback .
*
* @ adev : amdgpu_device pointer
*
* Setup the hpd pins used by the card ( evergreen + ) .
* Enable the pin , set the polarity , and enable the hpd interrupts .
*/
static void dce_v6_0_hpd_init ( struct amdgpu_device * adev )
{
struct drm_device * dev = adev - > ddev ;
struct drm_connector * connector ;
2016-09-28 14:21:55 -04:00
u32 tmp ;
2016-01-19 14:03:24 +08:00
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
struct amdgpu_connector * amdgpu_connector = to_amdgpu_connector ( connector ) ;
2016-09-28 14:15:24 -04:00
if ( amdgpu_connector - > hpd . hpd > = adev - > mode_info . num_hpd )
continue ;
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDC_HPD1_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] ) ;
tmp | = DC_HPD1_CONTROL__DC_HPD1_EN_MASK ;
WREG32 ( mmDC_HPD1_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] , tmp ) ;
2016-09-28 12:44:59 -04:00
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_eDP | |
connector - > connector_type = = DRM_MODE_CONNECTOR_LVDS ) {
/* don't try to enable hpd on eDP or LVDS avoid breaking the
* aux dp channel on imac and help ( but not completely fix )
* https : //bugzilla.redhat.com/show_bug.cgi?id=726143
* also avoid interrupt storms during dpms .
*/
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] ) ;
tmp & = ~ DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK ;
WREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] , tmp ) ;
2016-09-28 12:44:59 -04:00
continue ;
}
2016-01-19 14:03:24 +08:00
dce_v6_0_hpd_set_polarity ( adev , amdgpu_connector - > hpd . hpd ) ;
amdgpu_irq_get ( adev , & adev - > hpd_irq , amdgpu_connector - > hpd . hpd ) ;
}
}
/**
* dce_v6_0_hpd_fini - hpd tear down callback .
*
* @ adev : amdgpu_device pointer
*
* Tear down the hpd pins used by the card ( evergreen + ) .
* Disable the hpd interrupts .
*/
static void dce_v6_0_hpd_fini ( struct amdgpu_device * adev )
{
struct drm_device * dev = adev - > ddev ;
struct drm_connector * connector ;
2016-09-28 14:21:55 -04:00
u32 tmp ;
2016-01-19 14:03:24 +08:00
list_for_each_entry ( connector , & dev - > mode_config . connector_list , head ) {
struct amdgpu_connector * amdgpu_connector = to_amdgpu_connector ( connector ) ;
2016-09-28 14:15:24 -04:00
if ( amdgpu_connector - > hpd . hpd > = adev - > mode_info . num_hpd )
continue ;
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDC_HPD1_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] ) ;
tmp & = ~ DC_HPD1_CONTROL__DC_HPD1_EN_MASK ;
WREG32 ( mmDC_HPD1_CONTROL + hpd_offsets [ amdgpu_connector - > hpd . hpd ] , 0 ) ;
2016-09-28 14:15:24 -04:00
2016-01-19 14:03:24 +08:00
amdgpu_irq_put ( adev , & adev - > hpd_irq , amdgpu_connector - > hpd . hpd ) ;
}
}
static u32 dce_v6_0_hpd_get_gpio_reg ( struct amdgpu_device * adev )
{
2016-11-14 13:55:17 -05:00
return mmDC_GPIO_HPD_A ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_set_vga_render_state ( struct amdgpu_device * adev ,
bool render )
{
2016-09-29 23:30:21 -04:00
if ( ! render )
2016-11-14 13:55:17 -05:00
WREG32 ( mmVGA_RENDER_CONTROL ,
RREG32 ( mmVGA_RENDER_CONTROL ) & VGA_VSTATUS_CNTL ) ;
2016-01-19 14:03:24 +08:00
}
2016-09-29 23:30:21 -04:00
static int dce_v6_0_get_num_crtc ( struct amdgpu_device * adev )
{
switch ( adev - > asic_type ) {
case CHIP_TAHITI :
case CHIP_PITCAIRN :
case CHIP_VERDE :
2017-05-15 10:46:17 -04:00
return 6 ;
2016-09-29 23:30:21 -04:00
case CHIP_OLAND :
2017-05-15 10:46:17 -04:00
return 2 ;
2016-09-29 23:30:21 -04:00
default :
2017-05-15 10:46:17 -04:00
return 0 ;
2016-09-29 23:30:21 -04:00
}
}
void dce_v6_0_disable_dce ( struct amdgpu_device * adev )
{
/*Disable VGA render and enabled crtc, if has DCE engine*/
if ( amdgpu_atombios_has_dce_engine_info ( adev ) ) {
u32 tmp ;
int crtc_enabled , i ;
dce_v6_0_set_vga_render_state ( adev , false ) ;
/*Disable crtc*/
for ( i = 0 ; i < dce_v6_0_get_num_crtc ( adev ) ; i + + ) {
2016-11-14 13:55:17 -05:00
crtc_enabled = RREG32 ( mmCRTC_CONTROL + crtc_offsets [ i ] ) &
CRTC_CONTROL__CRTC_MASTER_EN_MASK ;
2016-09-29 23:30:21 -04:00
if ( crtc_enabled ) {
2016-11-14 13:55:17 -05:00
WREG32 ( mmCRTC_UPDATE_LOCK + crtc_offsets [ i ] , 1 ) ;
tmp = RREG32 ( mmCRTC_CONTROL + crtc_offsets [ i ] ) ;
tmp & = ~ CRTC_CONTROL__CRTC_MASTER_EN_MASK ;
WREG32 ( mmCRTC_CONTROL + crtc_offsets [ i ] , tmp ) ;
WREG32 ( mmCRTC_UPDATE_LOCK + crtc_offsets [ i ] , 0 ) ;
2016-09-29 23:30:21 -04:00
}
}
}
}
2016-01-19 14:03:24 +08:00
static void dce_v6_0_program_fmt ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct drm_connector * connector = amdgpu_get_connector_for_encoder ( encoder ) ;
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( encoder - > crtc ) ;
int bpc = 0 ;
u32 tmp = 0 ;
enum amdgpu_connector_dither dither = AMDGPU_FMT_DITHER_DISABLE ;
if ( connector ) {
struct amdgpu_connector * amdgpu_connector = to_amdgpu_connector ( connector ) ;
bpc = amdgpu_connector_get_monitor_bpc ( connector ) ;
dither = amdgpu_connector - > dither ;
}
/* LVDS FMT is set up by atom */
if ( amdgpu_encoder - > devices & ATOM_DEVICE_LCD_SUPPORT )
return ;
if ( bpc = = 0 )
return ;
switch ( bpc ) {
case 6 :
if ( dither = = AMDGPU_FMT_DITHER_ENABLE )
/* XXX sort out optimal dither settings */
2016-11-14 13:55:17 -05:00
tmp | = ( FMT_BIT_DEPTH_CONTROL__FMT_FRAME_RANDOM_ENABLE_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_HIGHPASS_RANDOM_ENABLE_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_SPATIAL_DITHER_EN_MASK ) ;
2016-01-19 14:03:24 +08:00
else
2016-11-14 13:55:17 -05:00
tmp | = FMT_BIT_DEPTH_CONTROL__FMT_TRUNCATE_EN_MASK ;
2016-01-19 14:03:24 +08:00
break ;
case 8 :
if ( dither = = AMDGPU_FMT_DITHER_ENABLE )
/* XXX sort out optimal dither settings */
2016-11-14 13:55:17 -05:00
tmp | = ( FMT_BIT_DEPTH_CONTROL__FMT_FRAME_RANDOM_ENABLE_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_HIGHPASS_RANDOM_ENABLE_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_RGB_RANDOM_ENABLE_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_SPATIAL_DITHER_EN_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_SPATIAL_DITHER_DEPTH_MASK ) ;
2016-01-19 14:03:24 +08:00
else
2016-11-14 13:55:17 -05:00
tmp | = ( FMT_BIT_DEPTH_CONTROL__FMT_TRUNCATE_EN_MASK |
FMT_BIT_DEPTH_CONTROL__FMT_TRUNCATE_DEPTH_MASK ) ;
2016-01-19 14:03:24 +08:00
break ;
case 10 :
default :
/* not needed */
break ;
}
2016-11-14 13:55:17 -05:00
WREG32 ( mmFMT_BIT_DEPTH_CONTROL + amdgpu_crtc - > crtc_offset , tmp ) ;
2016-01-19 14:03:24 +08:00
}
/**
* cik_get_number_of_dram_channels - get the number of dram channels
*
* @ adev : amdgpu_device pointer
*
* Look up the number of video ram channels ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the number of dram channels
*/
static u32 si_get_number_of_dram_channels ( struct amdgpu_device * adev )
{
2016-11-14 13:55:17 -05:00
u32 tmp = RREG32 ( mmMC_SHARED_CHMAP ) ;
2016-01-19 14:03:24 +08:00
switch ( ( tmp & MC_SHARED_CHMAP__NOOFCHAN_MASK ) > > MC_SHARED_CHMAP__NOOFCHAN__SHIFT ) {
case 0 :
default :
return 1 ;
case 1 :
return 2 ;
case 2 :
return 4 ;
case 3 :
return 8 ;
case 4 :
return 3 ;
case 5 :
return 6 ;
case 6 :
return 10 ;
case 7 :
return 12 ;
case 8 :
return 16 ;
}
}
struct dce6_wm_params {
u32 dram_channels ; /* number of dram channels */
u32 yclk ; /* bandwidth per dram data pin in kHz */
u32 sclk ; /* engine clock in kHz */
u32 disp_clk ; /* display clock in kHz */
u32 src_width ; /* viewport width */
u32 active_time ; /* active display time in ns */
u32 blank_time ; /* blank time in ns */
bool interlaced ; /* mode is interlaced */
fixed20_12 vsc ; /* vertical scale ratio */
u32 num_heads ; /* number of active crtcs */
u32 bytes_per_pixel ; /* bytes per pixel display + overlay */
u32 lb_size ; /* line buffer allocated to pipe */
u32 vtaps ; /* vertical scaler taps */
} ;
/**
* dce_v6_0_dram_bandwidth - get the dram bandwidth
*
* @ wm : watermark calculation data
*
* Calculate the raw dram bandwidth ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the dram bandwidth in MBytes / s
*/
static u32 dce_v6_0_dram_bandwidth ( struct dce6_wm_params * wm )
{
/* Calculate raw DRAM Bandwidth */
fixed20_12 dram_efficiency ; /* 0.7 */
fixed20_12 yclk , dram_channels , bandwidth ;
fixed20_12 a ;
a . full = dfixed_const ( 1000 ) ;
yclk . full = dfixed_const ( wm - > yclk ) ;
yclk . full = dfixed_div ( yclk , a ) ;
dram_channels . full = dfixed_const ( wm - > dram_channels * 4 ) ;
a . full = dfixed_const ( 10 ) ;
dram_efficiency . full = dfixed_const ( 7 ) ;
dram_efficiency . full = dfixed_div ( dram_efficiency , a ) ;
bandwidth . full = dfixed_mul ( dram_channels , yclk ) ;
bandwidth . full = dfixed_mul ( bandwidth , dram_efficiency ) ;
return dfixed_trunc ( bandwidth ) ;
}
/**
* dce_v6_0_dram_bandwidth_for_display - get the dram bandwidth for display
*
* @ wm : watermark calculation data
*
* Calculate the dram bandwidth used for display ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the dram bandwidth for display in MBytes / s
*/
static u32 dce_v6_0_dram_bandwidth_for_display ( struct dce6_wm_params * wm )
{
/* Calculate DRAM Bandwidth and the part allocated to display. */
fixed20_12 disp_dram_allocation ; /* 0.3 to 0.7 */
fixed20_12 yclk , dram_channels , bandwidth ;
fixed20_12 a ;
a . full = dfixed_const ( 1000 ) ;
yclk . full = dfixed_const ( wm - > yclk ) ;
yclk . full = dfixed_div ( yclk , a ) ;
dram_channels . full = dfixed_const ( wm - > dram_channels * 4 ) ;
a . full = dfixed_const ( 10 ) ;
disp_dram_allocation . full = dfixed_const ( 3 ) ; /* XXX worse case value 0.3 */
disp_dram_allocation . full = dfixed_div ( disp_dram_allocation , a ) ;
bandwidth . full = dfixed_mul ( dram_channels , yclk ) ;
bandwidth . full = dfixed_mul ( bandwidth , disp_dram_allocation ) ;
return dfixed_trunc ( bandwidth ) ;
}
/**
* dce_v6_0_data_return_bandwidth - get the data return bandwidth
*
* @ wm : watermark calculation data
*
* Calculate the data return bandwidth used for display ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the data return bandwidth in MBytes / s
*/
static u32 dce_v6_0_data_return_bandwidth ( struct dce6_wm_params * wm )
{
/* Calculate the display Data return Bandwidth */
fixed20_12 return_efficiency ; /* 0.8 */
fixed20_12 sclk , bandwidth ;
fixed20_12 a ;
a . full = dfixed_const ( 1000 ) ;
sclk . full = dfixed_const ( wm - > sclk ) ;
sclk . full = dfixed_div ( sclk , a ) ;
a . full = dfixed_const ( 10 ) ;
return_efficiency . full = dfixed_const ( 8 ) ;
return_efficiency . full = dfixed_div ( return_efficiency , a ) ;
a . full = dfixed_const ( 32 ) ;
bandwidth . full = dfixed_mul ( a , sclk ) ;
bandwidth . full = dfixed_mul ( bandwidth , return_efficiency ) ;
return dfixed_trunc ( bandwidth ) ;
}
/**
* dce_v6_0_dmif_request_bandwidth - get the dmif bandwidth
*
* @ wm : watermark calculation data
*
* Calculate the dmif bandwidth used for display ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the dmif bandwidth in MBytes / s
*/
static u32 dce_v6_0_dmif_request_bandwidth ( struct dce6_wm_params * wm )
{
/* Calculate the DMIF Request Bandwidth */
fixed20_12 disp_clk_request_efficiency ; /* 0.8 */
fixed20_12 disp_clk , bandwidth ;
fixed20_12 a , b ;
a . full = dfixed_const ( 1000 ) ;
disp_clk . full = dfixed_const ( wm - > disp_clk ) ;
disp_clk . full = dfixed_div ( disp_clk , a ) ;
a . full = dfixed_const ( 32 ) ;
b . full = dfixed_mul ( a , disp_clk ) ;
a . full = dfixed_const ( 10 ) ;
disp_clk_request_efficiency . full = dfixed_const ( 8 ) ;
disp_clk_request_efficiency . full = dfixed_div ( disp_clk_request_efficiency , a ) ;
bandwidth . full = dfixed_mul ( b , disp_clk_request_efficiency ) ;
return dfixed_trunc ( bandwidth ) ;
}
/**
* dce_v6_0_available_bandwidth - get the min available bandwidth
*
* @ wm : watermark calculation data
*
* Calculate the min available bandwidth used for display ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the min available bandwidth in MBytes / s
*/
static u32 dce_v6_0_available_bandwidth ( struct dce6_wm_params * wm )
{
/* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
u32 dram_bandwidth = dce_v6_0_dram_bandwidth ( wm ) ;
u32 data_return_bandwidth = dce_v6_0_data_return_bandwidth ( wm ) ;
u32 dmif_req_bandwidth = dce_v6_0_dmif_request_bandwidth ( wm ) ;
return min ( dram_bandwidth , min ( data_return_bandwidth , dmif_req_bandwidth ) ) ;
}
/**
* dce_v6_0_average_bandwidth - get the average available bandwidth
*
* @ wm : watermark calculation data
*
* Calculate the average available bandwidth used for display ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the average available bandwidth in MBytes / s
*/
static u32 dce_v6_0_average_bandwidth ( struct dce6_wm_params * wm )
{
/* Calculate the display mode Average Bandwidth
* DisplayMode should contain the source and destination dimensions ,
* timing , etc .
*/
fixed20_12 bpp ;
fixed20_12 line_time ;
fixed20_12 src_width ;
fixed20_12 bandwidth ;
fixed20_12 a ;
a . full = dfixed_const ( 1000 ) ;
line_time . full = dfixed_const ( wm - > active_time + wm - > blank_time ) ;
line_time . full = dfixed_div ( line_time , a ) ;
bpp . full = dfixed_const ( wm - > bytes_per_pixel ) ;
src_width . full = dfixed_const ( wm - > src_width ) ;
bandwidth . full = dfixed_mul ( src_width , bpp ) ;
bandwidth . full = dfixed_mul ( bandwidth , wm - > vsc ) ;
bandwidth . full = dfixed_div ( bandwidth , line_time ) ;
return dfixed_trunc ( bandwidth ) ;
}
/**
* dce_v6_0_latency_watermark - get the latency watermark
*
* @ wm : watermark calculation data
*
* Calculate the latency watermark ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns the latency watermark in ns
*/
static u32 dce_v6_0_latency_watermark ( struct dce6_wm_params * wm )
{
/* First calculate the latency in ns */
u32 mc_latency = 2000 ; /* 2000 ns. */
u32 available_bandwidth = dce_v6_0_available_bandwidth ( wm ) ;
u32 worst_chunk_return_time = ( 512 * 8 * 1000 ) / available_bandwidth ;
u32 cursor_line_pair_return_time = ( 128 * 4 * 1000 ) / available_bandwidth ;
u32 dc_latency = 40000000 / wm - > disp_clk ; /* dc pipe latency */
u32 other_heads_data_return_time = ( ( wm - > num_heads + 1 ) * worst_chunk_return_time ) +
( wm - > num_heads * cursor_line_pair_return_time ) ;
u32 latency = mc_latency + other_heads_data_return_time + dc_latency ;
u32 max_src_lines_per_dst_line , lb_fill_bw , line_fill_time ;
u32 tmp , dmif_size = 12288 ;
fixed20_12 a , b , c ;
if ( wm - > num_heads = = 0 )
return 0 ;
a . full = dfixed_const ( 2 ) ;
b . full = dfixed_const ( 1 ) ;
if ( ( wm - > vsc . full > a . full ) | |
( ( wm - > vsc . full > b . full ) & & ( wm - > vtaps > = 3 ) ) | |
( wm - > vtaps > = 5 ) | |
( ( wm - > vsc . full > = a . full ) & & wm - > interlaced ) )
max_src_lines_per_dst_line = 4 ;
else
max_src_lines_per_dst_line = 2 ;
a . full = dfixed_const ( available_bandwidth ) ;
b . full = dfixed_const ( wm - > num_heads ) ;
a . full = dfixed_div ( a , b ) ;
2017-03-29 22:09:12 +02:00
tmp = div_u64 ( ( u64 ) dmif_size * ( u64 ) wm - > disp_clk , mc_latency + 512 ) ;
tmp = min ( dfixed_trunc ( a ) , tmp ) ;
2016-01-19 14:03:24 +08:00
2017-03-29 22:09:12 +02:00
lb_fill_bw = min ( tmp , wm - > disp_clk * wm - > bytes_per_pixel / 1000 ) ;
2016-01-19 14:03:24 +08:00
a . full = dfixed_const ( max_src_lines_per_dst_line * wm - > src_width * wm - > bytes_per_pixel ) ;
b . full = dfixed_const ( 1000 ) ;
c . full = dfixed_const ( lb_fill_bw ) ;
b . full = dfixed_div ( c , b ) ;
a . full = dfixed_div ( a , b ) ;
line_fill_time = dfixed_trunc ( a ) ;
if ( line_fill_time < wm - > active_time )
return latency ;
else
return latency + ( line_fill_time - wm - > active_time ) ;
}
/**
* dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display - check
* average and available dram bandwidth
*
* @ wm : watermark calculation data
*
* Check if the display average bandwidth fits in the display
* dram bandwidth ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns true if the display fits , false if not .
*/
static bool dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display ( struct dce6_wm_params * wm )
{
if ( dce_v6_0_average_bandwidth ( wm ) < =
( dce_v6_0_dram_bandwidth_for_display ( wm ) / wm - > num_heads ) )
return true ;
else
return false ;
}
/**
* dce_v6_0_average_bandwidth_vs_available_bandwidth - check
* average and available bandwidth
*
* @ wm : watermark calculation data
*
* Check if the display average bandwidth fits in the display
* available bandwidth ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns true if the display fits , false if not .
*/
static bool dce_v6_0_average_bandwidth_vs_available_bandwidth ( struct dce6_wm_params * wm )
{
if ( dce_v6_0_average_bandwidth ( wm ) < =
( dce_v6_0_available_bandwidth ( wm ) / wm - > num_heads ) )
return true ;
else
return false ;
}
/**
* dce_v6_0_check_latency_hiding - check latency hiding
*
* @ wm : watermark calculation data
*
* Check latency hiding ( CIK ) .
* Used for display watermark bandwidth calculations
* Returns true if the display fits , false if not .
*/
static bool dce_v6_0_check_latency_hiding ( struct dce6_wm_params * wm )
{
u32 lb_partitions = wm - > lb_size / wm - > src_width ;
u32 line_time = wm - > active_time + wm - > blank_time ;
u32 latency_tolerant_lines ;
u32 latency_hiding ;
fixed20_12 a ;
a . full = dfixed_const ( 1 ) ;
if ( wm - > vsc . full > a . full )
latency_tolerant_lines = 1 ;
else {
if ( lb_partitions < = ( wm - > vtaps + 1 ) )
latency_tolerant_lines = 1 ;
else
latency_tolerant_lines = 2 ;
}
latency_hiding = ( latency_tolerant_lines * line_time + wm - > blank_time ) ;
if ( dce_v6_0_latency_watermark ( wm ) < = latency_hiding )
return true ;
else
return false ;
}
/**
* dce_v6_0_program_watermarks - program display watermarks
*
* @ adev : amdgpu_device pointer
* @ amdgpu_crtc : the selected display controller
* @ lb_size : line buffer size
* @ num_heads : number of display controllers in use
*
* Calculate and program the display watermarks for the
* selected display controller ( CIK ) .
*/
static void dce_v6_0_program_watermarks ( struct amdgpu_device * adev ,
struct amdgpu_crtc * amdgpu_crtc ,
u32 lb_size , u32 num_heads )
{
struct drm_display_mode * mode = & amdgpu_crtc - > base . mode ;
struct dce6_wm_params wm_low , wm_high ;
u32 dram_channels ;
2017-03-29 22:09:11 +02:00
u32 active_time ;
2016-01-19 14:03:24 +08:00
u32 line_time = 0 ;
u32 latency_watermark_a = 0 , latency_watermark_b = 0 ;
u32 priority_a_mark = 0 , priority_b_mark = 0 ;
u32 priority_a_cnt = PRIORITY_OFF ;
u32 priority_b_cnt = PRIORITY_OFF ;
2017-04-24 01:02:46 +02:00
u32 tmp , arb_control3 , lb_vblank_lead_lines = 0 ;
2016-01-19 14:03:24 +08:00
fixed20_12 a , b , c ;
if ( amdgpu_crtc - > base . enabled & & num_heads & & mode ) {
2017-06-13 07:17:10 +02:00
active_time = ( u32 ) div_u64 ( ( u64 ) mode - > crtc_hdisplay * 1000000 ,
( u32 ) mode - > clock ) ;
line_time = ( u32 ) div_u64 ( ( u64 ) mode - > crtc_htotal * 1000000 ,
( u32 ) mode - > clock ) ;
line_time = min ( line_time , ( u32 ) 65535 ) ;
2016-01-19 14:03:24 +08:00
priority_a_cnt = 0 ;
priority_b_cnt = 0 ;
dram_channels = si_get_number_of_dram_channels ( adev ) ;
/* watermark for high clocks */
if ( adev - > pm . dpm_enabled ) {
wm_high . yclk =
amdgpu_dpm_get_mclk ( adev , false ) * 10 ;
wm_high . sclk =
amdgpu_dpm_get_sclk ( adev , false ) * 10 ;
} else {
wm_high . yclk = adev - > pm . current_mclk * 10 ;
wm_high . sclk = adev - > pm . current_sclk * 10 ;
}
wm_high . disp_clk = mode - > clock ;
wm_high . src_width = mode - > crtc_hdisplay ;
2017-03-29 22:09:11 +02:00
wm_high . active_time = active_time ;
2016-01-19 14:03:24 +08:00
wm_high . blank_time = line_time - wm_high . active_time ;
wm_high . interlaced = false ;
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
wm_high . interlaced = true ;
wm_high . vsc = amdgpu_crtc - > vsc ;
wm_high . vtaps = 1 ;
if ( amdgpu_crtc - > rmx_type ! = RMX_OFF )
wm_high . vtaps = 2 ;
wm_high . bytes_per_pixel = 4 ; /* XXX: get this from fb config */
wm_high . lb_size = lb_size ;
wm_high . dram_channels = dram_channels ;
wm_high . num_heads = num_heads ;
if ( adev - > pm . dpm_enabled ) {
/* watermark for low clocks */
wm_low . yclk =
amdgpu_dpm_get_mclk ( adev , true ) * 10 ;
wm_low . sclk =
amdgpu_dpm_get_sclk ( adev , true ) * 10 ;
} else {
wm_low . yclk = adev - > pm . current_mclk * 10 ;
wm_low . sclk = adev - > pm . current_sclk * 10 ;
}
wm_low . disp_clk = mode - > clock ;
wm_low . src_width = mode - > crtc_hdisplay ;
2017-03-29 22:09:11 +02:00
wm_low . active_time = active_time ;
2016-01-19 14:03:24 +08:00
wm_low . blank_time = line_time - wm_low . active_time ;
wm_low . interlaced = false ;
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
wm_low . interlaced = true ;
wm_low . vsc = amdgpu_crtc - > vsc ;
wm_low . vtaps = 1 ;
if ( amdgpu_crtc - > rmx_type ! = RMX_OFF )
wm_low . vtaps = 2 ;
wm_low . bytes_per_pixel = 4 ; /* XXX: get this from fb config */
wm_low . lb_size = lb_size ;
wm_low . dram_channels = dram_channels ;
wm_low . num_heads = num_heads ;
/* set for high clocks */
latency_watermark_a = min ( dce_v6_0_latency_watermark ( & wm_high ) , ( u32 ) 65535 ) ;
/* set for low clocks */
latency_watermark_b = min ( dce_v6_0_latency_watermark ( & wm_low ) , ( u32 ) 65535 ) ;
/* possibly force display priority to high */
/* should really do this at mode validation time... */
if ( ! dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display ( & wm_high ) | |
! dce_v6_0_average_bandwidth_vs_available_bandwidth ( & wm_high ) | |
! dce_v6_0_check_latency_hiding ( & wm_high ) | |
( adev - > mode_info . disp_priority = = 2 ) ) {
DRM_DEBUG_KMS ( " force priority to high \n " ) ;
priority_a_cnt | = PRIORITY_ALWAYS_ON ;
priority_b_cnt | = PRIORITY_ALWAYS_ON ;
}
if ( ! dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display ( & wm_low ) | |
! dce_v6_0_average_bandwidth_vs_available_bandwidth ( & wm_low ) | |
! dce_v6_0_check_latency_hiding ( & wm_low ) | |
( adev - > mode_info . disp_priority = = 2 ) ) {
DRM_DEBUG_KMS ( " force priority to high \n " ) ;
priority_a_cnt | = PRIORITY_ALWAYS_ON ;
priority_b_cnt | = PRIORITY_ALWAYS_ON ;
}
a . full = dfixed_const ( 1000 ) ;
b . full = dfixed_const ( mode - > clock ) ;
b . full = dfixed_div ( b , a ) ;
c . full = dfixed_const ( latency_watermark_a ) ;
c . full = dfixed_mul ( c , b ) ;
c . full = dfixed_mul ( c , amdgpu_crtc - > hsc ) ;
c . full = dfixed_div ( c , a ) ;
a . full = dfixed_const ( 16 ) ;
c . full = dfixed_div ( c , a ) ;
priority_a_mark = dfixed_trunc ( c ) ;
priority_a_cnt | = priority_a_mark & PRIORITY_MARK_MASK ;
a . full = dfixed_const ( 1000 ) ;
b . full = dfixed_const ( mode - > clock ) ;
b . full = dfixed_div ( b , a ) ;
c . full = dfixed_const ( latency_watermark_b ) ;
c . full = dfixed_mul ( c , b ) ;
c . full = dfixed_mul ( c , amdgpu_crtc - > hsc ) ;
c . full = dfixed_div ( c , a ) ;
a . full = dfixed_const ( 16 ) ;
c . full = dfixed_div ( c , a ) ;
priority_b_mark = dfixed_trunc ( c ) ;
priority_b_cnt | = priority_b_mark & PRIORITY_MARK_MASK ;
2017-04-24 01:02:46 +02:00
lb_vblank_lead_lines = DIV_ROUND_UP ( lb_size , mode - > crtc_hdisplay ) ;
2016-01-19 14:03:24 +08:00
}
/* select wm A */
2016-11-14 13:55:17 -05:00
arb_control3 = RREG32 ( mmDPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc - > crtc_offset ) ;
2016-01-19 14:03:24 +08:00
tmp = arb_control3 ;
tmp & = ~ LATENCY_WATERMARK_MASK ( 3 ) ;
tmp | = LATENCY_WATERMARK_MASK ( 1 ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc - > crtc_offset , tmp ) ;
WREG32 ( mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( latency_watermark_a < < DPG_PIPE_URGENCY_CONTROL__URGENCY_LOW_WATERMARK__SHIFT ) |
( line_time < < DPG_PIPE_URGENCY_CONTROL__URGENCY_HIGH_WATERMARK__SHIFT ) ) ) ;
2016-01-19 14:03:24 +08:00
/* select wm B */
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc - > crtc_offset ) ;
2016-01-19 14:03:24 +08:00
tmp & = ~ LATENCY_WATERMARK_MASK ( 3 ) ;
tmp | = LATENCY_WATERMARK_MASK ( 2 ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc - > crtc_offset , tmp ) ;
WREG32 ( mmDPG_PIPE_URGENCY_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( latency_watermark_b < < DPG_PIPE_URGENCY_CONTROL__URGENCY_LOW_WATERMARK__SHIFT ) |
( line_time < < DPG_PIPE_URGENCY_CONTROL__URGENCY_HIGH_WATERMARK__SHIFT ) ) ) ;
2016-01-19 14:03:24 +08:00
/* restore original selection */
2016-11-14 13:55:17 -05:00
WREG32 ( mmDPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc - > crtc_offset , arb_control3 ) ;
2016-01-19 14:03:24 +08:00
/* write the priority marks */
2016-11-14 13:55:17 -05:00
WREG32 ( mmPRIORITY_A_CNT + amdgpu_crtc - > crtc_offset , priority_a_cnt ) ;
WREG32 ( mmPRIORITY_B_CNT + amdgpu_crtc - > crtc_offset , priority_b_cnt ) ;
2016-01-19 14:03:24 +08:00
/* save values for DPM */
amdgpu_crtc - > line_time = line_time ;
amdgpu_crtc - > wm_high = latency_watermark_a ;
2017-04-24 01:02:46 +02:00
/* Save number of lines the linebuffer leads before the scanout */
amdgpu_crtc - > lb_vblank_lead_lines = lb_vblank_lead_lines ;
2016-01-19 14:03:24 +08:00
}
/* watermark setup */
static u32 dce_v6_0_line_buffer_adjust ( struct amdgpu_device * adev ,
struct amdgpu_crtc * amdgpu_crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * other_mode )
{
u32 tmp , buffer_alloc , i ;
u32 pipe_offset = amdgpu_crtc - > crtc_id * 0x8 ;
/*
* Line Buffer Setup
* There are 3 line buffers , each one shared by 2 display controllers .
2016-11-14 13:55:17 -05:00
* mmDC_LB_MEMORY_SPLIT controls how that line buffer is shared between
2016-01-19 14:03:24 +08:00
* the display controllers . The paritioning is done via one of four
* preset allocations specified in bits 21 : 20 :
* 0 - half lb
* 2 - whole lb , other crtc must be disabled
*/
/* this can get tricky if we have two large displays on a paired group
* of crtcs . Ideally for multiple large displays we ' d assign them to
* non - linked crtcs for maximum line buffer allocation .
*/
if ( amdgpu_crtc - > base . enabled & & mode ) {
if ( other_mode ) {
tmp = 0 ; /* 1/2 */
buffer_alloc = 1 ;
} else {
tmp = 2 ; /* whole */
buffer_alloc = 2 ;
}
} else {
tmp = 0 ;
buffer_alloc = 0 ;
}
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LB_MEMORY_SPLIT + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
DC_LB_MEMORY_CONFIG ( tmp ) ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmPIPE0_DMIF_BUFFER_CONTROL + pipe_offset ,
( buffer_alloc < < PIPE0_DMIF_BUFFER_CONTROL__DMIF_BUFFERS_ALLOCATED__SHIFT ) ) ;
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < adev - > usec_timeout ; i + + ) {
2016-11-14 13:55:17 -05:00
if ( RREG32 ( mmPIPE0_DMIF_BUFFER_CONTROL + pipe_offset ) &
PIPE0_DMIF_BUFFER_CONTROL__DMIF_BUFFERS_ALLOCATION_COMPLETED_MASK )
2016-01-19 14:03:24 +08:00
break ;
udelay ( 1 ) ;
}
if ( amdgpu_crtc - > base . enabled & & mode ) {
switch ( tmp ) {
case 0 :
default :
return 4096 * 2 ;
case 2 :
return 8192 * 2 ;
}
}
/* controller not enabled, so no lb used */
return 0 ;
}
/**
*
* dce_v6_0_bandwidth_update - program display watermarks
*
* @ adev : amdgpu_device pointer
*
* Calculate and program the display watermarks and line
* buffer allocation ( CIK ) .
*/
static void dce_v6_0_bandwidth_update ( struct amdgpu_device * adev )
{
struct drm_display_mode * mode0 = NULL ;
struct drm_display_mode * mode1 = NULL ;
u32 num_heads = 0 , lb_size ;
int i ;
if ( ! adev - > mode_info . mode_config_initialized )
return ;
2018-01-19 15:28:27 -05:00
amdgpu_display_update_priority ( adev ) ;
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + ) {
if ( adev - > mode_info . crtcs [ i ] - > base . enabled )
num_heads + + ;
}
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + = 2 ) {
mode0 = & adev - > mode_info . crtcs [ i ] - > base . mode ;
mode1 = & adev - > mode_info . crtcs [ i + 1 ] - > base . mode ;
lb_size = dce_v6_0_line_buffer_adjust ( adev , adev - > mode_info . crtcs [ i ] , mode0 , mode1 ) ;
dce_v6_0_program_watermarks ( adev , adev - > mode_info . crtcs [ i ] , lb_size , num_heads ) ;
lb_size = dce_v6_0_line_buffer_adjust ( adev , adev - > mode_info . crtcs [ i + 1 ] , mode1 , mode0 ) ;
dce_v6_0_program_watermarks ( adev , adev - > mode_info . crtcs [ i + 1 ] , lb_size , num_heads ) ;
}
}
2017-02-19 19:56:17 +08:00
2016-01-19 14:03:24 +08:00
static void dce_v6_0_audio_get_connected_pins ( struct amdgpu_device * adev )
{
int i ;
2017-02-19 19:56:17 +08:00
u32 tmp ;
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + ) {
2017-02-19 19:56:17 +08:00
tmp = RREG32_AUDIO_ENDPT ( adev - > mode_info . audio . pin [ i ] . offset ,
ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT ) ;
if ( REG_GET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT ,
PORT_CONNECTIVITY ) )
2016-01-19 14:03:24 +08:00
adev - > mode_info . audio . pin [ i ] . connected = false ;
else
adev - > mode_info . audio . pin [ i ] . connected = true ;
}
}
static struct amdgpu_audio_pin * dce_v6_0_audio_get_pin ( struct amdgpu_device * adev )
{
int i ;
dce_v6_0_audio_get_connected_pins ( adev ) ;
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + ) {
if ( adev - > mode_info . audio . pin [ i ] . connected )
return & adev - > mode_info . audio . pin [ i ] ;
}
DRM_ERROR ( " No connected audio pins found! \n " ) ;
return NULL ;
}
2017-02-19 19:56:17 +08:00
static void dce_v6_0_audio_select_pin ( struct drm_encoder * encoder )
2016-01-19 14:03:24 +08:00
{
struct amdgpu_device * adev = encoder - > dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
if ( ! dig | | ! dig - > afmt | | ! dig - > afmt - > pin )
return ;
2017-02-19 19:56:17 +08:00
WREG32 ( mmAFMT_AUDIO_SRC_CONTROL + dig - > afmt - > offset ,
REG_SET_FIELD ( 0 , AFMT_AUDIO_SRC_CONTROL , AFMT_AUDIO_SRC_SELECT ,
dig - > afmt - > pin - > id ) ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_audio_write_latency_fields ( struct drm_encoder * encoder ,
struct drm_display_mode * mode )
{
2017-02-19 19:56:17 +08:00
struct amdgpu_device * adev = encoder - > dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
struct drm_connector * connector ;
struct amdgpu_connector * amdgpu_connector = NULL ;
int interlace = 0 ;
u32 tmp ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
if ( connector - > encoder = = encoder ) {
amdgpu_connector = to_amdgpu_connector ( connector ) ;
break ;
}
}
if ( ! amdgpu_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
interlace = 1 ;
if ( connector - > latency_present [ interlace ] ) {
tmp = REG_SET_FIELD ( 0 , AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC ,
VIDEO_LIPSYNC , connector - > video_latency [ interlace ] ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC ,
AUDIO_LIPSYNC , connector - > audio_latency [ interlace ] ) ;
} else {
tmp = REG_SET_FIELD ( 0 , AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC ,
VIDEO_LIPSYNC , 0 ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC ,
AUDIO_LIPSYNC , 0 ) ;
}
WREG32_AUDIO_ENDPT ( dig - > afmt - > pin - > offset ,
ixAZALIA_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC , tmp ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_audio_write_speaker_allocation ( struct drm_encoder * encoder )
{
2017-02-19 19:56:17 +08:00
struct amdgpu_device * adev = encoder - > dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
struct drm_connector * connector ;
struct amdgpu_connector * amdgpu_connector = NULL ;
u8 * sadb = NULL ;
int sad_count ;
u32 tmp ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
if ( connector - > encoder = = encoder ) {
amdgpu_connector = to_amdgpu_connector ( connector ) ;
break ;
}
}
if ( ! amdgpu_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
sad_count = drm_edid_to_speaker_allocation ( amdgpu_connector_edid ( connector ) , & sadb ) ;
if ( sad_count < 0 ) {
DRM_ERROR ( " Couldn't read Speaker Allocation Data Block: %d \n " , sad_count ) ;
sad_count = 0 ;
}
/* program the speaker allocation */
tmp = RREG32_AUDIO_ENDPT ( dig - > afmt - > pin - > offset ,
ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
HDMI_CONNECTION , 0 ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
DP_CONNECTION , 0 ) ;
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_DisplayPort )
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
DP_CONNECTION , 1 ) ;
else
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
HDMI_CONNECTION , 1 ) ;
if ( sad_count )
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
SPEAKER_ALLOCATION , sadb [ 0 ] ) ;
else
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER ,
SPEAKER_ALLOCATION , 5 ) ; /* stereo */
WREG32_AUDIO_ENDPT ( dig - > afmt - > pin - > offset ,
ixAZALIA_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER , tmp ) ;
kfree ( sadb ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_audio_write_sad_regs ( struct drm_encoder * encoder )
{
2017-02-19 19:56:17 +08:00
struct amdgpu_device * adev = encoder - > dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
struct drm_connector * connector ;
struct amdgpu_connector * amdgpu_connector = NULL ;
struct cea_sad * sads ;
int i , sad_count ;
static const u16 eld_reg_to_type [ ] [ 2 ] = {
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 , HDMI_AUDIO_CODING_TYPE_PCM } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR1 , HDMI_AUDIO_CODING_TYPE_AC3 } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR2 , HDMI_AUDIO_CODING_TYPE_MPEG1 } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR3 , HDMI_AUDIO_CODING_TYPE_MP3 } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR4 , HDMI_AUDIO_CODING_TYPE_MPEG2 } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR5 , HDMI_AUDIO_CODING_TYPE_AAC_LC } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR6 , HDMI_AUDIO_CODING_TYPE_DTS } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR7 , HDMI_AUDIO_CODING_TYPE_ATRAC } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR9 , HDMI_AUDIO_CODING_TYPE_EAC3 } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR10 , HDMI_AUDIO_CODING_TYPE_DTS_HD } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR11 , HDMI_AUDIO_CODING_TYPE_MLP } ,
{ ixAZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13 , HDMI_AUDIO_CODING_TYPE_WMA_PRO } ,
} ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
if ( connector - > encoder = = encoder ) {
amdgpu_connector = to_amdgpu_connector ( connector ) ;
break ;
}
}
if ( ! amdgpu_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
sad_count = drm_edid_to_sad ( amdgpu_connector_edid ( connector ) , & sads ) ;
if ( sad_count < = 0 ) {
DRM_ERROR ( " Couldn't read SADs: %d \n " , sad_count ) ;
return ;
}
for ( i = 0 ; i < ARRAY_SIZE ( eld_reg_to_type ) ; i + + ) {
u32 tmp = 0 ;
u8 stereo_freqs = 0 ;
int max_channels = - 1 ;
int j ;
for ( j = 0 ; j < sad_count ; j + + ) {
struct cea_sad * sad = & sads [ j ] ;
if ( sad - > format = = eld_reg_to_type [ i ] [ 1 ] ) {
if ( sad - > channels > max_channels ) {
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 ,
MAX_CHANNELS , sad - > channels ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 ,
DESCRIPTOR_BYTE_2 , sad - > byte2 ) ;
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 ,
SUPPORTED_FREQUENCIES , sad - > freq ) ;
max_channels = sad - > channels ;
}
if ( sad - > format = = HDMI_AUDIO_CODING_TYPE_PCM )
stereo_freqs | = sad - > freq ;
else
break ;
}
}
tmp = REG_SET_FIELD ( tmp , AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0 ,
SUPPORTED_FREQUENCIES_STEREO , stereo_freqs ) ;
WREG32_AUDIO_ENDPT ( dig - > afmt - > pin - > offset , eld_reg_to_type [ i ] [ 0 ] , tmp ) ;
}
kfree ( sads ) ;
2016-01-19 14:03:24 +08:00
}
2017-02-19 19:56:17 +08:00
2016-01-19 14:03:24 +08:00
static void dce_v6_0_audio_enable ( struct amdgpu_device * adev ,
struct amdgpu_audio_pin * pin ,
bool enable )
{
2017-02-19 19:56:17 +08:00
if ( ! pin )
return ;
WREG32_AUDIO_ENDPT ( pin - > offset , ixAZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL ,
enable ? AZALIA_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL__AUDIO_ENABLED_MASK : 0 ) ;
2016-01-19 14:03:24 +08:00
}
static const u32 pin_offsets [ 7 ] =
{
( 0x1780 - 0x1780 ) ,
( 0x1786 - 0x1780 ) ,
( 0x178c - 0x1780 ) ,
( 0x1792 - 0x1780 ) ,
( 0x1798 - 0x1780 ) ,
( 0x179d - 0x1780 ) ,
( 0x17a4 - 0x1780 ) ,
} ;
static int dce_v6_0_audio_init ( struct amdgpu_device * adev )
{
2017-02-19 19:56:17 +08:00
int i ;
if ( ! amdgpu_audio )
return 0 ;
adev - > mode_info . audio . enabled = true ;
switch ( adev - > asic_type ) {
case CHIP_TAHITI :
case CHIP_PITCAIRN :
case CHIP_VERDE :
default :
adev - > mode_info . audio . num_pins = 6 ;
break ;
case CHIP_OLAND :
adev - > mode_info . audio . num_pins = 2 ;
break ;
}
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + ) {
adev - > mode_info . audio . pin [ i ] . channels = - 1 ;
adev - > mode_info . audio . pin [ i ] . rate = - 1 ;
adev - > mode_info . audio . pin [ i ] . bits_per_sample = - 1 ;
adev - > mode_info . audio . pin [ i ] . status_bits = 0 ;
adev - > mode_info . audio . pin [ i ] . category_code = 0 ;
adev - > mode_info . audio . pin [ i ] . connected = false ;
adev - > mode_info . audio . pin [ i ] . offset = pin_offsets [ i ] ;
adev - > mode_info . audio . pin [ i ] . id = i ;
dce_v6_0_audio_enable ( adev , & adev - > mode_info . audio . pin [ i ] , false ) ;
}
2016-01-19 14:03:24 +08:00
return 0 ;
}
static void dce_v6_0_audio_fini ( struct amdgpu_device * adev )
{
2017-02-19 19:56:17 +08:00
int i ;
2016-01-19 14:03:24 +08:00
2017-02-19 19:56:17 +08:00
if ( ! amdgpu_audio )
return ;
if ( ! adev - > mode_info . audio . enabled )
return ;
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + )
dce_v6_0_audio_enable ( adev , & adev - > mode_info . audio . pin [ i ] , false ) ;
adev - > mode_info . audio . enabled = false ;
2016-01-19 14:03:24 +08:00
}
2017-02-21 11:41:40 +08:00
static void dce_v6_0_audio_set_vbi_packet ( struct drm_encoder * encoder )
2016-01-19 14:03:24 +08:00
{
2017-02-21 11:41:40 +08:00
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
tmp = RREG32 ( mmHDMI_VBI_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_VBI_PACKET_CONTROL , HDMI_NULL_SEND , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_VBI_PACKET_CONTROL , HDMI_GC_SEND , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_VBI_PACKET_CONTROL , HDMI_GC_CONT , 1 ) ;
WREG32 ( mmHDMI_VBI_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
2016-01-19 14:03:24 +08:00
}
2017-02-21 11:41:40 +08:00
static void dce_v6_0_audio_set_acr ( struct drm_encoder * encoder ,
uint32_t clock , int bpc )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_afmt_acr acr = amdgpu_afmt_acr ( clock ) ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
tmp = RREG32 ( mmHDMI_ACR_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_PACKET_CONTROL , HDMI_ACR_AUTO_SEND , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_PACKET_CONTROL , HDMI_ACR_SOURCE ,
bpc > 8 ? 0 : 1 ) ;
WREG32 ( mmHDMI_ACR_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_32_0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_32_0 , HDMI_ACR_CTS_32 , acr . cts_32khz ) ;
WREG32 ( mmHDMI_ACR_32_0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_32_1 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_32_1 , HDMI_ACR_N_32 , acr . n_32khz ) ;
WREG32 ( mmHDMI_ACR_32_1 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_44_0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_44_0 , HDMI_ACR_CTS_44 , acr . cts_44_1khz ) ;
WREG32 ( mmHDMI_ACR_44_0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_44_1 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_44_1 , HDMI_ACR_N_44 , acr . n_44_1khz ) ;
WREG32 ( mmHDMI_ACR_44_1 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_48_0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_48_0 , HDMI_ACR_CTS_48 , acr . cts_48khz ) ;
WREG32 ( mmHDMI_ACR_48_0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_ACR_48_1 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_ACR_48_1 , HDMI_ACR_N_48 , acr . n_48khz ) ;
WREG32 ( mmHDMI_ACR_48_1 + dig - > afmt - > offset , tmp ) ;
2016-01-19 14:03:24 +08:00
}
2017-02-19 19:56:17 +08:00
static void dce_v6_0_audio_set_avi_infoframe ( struct drm_encoder * encoder ,
struct drm_display_mode * mode )
2016-01-19 14:03:24 +08:00
{
2017-02-19 19:56:17 +08:00
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
2019-01-08 19:28:25 +02:00
struct drm_connector * connector = amdgpu_get_connector_for_encoder ( encoder ) ;
2017-02-19 19:56:17 +08:00
struct hdmi_avi_infoframe frame ;
u8 buffer [ HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE ] ;
uint8_t * payload = buffer + 3 ;
uint8_t * header = buffer ;
ssize_t err ;
u32 tmp ;
2019-01-08 19:28:25 +02:00
err = drm_hdmi_avi_infoframe_from_display_mode ( & frame , connector , mode ) ;
2017-02-19 19:56:17 +08:00
if ( err < 0 ) {
DRM_ERROR ( " failed to setup AVI infoframe: %zd \n " , err ) ;
return ;
}
err = hdmi_avi_infoframe_pack ( & frame , buffer , sizeof ( buffer ) ) ;
if ( err < 0 ) {
DRM_ERROR ( " failed to pack AVI infoframe: %zd \n " , err ) ;
return ;
}
WREG32 ( mmAFMT_AVI_INFO0 + dig - > afmt - > offset ,
payload [ 0x0 ] | ( payload [ 0x1 ] < < 8 ) | ( payload [ 0x2 ] < < 16 ) | ( payload [ 0x3 ] < < 24 ) ) ;
WREG32 ( mmAFMT_AVI_INFO1 + dig - > afmt - > offset ,
payload [ 0x4 ] | ( payload [ 0x5 ] < < 8 ) | ( payload [ 0x6 ] < < 16 ) | ( payload [ 0x7 ] < < 24 ) ) ;
WREG32 ( mmAFMT_AVI_INFO2 + dig - > afmt - > offset ,
payload [ 0x8 ] | ( payload [ 0x9 ] < < 8 ) | ( payload [ 0xA ] < < 16 ) | ( payload [ 0xB ] < < 24 ) ) ;
WREG32 ( mmAFMT_AVI_INFO3 + dig - > afmt - > offset ,
payload [ 0xC ] | ( payload [ 0xD ] < < 8 ) | ( header [ 1 ] < < 24 ) ) ;
tmp = RREG32 ( mmHDMI_INFOFRAME_CONTROL1 + dig - > afmt - > offset ) ;
/* anything other than 0 */
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL1 ,
HDMI_AUDIO_INFO_LINE , 2 ) ;
WREG32 ( mmHDMI_INFOFRAME_CONTROL1 + dig - > afmt - > offset , tmp ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_audio_set_dto ( struct drm_encoder * encoder , u32 clock )
{
2017-02-19 19:56:17 +08:00
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( encoder - > crtc ) ;
2017-02-21 11:41:40 +08:00
int em = amdgpu_atombios_encoder_get_encoder_mode ( encoder ) ;
2017-02-19 19:56:17 +08:00
u32 tmp ;
/*
* Two dtos : generally use dto0 for hdmi , dto1 for dp .
* Express [ 24 MHz / target pixel clock ] as an exact rational
* number ( coefficient of two integer numbers . DCCG_AUDIO_DTOx_PHASE
* is the numerator , DCCG_AUDIO_DTOx_MODULE is the denominator
*/
tmp = RREG32 ( mmDCCG_AUDIO_DTO_SOURCE ) ;
tmp = REG_SET_FIELD ( tmp , DCCG_AUDIO_DTO_SOURCE ,
DCCG_AUDIO_DTO0_SOURCE_SEL , amdgpu_crtc - > crtc_id ) ;
2017-02-21 11:41:40 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI ) {
tmp = REG_SET_FIELD ( tmp , DCCG_AUDIO_DTO_SOURCE ,
DCCG_AUDIO_DTO_SEL , 0 ) ;
} else if ( ENCODER_MODE_IS_DP ( em ) ) {
tmp = REG_SET_FIELD ( tmp , DCCG_AUDIO_DTO_SOURCE ,
DCCG_AUDIO_DTO_SEL , 1 ) ;
}
2017-02-19 19:56:17 +08:00
WREG32 ( mmDCCG_AUDIO_DTO_SOURCE , tmp ) ;
2017-02-21 11:41:40 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI ) {
WREG32 ( mmDCCG_AUDIO_DTO0_PHASE , 24000 ) ;
WREG32 ( mmDCCG_AUDIO_DTO0_MODULE , clock ) ;
} else if ( ENCODER_MODE_IS_DP ( em ) ) {
WREG32 ( mmDCCG_AUDIO_DTO1_PHASE , 24000 ) ;
WREG32 ( mmDCCG_AUDIO_DTO1_MODULE , clock ) ;
}
2016-01-19 14:03:24 +08:00
}
2017-02-19 19:56:17 +08:00
static void dce_v6_0_audio_set_packet ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
tmp = RREG32 ( mmAFMT_INFOFRAME_CONTROL0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_INFOFRAME_CONTROL0 , AFMT_AUDIO_INFO_UPDATE , 1 ) ;
WREG32 ( mmAFMT_INFOFRAME_CONTROL0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_60958_0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_0 , AFMT_60958_CS_CHANNEL_NUMBER_L , 1 ) ;
WREG32 ( mmAFMT_60958_0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_60958_1 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_1 , AFMT_60958_CS_CHANNEL_NUMBER_R , 2 ) ;
WREG32 ( mmAFMT_60958_1 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_60958_2 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_2 , 3 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_3 , 4 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_4 , 5 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_5 , 6 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_6 , 7 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_60958_2 , AFMT_60958_CS_CHANNEL_NUMBER_7 , 8 ) ;
WREG32 ( mmAFMT_60958_2 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_AUDIO_PACKET_CONTROL2 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL2 , AFMT_AUDIO_CHANNEL_ENABLE , 0xff ) ;
WREG32 ( mmAFMT_AUDIO_PACKET_CONTROL2 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_AUDIO_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_AUDIO_PACKET_CONTROL , HDMI_AUDIO_DELAY_EN , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_AUDIO_PACKET_CONTROL , HDMI_AUDIO_PACKETS_PER_LINE , 3 ) ;
WREG32 ( mmHDMI_AUDIO_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL , AFMT_RESET_FIFO_WHEN_AUDIO_DIS , 1 ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL , AFMT_60958_CS_UPDATE , 1 ) ;
WREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
}
static void dce_v6_0_audio_set_mute ( struct drm_encoder * encoder , bool mute )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
tmp = RREG32 ( mmHDMI_GC + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_GC , HDMI_GC_AVMUTE , mute ? 1 : 0 ) ;
WREG32 ( mmHDMI_GC + dig - > afmt - > offset , tmp ) ;
}
2017-02-21 11:41:40 +08:00
static void dce_v6_0_audio_hdmi_enable ( struct drm_encoder * encoder , bool enable )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
if ( enable ) {
tmp = RREG32 ( mmHDMI_INFOFRAME_CONTROL0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AVI_INFO_SEND , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AVI_INFO_CONT , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AUDIO_INFO_SEND , 1 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AUDIO_INFO_CONT , 1 ) ;
WREG32 ( mmHDMI_INFOFRAME_CONTROL0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmHDMI_INFOFRAME_CONTROL1 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL1 , HDMI_AVI_INFO_LINE , 2 ) ;
WREG32 ( mmHDMI_INFOFRAME_CONTROL1 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL , AFMT_AUDIO_SAMPLE_SEND , 1 ) ;
WREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
} else {
tmp = RREG32 ( mmHDMI_INFOFRAME_CONTROL0 + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AVI_INFO_SEND , 0 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AVI_INFO_CONT , 0 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AUDIO_INFO_SEND , 0 ) ;
tmp = REG_SET_FIELD ( tmp , HDMI_INFOFRAME_CONTROL0 , HDMI_AUDIO_INFO_CONT , 0 ) ;
WREG32 ( mmHDMI_INFOFRAME_CONTROL0 + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL , AFMT_AUDIO_SAMPLE_SEND , 0 ) ;
WREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
}
}
2017-02-19 19:56:17 +08:00
static void dce_v6_0_audio_dp_enable ( struct drm_encoder * encoder , bool enable )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
u32 tmp ;
if ( enable ) {
tmp = RREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , AFMT_AUDIO_PACKET_CONTROL , AFMT_AUDIO_SAMPLE_SEND , 1 ) ;
WREG32 ( mmAFMT_AUDIO_PACKET_CONTROL + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmDP_SEC_TIMESTAMP + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , DP_SEC_TIMESTAMP , DP_SEC_TIMESTAMP_MODE , 1 ) ;
WREG32 ( mmDP_SEC_TIMESTAMP + dig - > afmt - > offset , tmp ) ;
tmp = RREG32 ( mmDP_SEC_CNTL + dig - > afmt - > offset ) ;
tmp = REG_SET_FIELD ( tmp , DP_SEC_CNTL , DP_SEC_ASP_ENABLE , 1 ) ;
tmp = REG_SET_FIELD ( tmp , DP_SEC_CNTL , DP_SEC_ATP_ENABLE , 1 ) ;
tmp = REG_SET_FIELD ( tmp , DP_SEC_CNTL , DP_SEC_AIP_ENABLE , 1 ) ;
tmp = REG_SET_FIELD ( tmp , DP_SEC_CNTL , DP_SEC_STREAM_ENABLE , 1 ) ;
WREG32 ( mmDP_SEC_CNTL + dig - > afmt - > offset , tmp ) ;
} else {
WREG32 ( mmDP_SEC_CNTL + dig - > afmt - > offset , 0 ) ;
}
}
2016-01-19 14:03:24 +08:00
static void dce_v6_0_afmt_setmode ( struct drm_encoder * encoder ,
struct drm_display_mode * mode )
{
2017-02-19 19:56:17 +08:00
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
struct drm_connector * connector ;
struct amdgpu_connector * amdgpu_connector = NULL ;
2017-02-21 11:41:40 +08:00
int em = amdgpu_atombios_encoder_get_encoder_mode ( encoder ) ;
int bpc = 8 ;
2017-02-19 19:56:17 +08:00
if ( ! dig | | ! dig - > afmt )
return ;
list_for_each_entry ( connector , & encoder - > dev - > mode_config . connector_list , head ) {
if ( connector - > encoder = = encoder ) {
amdgpu_connector = to_amdgpu_connector ( connector ) ;
break ;
}
}
if ( ! amdgpu_connector ) {
DRM_ERROR ( " Couldn't find encoder's connector \n " ) ;
return ;
}
if ( ! dig - > afmt - > enabled )
return ;
dig - > afmt - > pin = dce_v6_0_audio_get_pin ( adev ) ;
if ( ! dig - > afmt - > pin )
return ;
2017-02-21 11:41:40 +08:00
if ( encoder - > crtc ) {
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( encoder - > crtc ) ;
bpc = amdgpu_crtc - > bpc ;
}
2017-02-19 19:56:17 +08:00
/* disable audio before setting up hw */
dce_v6_0_audio_enable ( adev , dig - > afmt - > pin , false ) ;
dce_v6_0_audio_set_mute ( encoder , true ) ;
dce_v6_0_audio_write_speaker_allocation ( encoder ) ;
dce_v6_0_audio_write_sad_regs ( encoder ) ;
dce_v6_0_audio_write_latency_fields ( encoder , mode ) ;
2017-02-21 11:41:40 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI ) {
dce_v6_0_audio_set_dto ( encoder , mode - > clock ) ;
dce_v6_0_audio_set_vbi_packet ( encoder ) ;
dce_v6_0_audio_set_acr ( encoder , mode - > clock , bpc ) ;
} else if ( ENCODER_MODE_IS_DP ( em ) ) {
dce_v6_0_audio_set_dto ( encoder , adev - > clock . default_dispclk * 10 ) ;
}
2017-02-19 19:56:17 +08:00
dce_v6_0_audio_set_packet ( encoder ) ;
dce_v6_0_audio_select_pin ( encoder ) ;
dce_v6_0_audio_set_avi_infoframe ( encoder , mode ) ;
dce_v6_0_audio_set_mute ( encoder , false ) ;
2017-02-21 11:41:40 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI ) {
dce_v6_0_audio_hdmi_enable ( encoder , 1 ) ;
} else if ( ENCODER_MODE_IS_DP ( em ) ) {
dce_v6_0_audio_dp_enable ( encoder , 1 ) ;
}
2017-02-19 19:56:17 +08:00
/* enable audio after setting up hw */
dce_v6_0_audio_enable ( adev , dig - > afmt - > pin , true ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_afmt_enable ( struct drm_encoder * encoder , bool enable )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
if ( ! dig | | ! dig - > afmt )
return ;
/* Silent, r600_hdmi_enable will raise WARN for us */
if ( enable & & dig - > afmt - > enabled )
return ;
2017-02-19 19:56:17 +08:00
2016-01-19 14:03:24 +08:00
if ( ! enable & & ! dig - > afmt - > enabled )
return ;
if ( ! enable & & dig - > afmt - > pin ) {
dce_v6_0_audio_enable ( adev , dig - > afmt - > pin , false ) ;
dig - > afmt - > pin = NULL ;
}
dig - > afmt - > enabled = enable ;
DRM_DEBUG ( " %sabling AFMT interface @ 0x%04X for encoder 0x%x \n " ,
enable ? " En " : " Dis " , dig - > afmt - > offset , amdgpu_encoder - > encoder_id ) ;
}
2016-09-22 12:20:58 -04:00
static int dce_v6_0_afmt_init ( struct amdgpu_device * adev )
2016-01-19 14:03:24 +08:00
{
2016-09-22 12:20:58 -04:00
int i , j ;
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < adev - > mode_info . num_dig ; i + + )
adev - > mode_info . afmt [ i ] = NULL ;
2016-09-22 12:20:58 -04:00
/* DCE6 has audio blocks tied to DIG encoders */
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < adev - > mode_info . num_dig ; i + + ) {
adev - > mode_info . afmt [ i ] = kzalloc ( sizeof ( struct amdgpu_afmt ) , GFP_KERNEL ) ;
if ( adev - > mode_info . afmt [ i ] ) {
adev - > mode_info . afmt [ i ] - > offset = dig_offsets [ i ] ;
adev - > mode_info . afmt [ i ] - > id = i ;
2016-09-22 12:20:58 -04:00
} else {
for ( j = 0 ; j < i ; j + + ) {
kfree ( adev - > mode_info . afmt [ j ] ) ;
adev - > mode_info . afmt [ j ] = NULL ;
}
DRM_ERROR ( " Out of memory allocating afmt table \n " ) ;
return - ENOMEM ;
2016-01-19 14:03:24 +08:00
}
}
2016-09-22 12:20:58 -04:00
return 0 ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_afmt_fini ( struct amdgpu_device * adev )
{
int i ;
for ( i = 0 ; i < adev - > mode_info . num_dig ; i + + ) {
kfree ( adev - > mode_info . afmt [ i ] ) ;
adev - > mode_info . afmt [ i ] = NULL ;
}
}
static const u32 vga_control_regs [ 6 ] =
{
2016-11-14 13:55:17 -05:00
mmD1VGA_CONTROL ,
mmD2VGA_CONTROL ,
mmD3VGA_CONTROL ,
mmD4VGA_CONTROL ,
mmD5VGA_CONTROL ,
mmD6VGA_CONTROL ,
2016-01-19 14:03:24 +08:00
} ;
static void dce_v6_0_vga_enable ( struct drm_crtc * crtc , bool enable )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
u32 vga_control ;
vga_control = RREG32 ( vga_control_regs [ amdgpu_crtc - > crtc_id ] ) & ~ 1 ;
2016-09-22 12:29:40 -04:00
WREG32 ( vga_control_regs [ amdgpu_crtc - > crtc_id ] , vga_control | ( enable ? 1 : 0 ) ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_grph_enable ( struct drm_crtc * crtc , bool enable )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_ENABLE + amdgpu_crtc - > crtc_offset , enable ? 1 : 0 ) ;
2016-01-19 14:03:24 +08:00
}
static int dce_v6_0_crtc_do_set_base ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int x , int y , int atomic )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct drm_framebuffer * target_fb ;
struct drm_gem_object * obj ;
2016-09-15 15:06:50 +02:00
struct amdgpu_bo * abo ;
2016-01-19 14:03:24 +08:00
uint64_t fb_location , tiling_flags ;
uint32_t fb_format , fb_pitch_pixels , pipe_config ;
2016-11-14 13:55:17 -05:00
u32 fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_NONE ) ;
2016-01-19 14:03:24 +08:00
u32 viewport_w , viewport_h ;
int r ;
bool bypass_lut = false ;
2016-11-12 01:12:56 +00:00
struct drm_format_name_buf format_name ;
2016-01-19 14:03:24 +08:00
/* no fb bound */
if ( ! atomic & & ! crtc - > primary - > fb ) {
DRM_DEBUG_KMS ( " No FB bound \n " ) ;
return 0 ;
}
2018-03-30 15:11:38 +01:00
if ( atomic )
2016-01-19 14:03:24 +08:00
target_fb = fb ;
2018-03-30 15:11:38 +01:00
else
2016-01-19 14:03:24 +08:00
target_fb = crtc - > primary - > fb ;
/* If atomic, assume fb object is pinned & idle & fenced and
* just update base pointers
*/
2018-03-30 15:11:38 +01:00
obj = target_fb - > obj [ 0 ] ;
2016-09-15 15:06:50 +02:00
abo = gem_to_amdgpu_bo ( obj ) ;
r = amdgpu_bo_reserve ( abo , false ) ;
2016-01-19 14:03:24 +08:00
if ( unlikely ( r ! = 0 ) )
return r ;
2018-06-25 12:51:14 +08:00
if ( ! atomic ) {
r = amdgpu_bo_pin ( abo , AMDGPU_GEM_DOMAIN_VRAM ) ;
2016-01-19 14:03:24 +08:00
if ( unlikely ( r ! = 0 ) ) {
2016-09-15 15:06:50 +02:00
amdgpu_bo_unreserve ( abo ) ;
2016-01-19 14:03:24 +08:00
return - EINVAL ;
}
}
2018-06-25 12:51:14 +08:00
fb_location = amdgpu_bo_gpu_offset ( abo ) ;
2016-01-19 14:03:24 +08:00
2016-09-15 15:06:50 +02:00
amdgpu_bo_get_tiling_flags ( abo , & tiling_flags ) ;
amdgpu_bo_unreserve ( abo ) ;
2016-01-19 14:03:24 +08:00
2016-12-14 23:32:55 +02:00
switch ( target_fb - > format - > format ) {
2016-01-19 14:03:24 +08:00
case DRM_FORMAT_C8 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_8BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_INDEXED ) ) ;
2016-01-19 14:03:24 +08:00
break ;
case DRM_FORMAT_XRGB4444 :
case DRM_FORMAT_ARGB4444 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_16BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB4444 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN16 ) ;
2016-01-19 14:03:24 +08:00
# endif
break ;
case DRM_FORMAT_XRGB1555 :
case DRM_FORMAT_ARGB1555 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_16BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB1555 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN16 ) ;
2016-01-19 14:03:24 +08:00
# endif
break ;
case DRM_FORMAT_BGRX5551 :
case DRM_FORMAT_BGRA5551 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_16BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_BGRA5551 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN16 ) ;
2016-01-19 14:03:24 +08:00
# endif
break ;
case DRM_FORMAT_RGB565 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_16BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB565 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN16 ) ;
2016-01-19 14:03:24 +08:00
# endif
break ;
case DRM_FORMAT_XRGB8888 :
case DRM_FORMAT_ARGB8888 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_32BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB8888 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN32 ) ;
2016-01-19 14:03:24 +08:00
# endif
break ;
case DRM_FORMAT_XRGB2101010 :
case DRM_FORMAT_ARGB2101010 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_32BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB2101010 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN32 ) ;
2016-01-19 14:03:24 +08:00
# endif
/* Greater 8 bpc fb needs to bypass hw-lut to retain precision */
bypass_lut = true ;
break ;
case DRM_FORMAT_BGRX1010102 :
case DRM_FORMAT_BGRA1010102 :
2016-11-14 13:55:17 -05:00
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_32BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_BGRA1010102 ) ) ;
2016-01-19 14:03:24 +08:00
# ifdef __BIG_ENDIAN
2016-11-14 13:55:17 -05:00
fb_swap = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN32 ) ;
2016-01-19 14:03:24 +08:00
# endif
/* Greater 8 bpc fb needs to bypass hw-lut to retain precision */
bypass_lut = true ;
break ;
2018-08-12 21:43:02 +02:00
case DRM_FORMAT_XBGR8888 :
case DRM_FORMAT_ABGR8888 :
fb_format = ( GRPH_DEPTH ( GRPH_DEPTH_32BPP ) |
GRPH_FORMAT ( GRPH_FORMAT_ARGB8888 ) ) ;
fb_swap = ( GRPH_RED_CROSSBAR ( GRPH_RED_SEL_B ) |
GRPH_BLUE_CROSSBAR ( GRPH_BLUE_SEL_R ) ) ;
# ifdef __BIG_ENDIAN
fb_swap | = GRPH_ENDIAN_SWAP ( GRPH_ENDIAN_8IN32 ) ;
# endif
break ;
2016-01-19 14:03:24 +08:00
default :
DRM_ERROR ( " Unsupported screen format %s \n " ,
2016-12-14 23:32:55 +02:00
drm_get_format_name ( target_fb - > format - > format , & format_name ) ) ;
2016-01-19 14:03:24 +08:00
return - EINVAL ;
}
if ( AMDGPU_TILING_GET ( tiling_flags , ARRAY_MODE ) = = ARRAY_2D_TILED_THIN1 ) {
unsigned bankw , bankh , mtaspect , tile_split , num_banks ;
bankw = AMDGPU_TILING_GET ( tiling_flags , BANK_WIDTH ) ;
bankh = AMDGPU_TILING_GET ( tiling_flags , BANK_HEIGHT ) ;
mtaspect = AMDGPU_TILING_GET ( tiling_flags , MACRO_TILE_ASPECT ) ;
tile_split = AMDGPU_TILING_GET ( tiling_flags , TILE_SPLIT ) ;
num_banks = AMDGPU_TILING_GET ( tiling_flags , NUM_BANKS ) ;
2016-11-14 13:55:17 -05:00
fb_format | = GRPH_NUM_BANKS ( num_banks ) ;
fb_format | = GRPH_ARRAY_MODE ( GRPH_ARRAY_2D_TILED_THIN1 ) ;
fb_format | = GRPH_TILE_SPLIT ( tile_split ) ;
fb_format | = GRPH_BANK_WIDTH ( bankw ) ;
fb_format | = GRPH_BANK_HEIGHT ( bankh ) ;
fb_format | = GRPH_MACRO_TILE_ASPECT ( mtaspect ) ;
2016-09-22 12:29:40 -04:00
} else if ( AMDGPU_TILING_GET ( tiling_flags , ARRAY_MODE ) = = ARRAY_1D_TILED_THIN1 ) {
2016-11-14 13:55:17 -05:00
fb_format | = GRPH_ARRAY_MODE ( GRPH_ARRAY_1D_TILED_THIN1 ) ;
2016-09-22 12:29:40 -04:00
}
2016-01-19 14:03:24 +08:00
pipe_config = AMDGPU_TILING_GET ( tiling_flags , PIPE_CONFIG ) ;
2016-11-14 13:55:17 -05:00
fb_format | = GRPH_PIPE_CONFIG ( pipe_config ) ;
2016-01-19 14:03:24 +08:00
dce_v6_0_vga_enable ( crtc , false ) ;
/* Make sure surface address is updated at vertical blank rather than
* horizontal blank
*/
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_FLIP_CONTROL + amdgpu_crtc - > crtc_offset , 0 ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_PRIMARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
upper_32_bits ( fb_location ) ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_SECONDARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
upper_32_bits ( fb_location ) ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc - > crtc_offset ,
( u32 ) fb_location & GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS_MASK ) ;
WREG32 ( mmGRPH_SECONDARY_SURFACE_ADDRESS + amdgpu_crtc - > crtc_offset ,
( u32 ) fb_location & GRPH_PRIMARY_SURFACE_ADDRESS__GRPH_PRIMARY_SURFACE_ADDRESS_MASK ) ;
WREG32 ( mmGRPH_CONTROL + amdgpu_crtc - > crtc_offset , fb_format ) ;
WREG32 ( mmGRPH_SWAP_CNTL + amdgpu_crtc - > crtc_offset , fb_swap ) ;
2016-01-19 14:03:24 +08:00
/*
* The LUT only has 256 slots for indexing by a 8 bpc fb . Bypass the LUT
* for > 8 bpc scanout to avoid truncation of fb indices to 8 msb ' s , to
* retain the full precision throughout the pipeline .
*/
2016-11-14 13:55:17 -05:00
WREG32_P ( mmGRPH_LUT_10BIT_BYPASS + amdgpu_crtc - > crtc_offset ,
( bypass_lut ? GRPH_LUT_10BIT_BYPASS__GRPH_LUT_10BIT_BYPASS_EN_MASK : 0 ) ,
~ GRPH_LUT_10BIT_BYPASS__GRPH_LUT_10BIT_BYPASS_EN_MASK ) ;
2016-01-19 14:03:24 +08:00
if ( bypass_lut )
DRM_DEBUG_KMS ( " Bypassing hardware LUT due to 10 bit fb scanout. \n " ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_SURFACE_OFFSET_X + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmGRPH_SURFACE_OFFSET_Y + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmGRPH_X_START + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmGRPH_Y_START + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmGRPH_X_END + amdgpu_crtc - > crtc_offset , target_fb - > width ) ;
WREG32 ( mmGRPH_Y_END + amdgpu_crtc - > crtc_offset , target_fb - > height ) ;
2016-01-19 14:03:24 +08:00
2016-12-14 23:32:20 +02:00
fb_pitch_pixels = target_fb - > pitches [ 0 ] / target_fb - > format - > cpp [ 0 ] ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_PITCH + amdgpu_crtc - > crtc_offset , fb_pitch_pixels ) ;
2016-01-19 14:03:24 +08:00
dce_v6_0_grph_enable ( crtc , true ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDESKTOP_HEIGHT + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
target_fb - > height ) ;
x & = ~ 3 ;
y & = ~ 1 ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmVIEWPORT_START + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
( x < < 16 ) | y ) ;
viewport_w = crtc - > mode . hdisplay ;
viewport_h = ( crtc - > mode . vdisplay + 1 ) & ~ 1 ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmVIEWPORT_SIZE + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
( viewport_w < < 16 ) | viewport_h ) ;
/* set pageflip to happen anywhere in vblank interval */
2016-11-14 13:55:17 -05:00
WREG32 ( mmMASTER_UPDATE_MODE + amdgpu_crtc - > crtc_offset , 0 ) ;
2016-01-19 14:03:24 +08:00
if ( ! atomic & & fb & & fb ! = crtc - > primary - > fb ) {
2018-03-30 15:11:38 +01:00
abo = gem_to_amdgpu_bo ( fb - > obj [ 0 ] ) ;
2017-04-28 17:28:14 +09:00
r = amdgpu_bo_reserve ( abo , true ) ;
2016-01-19 14:03:24 +08:00
if ( unlikely ( r ! = 0 ) )
return r ;
2016-09-15 15:06:50 +02:00
amdgpu_bo_unpin ( abo ) ;
amdgpu_bo_unreserve ( abo ) ;
2016-01-19 14:03:24 +08:00
}
/* Bytes per pixel may have changed */
dce_v6_0_bandwidth_update ( adev ) ;
return 0 ;
}
static void dce_v6_0_set_interleave ( struct drm_crtc * crtc ,
struct drm_display_mode * mode )
{
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
2016-11-14 13:55:17 -05:00
WREG32 ( mmDATA_FORMAT + amdgpu_crtc - > crtc_offset ,
INTERLEAVE_EN ) ;
2016-01-19 14:03:24 +08:00
else
2016-11-14 13:55:17 -05:00
WREG32 ( mmDATA_FORMAT + amdgpu_crtc - > crtc_offset , 0 ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_crtc_load_lut ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
2017-07-13 18:25:28 +02:00
u16 * r , * g , * b ;
2016-01-19 14:03:24 +08:00
int i ;
DRM_DEBUG_KMS ( " %d \n " , amdgpu_crtc - > crtc_id ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmINPUT_CSC_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < INPUT_CSC_CONTROL__INPUT_CSC_GRPH_MODE__SHIFT ) |
( 0 < < INPUT_CSC_CONTROL__INPUT_CSC_OVL_MODE__SHIFT ) ) ) ;
WREG32 ( mmPRESCALE_GRPH_CONTROL + amdgpu_crtc - > crtc_offset ,
PRESCALE_GRPH_CONTROL__GRPH_PRESCALE_BYPASS_MASK ) ;
WREG32 ( mmPRESCALE_OVL_CONTROL + amdgpu_crtc - > crtc_offset ,
PRESCALE_OVL_CONTROL__OVL_PRESCALE_BYPASS_MASK ) ;
WREG32 ( mmINPUT_GAMMA_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < INPUT_GAMMA_CONTROL__GRPH_INPUT_GAMMA_MODE__SHIFT ) |
( 0 < < INPUT_GAMMA_CONTROL__OVL_INPUT_GAMMA_MODE__SHIFT ) ) ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_CONTROL + amdgpu_crtc - > crtc_offset , 0 ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_BLACK_OFFSET_BLUE + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmDC_LUT_BLACK_OFFSET_GREEN + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmDC_LUT_BLACK_OFFSET_RED + amdgpu_crtc - > crtc_offset , 0 ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_WHITE_OFFSET_BLUE + amdgpu_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( mmDC_LUT_WHITE_OFFSET_GREEN + amdgpu_crtc - > crtc_offset , 0xffff ) ;
WREG32 ( mmDC_LUT_WHITE_OFFSET_RED + amdgpu_crtc - > crtc_offset , 0xffff ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_RW_MODE + amdgpu_crtc - > crtc_offset , 0 ) ;
WREG32 ( mmDC_LUT_WRITE_EN_MASK + amdgpu_crtc - > crtc_offset , 0x00000007 ) ;
2016-01-19 14:03:24 +08:00
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_RW_INDEX + amdgpu_crtc - > crtc_offset , 0 ) ;
2017-07-13 18:25:28 +02:00
r = crtc - > gamma_store ;
g = r + crtc - > gamma_size ;
b = g + crtc - > gamma_size ;
2016-01-19 14:03:24 +08:00
for ( i = 0 ; i < 256 ; i + + ) {
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_LUT_30_COLOR + amdgpu_crtc - > crtc_offset ,
2017-07-13 18:25:28 +02:00
( ( * r + + & 0xffc0 ) < < 14 ) |
( ( * g + + & 0xffc0 ) < < 4 ) |
( * b + + > > 6 ) ) ;
2016-01-19 14:03:24 +08:00
}
2016-11-14 13:55:17 -05:00
WREG32 ( mmDEGAMMA_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < DEGAMMA_CONTROL__GRPH_DEGAMMA_MODE__SHIFT ) |
( 0 < < DEGAMMA_CONTROL__OVL_DEGAMMA_MODE__SHIFT ) |
ICON_DEGAMMA_MODE ( 0 ) |
( 0 < < DEGAMMA_CONTROL__CURSOR_DEGAMMA_MODE__SHIFT ) ) ) ;
WREG32 ( mmGAMUT_REMAP_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < GAMUT_REMAP_CONTROL__GRPH_GAMUT_REMAP_MODE__SHIFT ) |
( 0 < < GAMUT_REMAP_CONTROL__OVL_GAMUT_REMAP_MODE__SHIFT ) ) ) ;
WREG32 ( mmREGAMMA_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < REGAMMA_CONTROL__GRPH_REGAMMA_MODE__SHIFT ) |
( 0 < < REGAMMA_CONTROL__OVL_REGAMMA_MODE__SHIFT ) ) ) ;
WREG32 ( mmOUTPUT_CSC_CONTROL + amdgpu_crtc - > crtc_offset ,
( ( 0 < < OUTPUT_CSC_CONTROL__OUTPUT_CSC_GRPH_MODE__SHIFT ) |
( 0 < < OUTPUT_CSC_CONTROL__OUTPUT_CSC_OVL_MODE__SHIFT ) ) ) ;
2016-01-19 14:03:24 +08:00
/* XXX match this to the depth of the crtc fmt block, move to modeset? */
WREG32 ( 0x1a50 + amdgpu_crtc - > crtc_offset , 0 ) ;
}
static int dce_v6_0_pick_dig_encoder ( struct drm_encoder * encoder )
{
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
switch ( amdgpu_encoder - > encoder_id ) {
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY :
2016-09-22 12:29:40 -04:00
return dig - > linkb ? 1 : 0 ;
2016-01-19 14:03:24 +08:00
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 :
2016-09-22 12:29:40 -04:00
return dig - > linkb ? 3 : 2 ;
2016-01-19 14:03:24 +08:00
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 :
2016-09-22 12:29:40 -04:00
return dig - > linkb ? 5 : 4 ;
2016-01-19 14:03:24 +08:00
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 :
return 6 ;
default :
DRM_ERROR ( " invalid encoder_id: 0x%x \n " , amdgpu_encoder - > encoder_id ) ;
return 0 ;
}
}
/**
* dce_v6_0_pick_pll - Allocate a PPLL for use by the crtc .
*
* @ crtc : drm crtc
*
* Returns the PPLL ( Pixel PLL ) to be used by the crtc . For DP monitors
* a single PPLL can be used for all DP crtcs / encoders . For non - DP
* monitors a dedicated PPLL must be used . If a particular board has
* an external DP PLL , return ATOM_PPLL_INVALID to skip PLL programming
* as there is no need to program the PLL itself . If we are not able to
* allocate a PLL , return ATOM_PPLL_INVALID to skip PLL programming to
* avoid messing up an existing monitor .
*
*
*/
static u32 dce_v6_0_pick_pll ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
u32 pll_in_use ;
int pll ;
if ( ENCODER_MODE_IS_DP ( amdgpu_atombios_encoder_get_encoder_mode ( amdgpu_crtc - > encoder ) ) ) {
if ( adev - > clock . dp_extclk )
/* skip PPLL programming if using ext clock */
return ATOM_PPLL_INVALID ;
else
return ATOM_PPLL0 ;
} else {
/* use the same PPLL for all monitors with the same clock */
pll = amdgpu_pll_get_shared_nondp_ppll ( crtc ) ;
if ( pll ! = ATOM_PPLL_INVALID )
return pll ;
}
/* PPLL1, and PPLL2 */
pll_in_use = amdgpu_pll_get_use_mask ( crtc ) ;
if ( ! ( pll_in_use & ( 1 < < ATOM_PPLL2 ) ) )
return ATOM_PPLL2 ;
if ( ! ( pll_in_use & ( 1 < < ATOM_PPLL1 ) ) )
return ATOM_PPLL1 ;
DRM_ERROR ( " unable to allocate a PPLL \n " ) ;
return ATOM_PPLL_INVALID ;
}
static void dce_v6_0_lock_cursor ( struct drm_crtc * crtc , bool lock )
{
struct amdgpu_device * adev = crtc - > dev - > dev_private ;
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
uint32_t cur_lock ;
2016-11-14 13:55:17 -05:00
cur_lock = RREG32 ( mmCUR_UPDATE + amdgpu_crtc - > crtc_offset ) ;
2016-01-19 14:03:24 +08:00
if ( lock )
2016-11-14 13:55:17 -05:00
cur_lock | = CUR_UPDATE__CURSOR_UPDATE_LOCK_MASK ;
2016-01-19 14:03:24 +08:00
else
2016-11-14 13:55:17 -05:00
cur_lock & = ~ CUR_UPDATE__CURSOR_UPDATE_LOCK_MASK ;
WREG32 ( mmCUR_UPDATE + amdgpu_crtc - > crtc_offset , cur_lock ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_hide_cursor ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct amdgpu_device * adev = crtc - > dev - > dev_private ;
2016-11-14 13:55:17 -05:00
WREG32_IDX ( mmCUR_CONTROL + amdgpu_crtc - > crtc_offset ,
( CURSOR_24_8_PRE_MULT < < CUR_CONTROL__CURSOR_MODE__SHIFT ) |
( CURSOR_URGENT_1_2 < < CUR_CONTROL__CURSOR_URGENT_CONTROL__SHIFT ) ) ;
2016-01-19 14:03:24 +08:00
}
static void dce_v6_0_show_cursor ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct amdgpu_device * adev = crtc - > dev - > dev_private ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmCUR_SURFACE_ADDRESS_HIGH + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
upper_32_bits ( amdgpu_crtc - > cursor_addr ) ) ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmCUR_SURFACE_ADDRESS + amdgpu_crtc - > crtc_offset ,
2016-01-19 14:03:24 +08:00
lower_32_bits ( amdgpu_crtc - > cursor_addr ) ) ;
2016-11-14 13:55:17 -05:00
WREG32_IDX ( mmCUR_CONTROL + amdgpu_crtc - > crtc_offset ,
CUR_CONTROL__CURSOR_EN_MASK |
( CURSOR_24_8_PRE_MULT < < CUR_CONTROL__CURSOR_MODE__SHIFT ) |
( CURSOR_URGENT_1_2 < < CUR_CONTROL__CURSOR_URGENT_CONTROL__SHIFT ) ) ;
2016-01-19 14:03:24 +08:00
}
static int dce_v6_0_cursor_move_locked ( struct drm_crtc * crtc ,
int x , int y )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct amdgpu_device * adev = crtc - > dev - > dev_private ;
int xorigin = 0 , yorigin = 0 ;
2017-01-11 18:27:25 +09:00
int w = amdgpu_crtc - > cursor_width ;
2016-10-27 17:11:43 +09:00
amdgpu_crtc - > cursor_x = x ;
amdgpu_crtc - > cursor_y = y ;
2016-01-19 14:03:24 +08:00
/* avivo cursor are offset into the total surface */
x + = crtc - > x ;
y + = crtc - > y ;
DRM_DEBUG ( " x %d y %d c->x %d c->y %d \n " , x , y , crtc - > x , crtc - > y ) ;
if ( x < 0 ) {
xorigin = min ( - x , amdgpu_crtc - > max_cursor_width - 1 ) ;
x = 0 ;
}
if ( y < 0 ) {
yorigin = min ( - y , amdgpu_crtc - > max_cursor_height - 1 ) ;
y = 0 ;
}
2016-11-14 13:55:17 -05:00
WREG32 ( mmCUR_POSITION + amdgpu_crtc - > crtc_offset , ( x < < 16 ) | y ) ;
WREG32 ( mmCUR_HOT_SPOT + amdgpu_crtc - > crtc_offset , ( xorigin < < 16 ) | yorigin ) ;
2017-01-11 18:27:25 +09:00
WREG32 ( mmCUR_SIZE + amdgpu_crtc - > crtc_offset ,
( ( w - 1 ) < < 16 ) | ( amdgpu_crtc - > cursor_height - 1 ) ) ;
2016-01-19 14:03:24 +08:00
return 0 ;
}
static int dce_v6_0_crtc_cursor_move ( struct drm_crtc * crtc ,
int x , int y )
{
int ret ;
dce_v6_0_lock_cursor ( crtc , true ) ;
ret = dce_v6_0_cursor_move_locked ( crtc , x , y ) ;
dce_v6_0_lock_cursor ( crtc , false ) ;
return ret ;
}
static int dce_v6_0_crtc_cursor_set2 ( struct drm_crtc * crtc ,
struct drm_file * file_priv ,
uint32_t handle ,
uint32_t width ,
uint32_t height ,
int32_t hot_x ,
int32_t hot_y )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_gem_object * obj ;
struct amdgpu_bo * aobj ;
int ret ;
if ( ! handle ) {
/* turn off cursor */
dce_v6_0_hide_cursor ( crtc ) ;
obj = NULL ;
goto unpin ;
}
if ( ( width > amdgpu_crtc - > max_cursor_width ) | |
( height > amdgpu_crtc - > max_cursor_height ) ) {
DRM_ERROR ( " bad cursor width or height %d x %d \n " , width , height ) ;
return - EINVAL ;
}
obj = drm_gem_object_lookup ( file_priv , handle ) ;
if ( ! obj ) {
DRM_ERROR ( " Cannot find cursor object %x for crtc %d \n " , handle , amdgpu_crtc - > crtc_id ) ;
return - ENOENT ;
}
aobj = gem_to_amdgpu_bo ( obj ) ;
ret = amdgpu_bo_reserve ( aobj , false ) ;
if ( ret ! = 0 ) {
2017-08-03 14:58:16 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2016-01-19 14:03:24 +08:00
return ret ;
}
2018-06-25 12:51:14 +08:00
ret = amdgpu_bo_pin ( aobj , AMDGPU_GEM_DOMAIN_VRAM ) ;
2016-01-19 14:03:24 +08:00
amdgpu_bo_unreserve ( aobj ) ;
if ( ret ) {
DRM_ERROR ( " Failed to pin new cursor BO (%d) \n " , ret ) ;
2017-08-03 14:58:16 +03:00
drm_gem_object_put_unlocked ( obj ) ;
2016-01-19 14:03:24 +08:00
return ret ;
}
2018-06-25 12:51:14 +08:00
amdgpu_crtc - > cursor_addr = amdgpu_bo_gpu_offset ( aobj ) ;
2016-01-19 14:03:24 +08:00
dce_v6_0_lock_cursor ( crtc , true ) ;
2017-01-11 18:27:25 +09:00
if ( width ! = amdgpu_crtc - > cursor_width | |
height ! = amdgpu_crtc - > cursor_height | |
hot_x ! = amdgpu_crtc - > cursor_hot_x | |
2016-01-19 14:03:24 +08:00
hot_y ! = amdgpu_crtc - > cursor_hot_y ) {
int x , y ;
x = amdgpu_crtc - > cursor_x + amdgpu_crtc - > cursor_hot_x - hot_x ;
y = amdgpu_crtc - > cursor_y + amdgpu_crtc - > cursor_hot_y - hot_y ;
dce_v6_0_cursor_move_locked ( crtc , x , y ) ;
2016-10-27 17:44:07 +09:00
amdgpu_crtc - > cursor_width = width ;
amdgpu_crtc - > cursor_height = height ;
2017-01-11 18:27:25 +09:00
amdgpu_crtc - > cursor_hot_x = hot_x ;
amdgpu_crtc - > cursor_hot_y = hot_y ;
2016-10-27 17:44:07 +09:00
}
2016-01-19 14:03:24 +08:00
dce_v6_0_show_cursor ( crtc ) ;
dce_v6_0_lock_cursor ( crtc , false ) ;
unpin :
if ( amdgpu_crtc - > cursor_bo ) {
struct amdgpu_bo * aobj = gem_to_amdgpu_bo ( amdgpu_crtc - > cursor_bo ) ;
2017-04-28 17:28:14 +09:00
ret = amdgpu_bo_reserve ( aobj , true ) ;
2016-01-19 14:03:24 +08:00
if ( likely ( ret = = 0 ) ) {
amdgpu_bo_unpin ( aobj ) ;
amdgpu_bo_unreserve ( aobj ) ;
}
2017-08-03 14:58:16 +03:00
drm_gem_object_put_unlocked ( amdgpu_crtc - > cursor_bo ) ;
2016-01-19 14:03:24 +08:00
}
amdgpu_crtc - > cursor_bo = obj ;
return 0 ;
}
static void dce_v6_0_cursor_reset ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
if ( amdgpu_crtc - > cursor_bo ) {
dce_v6_0_lock_cursor ( crtc , true ) ;
dce_v6_0_cursor_move_locked ( crtc , amdgpu_crtc - > cursor_x ,
amdgpu_crtc - > cursor_y ) ;
dce_v6_0_show_cursor ( crtc ) ;
dce_v6_0_lock_cursor ( crtc , false ) ;
}
}
static int dce_v6_0_crtc_gamma_set ( struct drm_crtc * crtc , u16 * red , u16 * green ,
2017-04-03 10:33:01 +02:00
u16 * blue , uint32_t size ,
struct drm_modeset_acquire_ctx * ctx )
2016-01-19 14:03:24 +08:00
{
dce_v6_0_crtc_load_lut ( crtc ) ;
return 0 ;
}
static void dce_v6_0_crtc_destroy ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
drm_crtc_cleanup ( crtc ) ;
kfree ( amdgpu_crtc ) ;
}
static const struct drm_crtc_funcs dce_v6_0_crtc_funcs = {
. cursor_set2 = dce_v6_0_crtc_cursor_set2 ,
. cursor_move = dce_v6_0_crtc_cursor_move ,
. gamma_set = dce_v6_0_crtc_gamma_set ,
2018-01-19 11:53:31 -05:00
. set_config = amdgpu_display_crtc_set_config ,
2016-01-19 14:03:24 +08:00
. destroy = dce_v6_0_crtc_destroy ,
2018-01-19 11:22:59 -05:00
. page_flip_target = amdgpu_display_crtc_page_flip_target ,
2016-01-19 14:03:24 +08:00
} ;
static void dce_v6_0_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
unsigned type ;
switch ( mode ) {
case DRM_MODE_DPMS_ON :
amdgpu_crtc - > enabled = true ;
amdgpu_atombios_crtc_enable ( crtc , ATOM_ENABLE ) ;
amdgpu_atombios_crtc_blank ( crtc , ATOM_DISABLE ) ;
/* Make sure VBLANK and PFLIP interrupts are still enabled */
2018-01-19 16:06:41 -05:00
type = amdgpu_display_crtc_idx_to_irq_type ( adev ,
amdgpu_crtc - > crtc_id ) ;
2016-01-19 14:03:24 +08:00
amdgpu_irq_update ( adev , & adev - > crtc_irq , type ) ;
amdgpu_irq_update ( adev , & adev - > pageflip_irq , type ) ;
2016-11-14 10:02:50 +01:00
drm_crtc_vblank_on ( crtc ) ;
2016-01-19 14:03:24 +08:00
dce_v6_0_crtc_load_lut ( crtc ) ;
break ;
case DRM_MODE_DPMS_STANDBY :
case DRM_MODE_DPMS_SUSPEND :
case DRM_MODE_DPMS_OFF :
2016-11-14 10:02:50 +01:00
drm_crtc_vblank_off ( crtc ) ;
2016-01-19 14:03:24 +08:00
if ( amdgpu_crtc - > enabled )
amdgpu_atombios_crtc_blank ( crtc , ATOM_ENABLE ) ;
amdgpu_atombios_crtc_enable ( crtc , ATOM_DISABLE ) ;
amdgpu_crtc - > enabled = false ;
break ;
}
/* adjust pm to dpms */
amdgpu_pm_compute_clocks ( adev ) ;
}
static void dce_v6_0_crtc_prepare ( struct drm_crtc * crtc )
{
/* disable crtc pair power gating before programming */
amdgpu_atombios_crtc_powergate ( crtc , ATOM_DISABLE ) ;
amdgpu_atombios_crtc_lock ( crtc , ATOM_ENABLE ) ;
dce_v6_0_crtc_dpms ( crtc , DRM_MODE_DPMS_OFF ) ;
}
static void dce_v6_0_crtc_commit ( struct drm_crtc * crtc )
{
dce_v6_0_crtc_dpms ( crtc , DRM_MODE_DPMS_ON ) ;
amdgpu_atombios_crtc_lock ( crtc , ATOM_DISABLE ) ;
}
static void dce_v6_0_crtc_disable ( struct drm_crtc * crtc )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
struct amdgpu_atom_ss ss ;
int i ;
dce_v6_0_crtc_dpms ( crtc , DRM_MODE_DPMS_OFF ) ;
if ( crtc - > primary - > fb ) {
int r ;
2016-09-15 15:06:50 +02:00
struct amdgpu_bo * abo ;
2016-01-19 14:03:24 +08:00
2018-03-30 15:11:38 +01:00
abo = gem_to_amdgpu_bo ( crtc - > primary - > fb - > obj [ 0 ] ) ;
2017-04-28 17:28:14 +09:00
r = amdgpu_bo_reserve ( abo , true ) ;
2016-01-19 14:03:24 +08:00
if ( unlikely ( r ) )
2016-09-15 15:06:50 +02:00
DRM_ERROR ( " failed to reserve abo before unpin \n " ) ;
2016-01-19 14:03:24 +08:00
else {
2016-09-15 15:06:50 +02:00
amdgpu_bo_unpin ( abo ) ;
amdgpu_bo_unreserve ( abo ) ;
2016-01-19 14:03:24 +08:00
}
}
/* disable the GRPH */
dce_v6_0_grph_enable ( crtc , false ) ;
amdgpu_atombios_crtc_powergate ( crtc , ATOM_ENABLE ) ;
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + ) {
if ( adev - > mode_info . crtcs [ i ] & &
adev - > mode_info . crtcs [ i ] - > enabled & &
i ! = amdgpu_crtc - > crtc_id & &
amdgpu_crtc - > pll_id = = adev - > mode_info . crtcs [ i ] - > pll_id ) {
/* one other crtc is using this pll don't turn
* off the pll
*/
goto done ;
}
}
switch ( amdgpu_crtc - > pll_id ) {
case ATOM_PPLL1 :
case ATOM_PPLL2 :
/* disable the ppll */
amdgpu_atombios_crtc_program_pll ( crtc , amdgpu_crtc - > crtc_id , amdgpu_crtc - > pll_id ,
0 , 0 , ATOM_DISABLE , 0 , 0 , 0 , 0 , 0 , false , & ss ) ;
break ;
default :
break ;
}
done :
amdgpu_crtc - > pll_id = ATOM_PPLL_INVALID ;
amdgpu_crtc - > adjusted_clock = 0 ;
amdgpu_crtc - > encoder = NULL ;
amdgpu_crtc - > connector = NULL ;
}
static int dce_v6_0_crtc_mode_set ( struct drm_crtc * crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode ,
int x , int y , struct drm_framebuffer * old_fb )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
if ( ! amdgpu_crtc - > adjusted_clock )
return - EINVAL ;
amdgpu_atombios_crtc_set_pll ( crtc , adjusted_mode ) ;
amdgpu_atombios_crtc_set_dtd_timing ( crtc , adjusted_mode ) ;
dce_v6_0_crtc_do_set_base ( crtc , old_fb , x , y , 0 ) ;
amdgpu_atombios_crtc_overscan_setup ( crtc , mode , adjusted_mode ) ;
amdgpu_atombios_crtc_scaler_setup ( crtc ) ;
dce_v6_0_cursor_reset ( crtc ) ;
/* update the hw version fpr dpm */
amdgpu_crtc - > hw_mode = * adjusted_mode ;
return 0 ;
}
static bool dce_v6_0_crtc_mode_fixup ( struct drm_crtc * crtc ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct amdgpu_crtc * amdgpu_crtc = to_amdgpu_crtc ( crtc ) ;
struct drm_device * dev = crtc - > dev ;
struct drm_encoder * encoder ;
/* assign the encoder to the amdgpu crtc to avoid repeated lookups later */
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
if ( encoder - > crtc = = crtc ) {
amdgpu_crtc - > encoder = encoder ;
amdgpu_crtc - > connector = amdgpu_get_connector_for_encoder ( encoder ) ;
break ;
}
}
if ( ( amdgpu_crtc - > encoder = = NULL ) | | ( amdgpu_crtc - > connector = = NULL ) ) {
amdgpu_crtc - > encoder = NULL ;
amdgpu_crtc - > connector = NULL ;
return false ;
}
2018-01-19 15:36:43 -05:00
if ( ! amdgpu_display_crtc_scaling_mode_fixup ( crtc , mode , adjusted_mode ) )
2016-01-19 14:03:24 +08:00
return false ;
if ( amdgpu_atombios_crtc_prepare_pll ( crtc , adjusted_mode ) )
return false ;
/* pick pll */
amdgpu_crtc - > pll_id = dce_v6_0_pick_pll ( crtc ) ;
/* if we can't get a PPLL for a non-DP encoder, fail */
if ( ( amdgpu_crtc - > pll_id = = ATOM_PPLL_INVALID ) & &
! ENCODER_MODE_IS_DP ( amdgpu_atombios_encoder_get_encoder_mode ( amdgpu_crtc - > encoder ) ) )
return false ;
return true ;
}
static int dce_v6_0_crtc_set_base ( struct drm_crtc * crtc , int x , int y ,
struct drm_framebuffer * old_fb )
{
return dce_v6_0_crtc_do_set_base ( crtc , old_fb , x , y , 0 ) ;
}
static int dce_v6_0_crtc_set_base_atomic ( struct drm_crtc * crtc ,
struct drm_framebuffer * fb ,
int x , int y , enum mode_set_atomic state )
{
return dce_v6_0_crtc_do_set_base ( crtc , fb , x , y , 1 ) ;
}
static const struct drm_crtc_helper_funcs dce_v6_0_crtc_helper_funcs = {
. dpms = dce_v6_0_crtc_dpms ,
. mode_fixup = dce_v6_0_crtc_mode_fixup ,
. mode_set = dce_v6_0_crtc_mode_set ,
. mode_set_base = dce_v6_0_crtc_set_base ,
. mode_set_base_atomic = dce_v6_0_crtc_set_base_atomic ,
. prepare = dce_v6_0_crtc_prepare ,
. commit = dce_v6_0_crtc_commit ,
. disable = dce_v6_0_crtc_disable ,
} ;
static int dce_v6_0_crtc_init ( struct amdgpu_device * adev , int index )
{
struct amdgpu_crtc * amdgpu_crtc ;
amdgpu_crtc = kzalloc ( sizeof ( struct amdgpu_crtc ) +
( AMDGPUFB_CONN_LIMIT * sizeof ( struct drm_connector * ) ) , GFP_KERNEL ) ;
if ( amdgpu_crtc = = NULL )
return - ENOMEM ;
drm_crtc_init ( adev - > ddev , & amdgpu_crtc - > base , & dce_v6_0_crtc_funcs ) ;
drm_mode_crtc_set_gamma_size ( & amdgpu_crtc - > base , 256 ) ;
amdgpu_crtc - > crtc_id = index ;
adev - > mode_info . crtcs [ index ] = amdgpu_crtc ;
amdgpu_crtc - > max_cursor_width = CURSOR_WIDTH ;
amdgpu_crtc - > max_cursor_height = CURSOR_HEIGHT ;
adev - > ddev - > mode_config . cursor_width = amdgpu_crtc - > max_cursor_width ;
adev - > ddev - > mode_config . cursor_height = amdgpu_crtc - > max_cursor_height ;
amdgpu_crtc - > crtc_offset = crtc_offsets [ amdgpu_crtc - > crtc_id ] ;
amdgpu_crtc - > pll_id = ATOM_PPLL_INVALID ;
amdgpu_crtc - > adjusted_clock = 0 ;
amdgpu_crtc - > encoder = NULL ;
amdgpu_crtc - > connector = NULL ;
drm_crtc_helper_add ( & amdgpu_crtc - > base , & dce_v6_0_crtc_helper_funcs ) ;
return 0 ;
}
static int dce_v6_0_early_init ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
adev - > audio_endpt_rreg = & dce_v6_0_audio_endpt_rreg ;
adev - > audio_endpt_wreg = & dce_v6_0_audio_endpt_wreg ;
dce_v6_0_set_display_funcs ( adev ) ;
2016-09-29 23:30:21 -04:00
adev - > mode_info . num_crtc = dce_v6_0_get_num_crtc ( adev ) ;
2016-01-19 14:03:24 +08:00
switch ( adev - > asic_type ) {
case CHIP_TAHITI :
case CHIP_PITCAIRN :
case CHIP_VERDE :
adev - > mode_info . num_hpd = 6 ;
adev - > mode_info . num_dig = 6 ;
break ;
case CHIP_OLAND :
adev - > mode_info . num_hpd = 2 ;
adev - > mode_info . num_dig = 2 ;
break ;
default :
return - EINVAL ;
}
2017-10-24 11:03:21 +02:00
dce_v6_0_set_irq_funcs ( adev ) ;
2016-01-19 14:03:24 +08:00
return 0 ;
}
static int dce_v6_0_sw_init ( void * handle )
{
int r , i ;
bool ret ;
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + ) {
2018-09-17 15:29:28 +02:00
r = amdgpu_irq_add_id ( adev , AMDGPU_IRQ_CLIENTID_LEGACY , i + 1 , & adev - > crtc_irq ) ;
2016-01-19 14:03:24 +08:00
if ( r )
return r ;
}
for ( i = 8 ; i < 20 ; i + = 2 ) {
2018-09-17 15:29:28 +02:00
r = amdgpu_irq_add_id ( adev , AMDGPU_IRQ_CLIENTID_LEGACY , i , & adev - > pageflip_irq ) ;
2016-01-19 14:03:24 +08:00
if ( r )
return r ;
}
/* HPD hotplug */
2018-09-17 15:29:28 +02:00
r = amdgpu_irq_add_id ( adev , AMDGPU_IRQ_CLIENTID_LEGACY , 42 , & adev - > hpd_irq ) ;
2016-01-19 14:03:24 +08:00
if ( r )
return r ;
adev - > mode_info . mode_config_initialized = true ;
adev - > ddev - > mode_config . funcs = & amdgpu_mode_funcs ;
adev - > ddev - > mode_config . async_page_flip = true ;
adev - > ddev - > mode_config . max_width = 16384 ;
adev - > ddev - > mode_config . max_height = 16384 ;
adev - > ddev - > mode_config . preferred_depth = 24 ;
adev - > ddev - > mode_config . prefer_shadow = 1 ;
2018-01-12 14:52:22 +01:00
adev - > ddev - > mode_config . fb_base = adev - > gmc . aper_base ;
2016-01-19 14:03:24 +08:00
2018-01-19 12:47:40 -05:00
r = amdgpu_display_modeset_create_props ( adev ) ;
2016-01-19 14:03:24 +08:00
if ( r )
return r ;
adev - > ddev - > mode_config . max_width = 16384 ;
adev - > ddev - > mode_config . max_height = 16384 ;
/* allocate crtcs */
for ( i = 0 ; i < adev - > mode_info . num_crtc ; i + + ) {
r = dce_v6_0_crtc_init ( adev , i ) ;
if ( r )
return r ;
}
ret = amdgpu_atombios_get_connector_info_from_object_table ( adev ) ;
if ( ret )
2018-01-19 12:02:45 -05:00
amdgpu_display_print_display_setup ( adev - > ddev ) ;
2016-01-19 14:03:24 +08:00
else
return - EINVAL ;
/* setup afmt */
2016-09-22 12:20:58 -04:00
r = dce_v6_0_afmt_init ( adev ) ;
if ( r )
return r ;
2016-01-19 14:03:24 +08:00
r = dce_v6_0_audio_init ( adev ) ;
if ( r )
return r ;
drm_kms_helper_poll_init ( adev - > ddev ) ;
return r ;
}
static int dce_v6_0_sw_fini ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
kfree ( adev - > mode_info . bios_hardcoded_edid ) ;
drm_kms_helper_poll_fini ( adev - > ddev ) ;
dce_v6_0_audio_fini ( adev ) ;
dce_v6_0_afmt_fini ( adev ) ;
drm_mode_config_cleanup ( adev - > ddev ) ;
adev - > mode_info . mode_config_initialized = false ;
return 0 ;
}
static int dce_v6_0_hw_init ( void * handle )
{
int i ;
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
2017-06-19 17:00:38 -04:00
/* disable vga render */
dce_v6_0_set_vga_render_state ( adev , false ) ;
2016-01-19 14:03:24 +08:00
/* init dig PHYs, disp eng pll */
amdgpu_atombios_encoder_init_dig ( adev ) ;
amdgpu_atombios_crtc_set_disp_eng_pll ( adev , adev - > clock . default_dispclk ) ;
/* initialize hpd */
dce_v6_0_hpd_init ( adev ) ;
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + ) {
dce_v6_0_audio_enable ( adev , & adev - > mode_info . audio . pin [ i ] , false ) ;
}
dce_v6_0_pageflip_interrupt_init ( adev ) ;
return 0 ;
}
static int dce_v6_0_hw_fini ( void * handle )
{
int i ;
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
dce_v6_0_hpd_fini ( adev ) ;
for ( i = 0 ; i < adev - > mode_info . audio . num_pins ; i + + ) {
dce_v6_0_audio_enable ( adev , & adev - > mode_info . audio . pin [ i ] , false ) ;
}
dce_v6_0_pageflip_interrupt_fini ( adev ) ;
return 0 ;
}
static int dce_v6_0_suspend ( void * handle )
{
2018-03-08 09:56:01 -05:00
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
adev - > mode_info . bl_level =
amdgpu_atombios_encoder_get_backlight_level_from_reg ( adev ) ;
2016-01-19 14:03:24 +08:00
return dce_v6_0_hw_fini ( handle ) ;
}
static int dce_v6_0_resume ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
int ret ;
2018-03-08 09:56:01 -05:00
amdgpu_atombios_encoder_set_backlight_level_to_reg ( adev ,
adev - > mode_info . bl_level ) ;
2016-01-19 14:03:24 +08:00
ret = dce_v6_0_hw_init ( handle ) ;
/* turn on the BL */
if ( adev - > mode_info . bl_encoder ) {
u8 bl_level = amdgpu_display_backlight_get_level ( adev ,
adev - > mode_info . bl_encoder ) ;
amdgpu_display_backlight_set_level ( adev , adev - > mode_info . bl_encoder ,
bl_level ) ;
}
return ret ;
}
static bool dce_v6_0_is_idle ( void * handle )
{
return true ;
}
static int dce_v6_0_wait_for_idle ( void * handle )
{
return 0 ;
}
static int dce_v6_0_soft_reset ( void * handle )
{
DRM_INFO ( " xxxx: dce_v6_0_soft_reset --- no impl!! \n " ) ;
return 0 ;
}
static void dce_v6_0_set_crtc_vblank_interrupt_state ( struct amdgpu_device * adev ,
int crtc ,
enum amdgpu_interrupt_state state )
{
u32 reg_block , interrupt_mask ;
if ( crtc > = adev - > mode_info . num_crtc ) {
DRM_DEBUG ( " invalid crtc %d \n " , crtc ) ;
return ;
}
switch ( crtc ) {
case 0 :
reg_block = SI_CRTC0_REGISTER_OFFSET ;
break ;
case 1 :
reg_block = SI_CRTC1_REGISTER_OFFSET ;
break ;
case 2 :
reg_block = SI_CRTC2_REGISTER_OFFSET ;
break ;
case 3 :
reg_block = SI_CRTC3_REGISTER_OFFSET ;
break ;
case 4 :
reg_block = SI_CRTC4_REGISTER_OFFSET ;
break ;
case 5 :
reg_block = SI_CRTC5_REGISTER_OFFSET ;
break ;
default :
DRM_DEBUG ( " invalid crtc %d \n " , crtc ) ;
return ;
}
switch ( state ) {
case AMDGPU_IRQ_STATE_DISABLE :
2016-11-14 13:55:17 -05:00
interrupt_mask = RREG32 ( mmINT_MASK + reg_block ) ;
2016-01-19 14:03:24 +08:00
interrupt_mask & = ~ VBLANK_INT_MASK ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmINT_MASK + reg_block , interrupt_mask ) ;
2016-01-19 14:03:24 +08:00
break ;
case AMDGPU_IRQ_STATE_ENABLE :
2016-11-14 13:55:17 -05:00
interrupt_mask = RREG32 ( mmINT_MASK + reg_block ) ;
2016-01-19 14:03:24 +08:00
interrupt_mask | = VBLANK_INT_MASK ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmINT_MASK + reg_block , interrupt_mask ) ;
2016-01-19 14:03:24 +08:00
break ;
default :
break ;
}
}
static void dce_v6_0_set_crtc_vline_interrupt_state ( struct amdgpu_device * adev ,
int crtc ,
enum amdgpu_interrupt_state state )
{
}
static int dce_v6_0_set_hpd_interrupt_state ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * src ,
unsigned type ,
enum amdgpu_interrupt_state state )
{
2016-09-28 14:15:24 -04:00
u32 dc_hpd_int_cntl ;
2016-01-19 14:03:24 +08:00
2016-09-28 14:15:24 -04:00
if ( type > = adev - > mode_info . num_hpd ) {
2016-01-19 14:03:24 +08:00
DRM_DEBUG ( " invalid hdp %d \n " , type ) ;
return 0 ;
}
switch ( state ) {
case AMDGPU_IRQ_STATE_DISABLE :
2016-11-14 13:55:17 -05:00
dc_hpd_int_cntl = RREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ type ] ) ;
2016-09-28 14:23:49 -04:00
dc_hpd_int_cntl & = ~ DC_HPDx_INT_EN ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ type ] , dc_hpd_int_cntl ) ;
2016-01-19 14:03:24 +08:00
break ;
case AMDGPU_IRQ_STATE_ENABLE :
2016-11-14 13:55:17 -05:00
dc_hpd_int_cntl = RREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ type ] ) ;
2016-09-28 14:23:49 -04:00
dc_hpd_int_cntl | = DC_HPDx_INT_EN ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ type ] , dc_hpd_int_cntl ) ;
2016-01-19 14:03:24 +08:00
break ;
default :
break ;
}
return 0 ;
}
static int dce_v6_0_set_crtc_interrupt_state ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * src ,
unsigned type ,
enum amdgpu_interrupt_state state )
{
switch ( type ) {
case AMDGPU_CRTC_IRQ_VBLANK1 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 0 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VBLANK2 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 1 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VBLANK3 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 2 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VBLANK4 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 3 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VBLANK5 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 4 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VBLANK6 :
dce_v6_0_set_crtc_vblank_interrupt_state ( adev , 5 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE1 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 0 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE2 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 1 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE3 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 2 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE4 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 3 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE5 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 4 , state ) ;
break ;
case AMDGPU_CRTC_IRQ_VLINE6 :
dce_v6_0_set_crtc_vline_interrupt_state ( adev , 5 , state ) ;
break ;
default :
break ;
}
return 0 ;
}
static int dce_v6_0_crtc_irq ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * source ,
struct amdgpu_iv_entry * entry )
{
unsigned crtc = entry - > src_id - 1 ;
uint32_t disp_int = RREG32 ( interrupt_status_offsets [ crtc ] . reg ) ;
2018-01-19 16:06:41 -05:00
unsigned int irq_type = amdgpu_display_crtc_idx_to_irq_type ( adev ,
crtc ) ;
2016-01-19 14:03:24 +08:00
2016-11-29 18:02:12 -05:00
switch ( entry - > src_data [ 0 ] ) {
2016-01-19 14:03:24 +08:00
case 0 : /* vblank */
if ( disp_int & interrupt_status_offsets [ crtc ] . vblank )
2016-11-14 13:55:17 -05:00
WREG32 ( mmVBLANK_STATUS + crtc_offsets [ crtc ] , VBLANK_ACK ) ;
2016-01-19 14:03:24 +08:00
else
DRM_DEBUG ( " IH: IH event w/o asserted irq bit? \n " ) ;
if ( amdgpu_irq_enabled ( adev , source , irq_type ) ) {
drm_handle_vblank ( adev - > ddev , crtc ) ;
}
DRM_DEBUG ( " IH: D%d vblank \n " , crtc + 1 ) ;
break ;
case 1 : /* vline */
if ( disp_int & interrupt_status_offsets [ crtc ] . vline )
2016-11-14 13:55:17 -05:00
WREG32 ( mmVLINE_STATUS + crtc_offsets [ crtc ] , VLINE_ACK ) ;
2016-01-19 14:03:24 +08:00
else
DRM_DEBUG ( " IH: IH event w/o asserted irq bit? \n " ) ;
DRM_DEBUG ( " IH: D%d vline \n " , crtc + 1 ) ;
break ;
default :
2016-11-29 18:02:12 -05:00
DRM_DEBUG ( " Unhandled interrupt: %d %d \n " , entry - > src_id , entry - > src_data [ 0 ] ) ;
2016-01-19 14:03:24 +08:00
break ;
}
return 0 ;
}
static int dce_v6_0_set_pageflip_interrupt_state ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * src ,
unsigned type ,
enum amdgpu_interrupt_state state )
{
u32 reg ;
if ( type > = adev - > mode_info . num_crtc ) {
DRM_ERROR ( " invalid pageflip crtc %d \n " , type ) ;
return - EINVAL ;
}
2016-11-14 13:55:17 -05:00
reg = RREG32 ( mmGRPH_INTERRUPT_CONTROL + crtc_offsets [ type ] ) ;
2016-01-19 14:03:24 +08:00
if ( state = = AMDGPU_IRQ_STATE_DISABLE )
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_INTERRUPT_CONTROL + crtc_offsets [ type ] ,
2016-01-19 14:03:24 +08:00
reg & ~ GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK ) ;
else
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_INTERRUPT_CONTROL + crtc_offsets [ type ] ,
2016-01-19 14:03:24 +08:00
reg | GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK ) ;
return 0 ;
}
static int dce_v6_0_pageflip_irq ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * source ,
struct amdgpu_iv_entry * entry )
{
2019-02-12 14:05:08 +00:00
unsigned long flags ;
2016-01-19 14:03:24 +08:00
unsigned crtc_id ;
struct amdgpu_crtc * amdgpu_crtc ;
struct amdgpu_flip_work * works ;
crtc_id = ( entry - > src_id - 8 ) > > 1 ;
amdgpu_crtc = adev - > mode_info . crtcs [ crtc_id ] ;
if ( crtc_id > = adev - > mode_info . num_crtc ) {
DRM_ERROR ( " invalid pageflip crtc %d \n " , crtc_id ) ;
return - EINVAL ;
}
2016-11-14 13:55:17 -05:00
if ( RREG32 ( mmGRPH_INTERRUPT_STATUS + crtc_offsets [ crtc_id ] ) &
2016-01-19 14:03:24 +08:00
GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_OCCURRED_MASK )
2016-11-14 13:55:17 -05:00
WREG32 ( mmGRPH_INTERRUPT_STATUS + crtc_offsets [ crtc_id ] ,
2016-01-19 14:03:24 +08:00
GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK ) ;
/* IRQ could occur when in initial stage */
if ( amdgpu_crtc = = NULL )
return 0 ;
spin_lock_irqsave ( & adev - > ddev - > event_lock , flags ) ;
works = amdgpu_crtc - > pflip_works ;
if ( amdgpu_crtc - > pflip_status ! = AMDGPU_FLIP_SUBMITTED ) {
DRM_DEBUG_DRIVER ( " amdgpu_crtc->pflip_status = %d != "
" AMDGPU_FLIP_SUBMITTED(%d) \n " ,
amdgpu_crtc - > pflip_status ,
AMDGPU_FLIP_SUBMITTED ) ;
spin_unlock_irqrestore ( & adev - > ddev - > event_lock , flags ) ;
return 0 ;
}
/* page flip completed. clean up */
amdgpu_crtc - > pflip_status = AMDGPU_FLIP_NONE ;
amdgpu_crtc - > pflip_works = NULL ;
/* wakeup usersapce */
if ( works - > event )
drm_crtc_send_vblank_event ( & amdgpu_crtc - > base , works - > event ) ;
spin_unlock_irqrestore ( & adev - > ddev - > event_lock , flags ) ;
drm_crtc_vblank_put ( & amdgpu_crtc - > base ) ;
schedule_work ( & works - > unpin_work ) ;
return 0 ;
}
static int dce_v6_0_hpd_irq ( struct amdgpu_device * adev ,
struct amdgpu_irq_src * source ,
struct amdgpu_iv_entry * entry )
{
2016-09-28 14:15:24 -04:00
uint32_t disp_int , mask , tmp ;
2016-01-19 14:03:24 +08:00
unsigned hpd ;
2016-11-29 18:02:12 -05:00
if ( entry - > src_data [ 0 ] > = adev - > mode_info . num_hpd ) {
DRM_DEBUG ( " Unhandled interrupt: %d %d \n " , entry - > src_id , entry - > src_data [ 0 ] ) ;
2016-01-19 14:03:24 +08:00
return 0 ;
}
2016-11-29 18:02:12 -05:00
hpd = entry - > src_data [ 0 ] ;
2016-01-19 14:03:24 +08:00
disp_int = RREG32 ( interrupt_status_offsets [ hpd ] . reg ) ;
mask = interrupt_status_offsets [ hpd ] . hpd ;
if ( disp_int & mask ) {
2016-11-14 13:55:17 -05:00
tmp = RREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ hpd ] ) ;
2016-01-19 14:03:24 +08:00
tmp | = DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK ;
2016-11-14 13:55:17 -05:00
WREG32 ( mmDC_HPD1_INT_CONTROL + hpd_offsets [ hpd ] , tmp ) ;
2016-01-19 14:03:24 +08:00
schedule_work ( & adev - > hotplug_work ) ;
2018-02-23 12:29:04 +01:00
DRM_DEBUG ( " IH: HPD%d \n " , hpd + 1 ) ;
2016-01-19 14:03:24 +08:00
}
return 0 ;
}
static int dce_v6_0_set_clockgating_state ( void * handle ,
enum amd_clockgating_state state )
{
return 0 ;
}
static int dce_v6_0_set_powergating_state ( void * handle ,
enum amd_powergating_state state )
{
return 0 ;
}
2016-10-13 17:41:13 -04:00
static const struct amd_ip_funcs dce_v6_0_ip_funcs = {
2016-01-19 14:03:24 +08:00
. name = " dce_v6_0 " ,
. early_init = dce_v6_0_early_init ,
. late_init = NULL ,
. sw_init = dce_v6_0_sw_init ,
. sw_fini = dce_v6_0_sw_fini ,
. hw_init = dce_v6_0_hw_init ,
. hw_fini = dce_v6_0_hw_fini ,
. suspend = dce_v6_0_suspend ,
. resume = dce_v6_0_resume ,
. is_idle = dce_v6_0_is_idle ,
. wait_for_idle = dce_v6_0_wait_for_idle ,
. soft_reset = dce_v6_0_soft_reset ,
. set_clockgating_state = dce_v6_0_set_clockgating_state ,
. set_powergating_state = dce_v6_0_set_powergating_state ,
} ;
static void
dce_v6_0_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
2017-02-19 19:56:17 +08:00
int em = amdgpu_atombios_encoder_get_encoder_mode ( encoder ) ;
2016-01-19 14:03:24 +08:00
amdgpu_encoder - > pixel_clock = adjusted_mode - > clock ;
/* need to call this here rather than in prepare() since we need some crtc info */
amdgpu_atombios_encoder_dpms ( encoder , DRM_MODE_DPMS_OFF ) ;
/* set scaler clears this on some chips */
dce_v6_0_set_interleave ( encoder - > crtc , mode ) ;
2017-02-19 19:56:17 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI | | ENCODER_MODE_IS_DP ( em ) ) {
2016-01-19 14:03:24 +08:00
dce_v6_0_afmt_enable ( encoder , true ) ;
dce_v6_0_afmt_setmode ( encoder , adjusted_mode ) ;
}
}
static void dce_v6_0_encoder_prepare ( struct drm_encoder * encoder )
{
struct amdgpu_device * adev = encoder - > dev - > dev_private ;
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct drm_connector * connector = amdgpu_get_connector_for_encoder ( encoder ) ;
if ( ( amdgpu_encoder - > active_device &
( ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT ) ) | |
( amdgpu_encoder_get_dp_bridge_encoder_id ( encoder ) ! =
ENCODER_OBJECT_ID_NONE ) ) {
struct amdgpu_encoder_atom_dig * dig = amdgpu_encoder - > enc_priv ;
if ( dig ) {
dig - > dig_encoder = dce_v6_0_pick_dig_encoder ( encoder ) ;
if ( amdgpu_encoder - > active_device & ATOM_DEVICE_DFP_SUPPORT )
dig - > afmt = adev - > mode_info . afmt [ dig - > dig_encoder ] ;
}
}
amdgpu_atombios_scratch_regs_lock ( adev , true ) ;
if ( connector ) {
struct amdgpu_connector * amdgpu_connector = to_amdgpu_connector ( connector ) ;
/* select the clock/data port if it uses a router */
if ( amdgpu_connector - > router . cd_valid )
amdgpu_i2c_router_select_cd_port ( amdgpu_connector ) ;
/* turn eDP panel on for mode set */
if ( connector - > connector_type = = DRM_MODE_CONNECTOR_eDP )
amdgpu_atombios_encoder_set_edp_panel_power ( connector ,
ATOM_TRANSMITTER_ACTION_POWER_ON ) ;
}
/* this is needed for the pll/ss setup to work correctly in some cases */
amdgpu_atombios_encoder_set_crtc_source ( encoder ) ;
/* set up the FMT blocks */
dce_v6_0_program_fmt ( encoder ) ;
}
static void dce_v6_0_encoder_commit ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
struct amdgpu_device * adev = dev - > dev_private ;
/* need to call this here as we need the crtc set up */
amdgpu_atombios_encoder_dpms ( encoder , DRM_MODE_DPMS_ON ) ;
amdgpu_atombios_scratch_regs_lock ( adev , false ) ;
}
static void dce_v6_0_encoder_disable ( struct drm_encoder * encoder )
{
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
struct amdgpu_encoder_atom_dig * dig ;
2017-02-19 19:56:17 +08:00
int em = amdgpu_atombios_encoder_get_encoder_mode ( encoder ) ;
2016-01-19 14:03:24 +08:00
amdgpu_atombios_encoder_dpms ( encoder , DRM_MODE_DPMS_OFF ) ;
if ( amdgpu_atombios_encoder_is_digital ( encoder ) ) {
2017-02-19 19:56:17 +08:00
if ( em = = ATOM_ENCODER_MODE_HDMI | | ENCODER_MODE_IS_DP ( em ) )
2016-01-19 14:03:24 +08:00
dce_v6_0_afmt_enable ( encoder , false ) ;
dig = amdgpu_encoder - > enc_priv ;
dig - > dig_encoder = - 1 ;
}
amdgpu_encoder - > active_device = 0 ;
}
/* these are handled by the primary encoders */
static void dce_v6_0_ext_prepare ( struct drm_encoder * encoder )
{
}
static void dce_v6_0_ext_commit ( struct drm_encoder * encoder )
{
}
static void
dce_v6_0_ext_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
}
static void dce_v6_0_ext_disable ( struct drm_encoder * encoder )
{
}
static void
dce_v6_0_ext_dpms ( struct drm_encoder * encoder , int mode )
{
}
static bool dce_v6_0_ext_mode_fixup ( struct drm_encoder * encoder ,
const struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
return true ;
}
static const struct drm_encoder_helper_funcs dce_v6_0_ext_helper_funcs = {
. dpms = dce_v6_0_ext_dpms ,
. mode_fixup = dce_v6_0_ext_mode_fixup ,
. prepare = dce_v6_0_ext_prepare ,
. mode_set = dce_v6_0_ext_mode_set ,
. commit = dce_v6_0_ext_commit ,
. disable = dce_v6_0_ext_disable ,
/* no detect for TMDS/LVDS yet */
} ;
static const struct drm_encoder_helper_funcs dce_v6_0_dig_helper_funcs = {
. dpms = amdgpu_atombios_encoder_dpms ,
. mode_fixup = amdgpu_atombios_encoder_mode_fixup ,
. prepare = dce_v6_0_encoder_prepare ,
. mode_set = dce_v6_0_encoder_mode_set ,
. commit = dce_v6_0_encoder_commit ,
. disable = dce_v6_0_encoder_disable ,
. detect = amdgpu_atombios_encoder_dig_detect ,
} ;
static const struct drm_encoder_helper_funcs dce_v6_0_dac_helper_funcs = {
. dpms = amdgpu_atombios_encoder_dpms ,
. mode_fixup = amdgpu_atombios_encoder_mode_fixup ,
. prepare = dce_v6_0_encoder_prepare ,
. mode_set = dce_v6_0_encoder_mode_set ,
. commit = dce_v6_0_encoder_commit ,
. detect = amdgpu_atombios_encoder_dac_detect ,
} ;
static void dce_v6_0_encoder_destroy ( struct drm_encoder * encoder )
{
struct amdgpu_encoder * amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
if ( amdgpu_encoder - > devices & ( ATOM_DEVICE_LCD_SUPPORT ) )
amdgpu_atombios_encoder_fini_backlight ( amdgpu_encoder ) ;
kfree ( amdgpu_encoder - > enc_priv ) ;
drm_encoder_cleanup ( encoder ) ;
kfree ( amdgpu_encoder ) ;
}
static const struct drm_encoder_funcs dce_v6_0_encoder_funcs = {
. destroy = dce_v6_0_encoder_destroy ,
} ;
static void dce_v6_0_encoder_add ( struct amdgpu_device * adev ,
uint32_t encoder_enum ,
uint32_t supported_device ,
u16 caps )
{
struct drm_device * dev = adev - > ddev ;
struct drm_encoder * encoder ;
struct amdgpu_encoder * amdgpu_encoder ;
/* see if we already added it */
list_for_each_entry ( encoder , & dev - > mode_config . encoder_list , head ) {
amdgpu_encoder = to_amdgpu_encoder ( encoder ) ;
if ( amdgpu_encoder - > encoder_enum = = encoder_enum ) {
amdgpu_encoder - > devices | = supported_device ;
return ;
}
}
/* add a new one */
amdgpu_encoder = kzalloc ( sizeof ( struct amdgpu_encoder ) , GFP_KERNEL ) ;
if ( ! amdgpu_encoder )
return ;
encoder = & amdgpu_encoder - > base ;
switch ( adev - > mode_info . num_crtc ) {
case 1 :
encoder - > possible_crtcs = 0x1 ;
break ;
case 2 :
default :
encoder - > possible_crtcs = 0x3 ;
break ;
case 4 :
encoder - > possible_crtcs = 0xf ;
break ;
case 6 :
encoder - > possible_crtcs = 0x3f ;
break ;
}
amdgpu_encoder - > enc_priv = NULL ;
amdgpu_encoder - > encoder_enum = encoder_enum ;
amdgpu_encoder - > encoder_id = ( encoder_enum & OBJECT_ID_MASK ) > > OBJECT_ID_SHIFT ;
amdgpu_encoder - > devices = supported_device ;
amdgpu_encoder - > rmx_type = RMX_OFF ;
amdgpu_encoder - > underscan_type = UNDERSCAN_OFF ;
amdgpu_encoder - > is_ext_encoder = false ;
amdgpu_encoder - > caps = caps ;
switch ( amdgpu_encoder - > encoder_id ) {
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 :
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 :
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_DAC , NULL ) ;
drm_encoder_helper_add ( encoder , & dce_v6_0_dac_helper_funcs ) ;
break ;
case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 :
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY :
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 :
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 :
case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 :
if ( amdgpu_encoder - > devices & ( ATOM_DEVICE_LCD_SUPPORT ) ) {
amdgpu_encoder - > rmx_type = RMX_FULL ;
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_LVDS , NULL ) ;
amdgpu_encoder - > enc_priv = amdgpu_atombios_encoder_get_lcd_info ( amdgpu_encoder ) ;
} else if ( amdgpu_encoder - > devices & ( ATOM_DEVICE_CRT_SUPPORT ) ) {
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_DAC , NULL ) ;
amdgpu_encoder - > enc_priv = amdgpu_atombios_encoder_get_dig_info ( amdgpu_encoder ) ;
} else {
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_TMDS , NULL ) ;
amdgpu_encoder - > enc_priv = amdgpu_atombios_encoder_get_dig_info ( amdgpu_encoder ) ;
}
drm_encoder_helper_add ( encoder , & dce_v6_0_dig_helper_funcs ) ;
break ;
case ENCODER_OBJECT_ID_SI170B :
case ENCODER_OBJECT_ID_CH7303 :
case ENCODER_OBJECT_ID_EXTERNAL_SDVOA :
case ENCODER_OBJECT_ID_EXTERNAL_SDVOB :
case ENCODER_OBJECT_ID_TITFP513 :
case ENCODER_OBJECT_ID_VT1623 :
case ENCODER_OBJECT_ID_HDMI_SI1930 :
case ENCODER_OBJECT_ID_TRAVIS :
case ENCODER_OBJECT_ID_NUTMEG :
/* these are handled by the primary encoders */
amdgpu_encoder - > is_ext_encoder = true ;
if ( amdgpu_encoder - > devices & ( ATOM_DEVICE_LCD_SUPPORT ) )
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_LVDS , NULL ) ;
else if ( amdgpu_encoder - > devices & ( ATOM_DEVICE_CRT_SUPPORT ) )
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_DAC , NULL ) ;
else
drm_encoder_init ( dev , encoder , & dce_v6_0_encoder_funcs ,
DRM_MODE_ENCODER_TMDS , NULL ) ;
drm_encoder_helper_add ( encoder , & dce_v6_0_ext_helper_funcs ) ;
break ;
}
}
static const struct amdgpu_display_funcs dce_v6_0_display_funcs = {
. bandwidth_update = & dce_v6_0_bandwidth_update ,
. vblank_get_counter = & dce_v6_0_vblank_get_counter ,
. backlight_set_level = & amdgpu_atombios_encoder_set_backlight_level ,
. backlight_get_level = & amdgpu_atombios_encoder_get_backlight_level ,
. hpd_sense = & dce_v6_0_hpd_sense ,
. hpd_set_polarity = & dce_v6_0_hpd_set_polarity ,
. hpd_get_gpio_reg = & dce_v6_0_hpd_get_gpio_reg ,
. page_flip = & dce_v6_0_page_flip ,
. page_flip_get_scanoutpos = & dce_v6_0_crtc_get_scanoutpos ,
. add_encoder = & dce_v6_0_encoder_add ,
. add_connector = & amdgpu_connector_add ,
} ;
static void dce_v6_0_set_display_funcs ( struct amdgpu_device * adev )
{
2018-09-17 15:41:45 +02:00
adev - > mode_info . funcs = & dce_v6_0_display_funcs ;
2016-01-19 14:03:24 +08:00
}
static const struct amdgpu_irq_src_funcs dce_v6_0_crtc_irq_funcs = {
. set = dce_v6_0_set_crtc_interrupt_state ,
. process = dce_v6_0_crtc_irq ,
} ;
static const struct amdgpu_irq_src_funcs dce_v6_0_pageflip_irq_funcs = {
. set = dce_v6_0_set_pageflip_interrupt_state ,
. process = dce_v6_0_pageflip_irq ,
} ;
static const struct amdgpu_irq_src_funcs dce_v6_0_hpd_irq_funcs = {
. set = dce_v6_0_set_hpd_interrupt_state ,
. process = dce_v6_0_hpd_irq ,
} ;
static void dce_v6_0_set_irq_funcs ( struct amdgpu_device * adev )
{
2017-10-24 11:03:21 +02:00
if ( adev - > mode_info . num_crtc > 0 )
adev - > crtc_irq . num_types = AMDGPU_CRTC_IRQ_VLINE1 + adev - > mode_info . num_crtc ;
else
adev - > crtc_irq . num_types = 0 ;
2016-01-19 14:03:24 +08:00
adev - > crtc_irq . funcs = & dce_v6_0_crtc_irq_funcs ;
2017-10-24 11:03:21 +02:00
adev - > pageflip_irq . num_types = adev - > mode_info . num_crtc ;
2016-01-19 14:03:24 +08:00
adev - > pageflip_irq . funcs = & dce_v6_0_pageflip_irq_funcs ;
2017-10-24 11:03:21 +02:00
adev - > hpd_irq . num_types = adev - > mode_info . num_hpd ;
2016-01-19 14:03:24 +08:00
adev - > hpd_irq . funcs = & dce_v6_0_hpd_irq_funcs ;
}
2016-10-13 17:41:13 -04:00
const struct amdgpu_ip_block_version dce_v6_0_ip_block =
{
. type = AMD_IP_BLOCK_TYPE_DCE ,
. major = 6 ,
. minor = 0 ,
. rev = 0 ,
. funcs = & dce_v6_0_ip_funcs ,
} ;
const struct amdgpu_ip_block_version dce_v6_4_ip_block =
{
. type = AMD_IP_BLOCK_TYPE_DCE ,
. major = 6 ,
. minor = 4 ,
. rev = 0 ,
. funcs = & dce_v6_0_ip_funcs ,
} ;