2016-03-08 17:46:15 +02:00
/*
* Copyright © 2006 - 2016 Intel Corporation
*
* 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 ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE .
*/
# include "intel_drv.h"
struct intel_shared_dpll *
2016-03-08 17:46:18 +02:00
intel_get_shared_dpll_by_id ( struct drm_i915_private * dev_priv ,
enum intel_dpll_id id )
2016-03-08 17:46:15 +02:00
{
2016-03-08 17:46:18 +02:00
return & dev_priv - > shared_dplls [ id ] ;
}
2016-03-08 17:46:15 +02:00
2016-03-08 17:46:18 +02:00
enum intel_dpll_id
intel_get_shared_dpll_id ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
if ( WARN_ON ( pll < dev_priv - > shared_dplls | |
pll > & dev_priv - > shared_dplls [ dev_priv - > num_shared_dpll ] ) )
return - 1 ;
return ( enum intel_dpll_id ) ( pll - dev_priv - > shared_dplls ) ;
}
void
intel_shared_dpll_config_get ( struct intel_shared_dpll_config * config ,
struct intel_shared_dpll * pll ,
struct intel_crtc * crtc )
{
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
enum intel_dpll_id id = intel_get_shared_dpll_id ( dev_priv , pll ) ;
config [ id ] . crtc_mask | = 1 < < crtc - > pipe ;
}
void
intel_shared_dpll_config_put ( struct intel_shared_dpll_config * config ,
struct intel_shared_dpll * pll ,
struct intel_crtc * crtc )
{
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
enum intel_dpll_id id = intel_get_shared_dpll_id ( dev_priv , pll ) ;
2016-03-08 17:46:15 +02:00
2016-03-08 17:46:18 +02:00
config [ id ] . crtc_mask & = ~ ( 1 < < crtc - > pipe ) ;
2016-03-08 17:46:15 +02:00
}
/* For ILK+ */
void assert_shared_dpll ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
bool state )
{
bool cur_state ;
struct intel_dpll_hw_state hw_state ;
if ( WARN ( ! pll , " asserting DPLL %s with no DPLL \n " , onoff ( state ) ) )
return ;
2016-03-08 17:46:21 +02:00
cur_state = pll - > funcs . get_hw_state ( dev_priv , pll , & hw_state ) ;
2016-03-08 17:46:15 +02:00
I915_STATE_WARN ( cur_state ! = state ,
" %s assertion failure (expected %s, current %s) \n " ,
pll - > name , onoff ( state ) , onoff ( cur_state ) ) ;
}
void intel_prepare_shared_dpll ( struct intel_crtc * crtc )
{
struct drm_device * dev = crtc - > base . dev ;
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( dev ) ;
2016-03-08 17:46:18 +02:00
struct intel_shared_dpll * pll = crtc - > config - > shared_dpll ;
2016-03-08 17:46:15 +02:00
if ( WARN_ON ( pll = = NULL ) )
return ;
2016-03-23 14:51:12 +01:00
mutex_lock ( & dev_priv - > dpll_lock ) ;
2016-03-08 17:46:15 +02:00
WARN_ON ( ! pll - > config . crtc_mask ) ;
2016-03-23 14:51:12 +01:00
if ( ! pll - > active_mask ) {
2016-03-08 17:46:15 +02:00
DRM_DEBUG_DRIVER ( " setting up %s \n " , pll - > name ) ;
WARN_ON ( pll - > on ) ;
assert_shared_dpll_disabled ( dev_priv , pll ) ;
2016-03-08 17:46:21 +02:00
pll - > funcs . mode_set ( dev_priv , pll ) ;
2016-03-08 17:46:15 +02:00
}
2016-03-23 14:51:12 +01:00
mutex_unlock ( & dev_priv - > dpll_lock ) ;
2016-03-08 17:46:15 +02:00
}
/**
* intel_enable_shared_dpll - enable PCH PLL
* @ dev_priv : i915 private structure
* @ pipe : pipe PLL to enable
*
* The PCH PLL needs to be enabled before the PCH transcoder , since it
* drives the transcoder clock .
*/
void intel_enable_shared_dpll ( struct intel_crtc * crtc )
{
struct drm_device * dev = crtc - > base . dev ;
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( dev ) ;
2016-03-08 17:46:18 +02:00
struct intel_shared_dpll * pll = crtc - > config - > shared_dpll ;
2016-03-14 09:27:52 +01:00
unsigned crtc_mask = 1 < < drm_crtc_index ( & crtc - > base ) ;
2016-03-23 14:51:12 +01:00
unsigned old_mask ;
2016-03-08 17:46:15 +02:00
if ( WARN_ON ( pll = = NULL ) )
return ;
2016-03-23 14:51:12 +01:00
mutex_lock ( & dev_priv - > dpll_lock ) ;
old_mask = pll - > active_mask ;
2016-03-14 09:27:52 +01:00
if ( WARN_ON ( ! ( pll - > config . crtc_mask & crtc_mask ) ) | |
WARN_ON ( pll - > active_mask & crtc_mask ) )
2016-03-23 14:51:12 +01:00
goto out ;
2016-03-08 17:46:15 +02:00
2016-03-14 09:27:52 +01:00
pll - > active_mask | = crtc_mask ;
DRM_DEBUG_KMS ( " enable %s (active %x, on? %d) for crtc %d \n " ,
pll - > name , pll - > active_mask , pll - > on ,
2016-03-08 17:46:15 +02:00
crtc - > base . base . id ) ;
2016-03-14 09:27:52 +01:00
if ( old_mask ) {
2016-03-08 17:46:15 +02:00
WARN_ON ( ! pll - > on ) ;
assert_shared_dpll_enabled ( dev_priv , pll ) ;
2016-03-23 14:51:12 +01:00
goto out ;
2016-03-08 17:46:15 +02:00
}
WARN_ON ( pll - > on ) ;
DRM_DEBUG_KMS ( " enabling %s \n " , pll - > name ) ;
2016-03-08 17:46:21 +02:00
pll - > funcs . enable ( dev_priv , pll ) ;
2016-03-08 17:46:15 +02:00
pll - > on = true ;
2016-03-23 14:51:12 +01:00
out :
mutex_unlock ( & dev_priv - > dpll_lock ) ;
2016-03-08 17:46:15 +02:00
}
void intel_disable_shared_dpll ( struct intel_crtc * crtc )
{
struct drm_device * dev = crtc - > base . dev ;
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( dev ) ;
2016-03-08 17:46:18 +02:00
struct intel_shared_dpll * pll = crtc - > config - > shared_dpll ;
2016-03-14 09:27:52 +01:00
unsigned crtc_mask = 1 < < drm_crtc_index ( & crtc - > base ) ;
2016-03-08 17:46:15 +02:00
/* PCH only available on ILK+ */
if ( INTEL_INFO ( dev ) - > gen < 5 )
return ;
if ( pll = = NULL )
return ;
2016-03-23 14:51:12 +01:00
mutex_lock ( & dev_priv - > dpll_lock ) ;
2016-03-14 09:27:53 +01:00
if ( WARN_ON ( ! ( pll - > active_mask & crtc_mask ) ) )
2016-03-23 14:51:12 +01:00
goto out ;
2016-03-08 17:46:15 +02:00
2016-03-14 09:27:52 +01:00
DRM_DEBUG_KMS ( " disable %s (active %x, on? %d) for crtc %d \n " ,
pll - > name , pll - > active_mask , pll - > on ,
2016-03-08 17:46:15 +02:00
crtc - > base . base . id ) ;
assert_shared_dpll_enabled ( dev_priv , pll ) ;
WARN_ON ( ! pll - > on ) ;
2016-03-14 09:27:52 +01:00
pll - > active_mask & = ~ crtc_mask ;
if ( pll - > active_mask )
2016-03-23 14:51:12 +01:00
goto out ;
2016-03-08 17:46:15 +02:00
DRM_DEBUG_KMS ( " disabling %s \n " , pll - > name ) ;
2016-03-08 17:46:21 +02:00
pll - > funcs . disable ( dev_priv , pll ) ;
2016-03-08 17:46:15 +02:00
pll - > on = false ;
2016-03-23 14:51:12 +01:00
out :
mutex_unlock ( & dev_priv - > dpll_lock ) ;
2016-03-08 17:46:15 +02:00
}
2016-03-08 17:46:22 +02:00
static struct intel_shared_dpll *
2016-03-08 17:46:17 +02:00
intel_find_shared_dpll ( struct intel_crtc * crtc ,
2016-03-08 17:46:22 +02:00
struct intel_crtc_state * crtc_state ,
enum intel_dpll_id range_min ,
enum intel_dpll_id range_max )
2016-03-08 17:46:17 +02:00
{
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
2016-03-08 17:46:17 +02:00
struct intel_shared_dpll * pll ;
struct intel_shared_dpll_config * shared_dpll ;
enum intel_dpll_id i ;
2016-03-08 17:46:15 +02:00
2016-03-08 17:46:17 +02:00
shared_dpll = intel_atomic_get_shared_dpll_state ( crtc_state - > base . state ) ;
2016-03-08 17:46:22 +02:00
for ( i = range_min ; i < = range_max ; i + + ) {
2016-03-08 17:46:15 +02:00
pll = & dev_priv - > shared_dplls [ i ] ;
/* Only want to check enabled timings first */
if ( shared_dpll [ i ] . crtc_mask = = 0 )
continue ;
if ( memcmp ( & crtc_state - > dpll_hw_state ,
& shared_dpll [ i ] . hw_state ,
sizeof ( crtc_state - > dpll_hw_state ) ) = = 0 ) {
2016-05-27 20:59:19 +03:00
DRM_DEBUG_KMS ( " [CRTC:%d:%s] sharing existing %s (crtc mask 0x%08x, active %x) \n " ,
crtc - > base . base . id , crtc - > base . name , pll - > name ,
2016-03-08 17:46:15 +02:00
shared_dpll [ i ] . crtc_mask ,
2016-03-14 09:27:52 +01:00
pll - > active_mask ) ;
2016-03-08 17:46:22 +02:00
return pll ;
2016-03-08 17:46:15 +02:00
}
}
/* Ok no matching timings, maybe there's a free one? */
2016-03-08 17:46:22 +02:00
for ( i = range_min ; i < = range_max ; i + + ) {
2016-03-08 17:46:15 +02:00
pll = & dev_priv - > shared_dplls [ i ] ;
if ( shared_dpll [ i ] . crtc_mask = = 0 ) {
2016-05-27 20:59:19 +03:00
DRM_DEBUG_KMS ( " [CRTC:%d:%s] allocated %s \n " ,
crtc - > base . base . id , crtc - > base . name , pll - > name ) ;
2016-03-08 17:46:22 +02:00
return pll ;
2016-03-08 17:46:15 +02:00
}
}
2016-03-08 17:46:22 +02:00
return NULL ;
2016-03-08 17:46:17 +02:00
}
2016-03-08 17:46:22 +02:00
static void
intel_reference_shared_dpll ( struct intel_shared_dpll * pll ,
struct intel_crtc_state * crtc_state )
2016-03-08 17:46:17 +02:00
{
struct intel_shared_dpll_config * shared_dpll ;
2016-03-08 17:46:22 +02:00
struct intel_crtc * crtc = to_intel_crtc ( crtc_state - > base . crtc ) ;
enum intel_dpll_id i = pll - > id ;
2016-03-08 17:46:17 +02:00
shared_dpll = intel_atomic_get_shared_dpll_state ( crtc_state - > base . state ) ;
2016-03-08 17:46:15 +02:00
if ( shared_dpll [ i ] . crtc_mask = = 0 )
shared_dpll [ i ] . hw_state =
crtc_state - > dpll_hw_state ;
2016-03-08 17:46:18 +02:00
crtc_state - > shared_dpll = pll ;
2016-03-08 17:46:15 +02:00
DRM_DEBUG_DRIVER ( " using %s for pipe %c \n " , pll - > name ,
pipe_name ( crtc - > pipe ) ) ;
2016-03-08 17:46:18 +02:00
intel_shared_dpll_config_get ( shared_dpll , pll , crtc ) ;
2016-03-08 17:46:15 +02:00
}
void intel_shared_dpll_commit ( struct drm_atomic_state * state )
{
struct drm_i915_private * dev_priv = to_i915 ( state - > dev ) ;
struct intel_shared_dpll_config * shared_dpll ;
struct intel_shared_dpll * pll ;
enum intel_dpll_id i ;
if ( ! to_intel_atomic_state ( state ) - > dpll_set )
return ;
shared_dpll = to_intel_atomic_state ( state ) - > shared_dpll ;
for ( i = 0 ; i < dev_priv - > num_shared_dpll ; i + + ) {
pll = & dev_priv - > shared_dplls [ i ] ;
pll - > config = shared_dpll [ i ] ;
}
}
static bool ibx_pch_dpll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
uint32_t val ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
val = I915_READ ( PCH_DPLL ( pll - > id ) ) ;
hw_state - > dpll = val ;
hw_state - > fp0 = I915_READ ( PCH_FP0 ( pll - > id ) ) ;
hw_state - > fp1 = I915_READ ( PCH_FP1 ( pll - > id ) ) ;
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return val & DPLL_VCO_ENABLE ;
}
static void ibx_pch_dpll_mode_set ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
I915_WRITE ( PCH_FP0 ( pll - > id ) , pll - > config . hw_state . fp0 ) ;
I915_WRITE ( PCH_FP1 ( pll - > id ) , pll - > config . hw_state . fp1 ) ;
}
static void ibx_assert_pch_refclk_enabled ( struct drm_i915_private * dev_priv )
{
u32 val ;
bool enabled ;
2016-04-07 11:08:05 +03:00
I915_STATE_WARN_ON ( ! ( HAS_PCH_IBX ( dev_priv ) | | HAS_PCH_CPT ( dev_priv ) ) ) ;
2016-03-08 17:46:15 +02:00
val = I915_READ ( PCH_DREF_CONTROL ) ;
enabled = ! ! ( val & ( DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK |
DREF_SUPERSPREAD_SOURCE_MASK ) ) ;
I915_STATE_WARN ( ! enabled , " PCH refclk assertion failure, should be active but is disabled \n " ) ;
}
static void ibx_pch_dpll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
/* PCH refclock must be enabled first */
ibx_assert_pch_refclk_enabled ( dev_priv ) ;
I915_WRITE ( PCH_DPLL ( pll - > id ) , pll - > config . hw_state . dpll ) ;
/* Wait for the clocks to stabilize. */
POSTING_READ ( PCH_DPLL ( pll - > id ) ) ;
udelay ( 150 ) ;
/* The pixel multiplier can only be updated once the
* DPLL is enabled and the clocks are stable .
*
* So write it again .
*/
I915_WRITE ( PCH_DPLL ( pll - > id ) , pll - > config . hw_state . dpll ) ;
POSTING_READ ( PCH_DPLL ( pll - > id ) ) ;
udelay ( 200 ) ;
}
static void ibx_pch_dpll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
struct drm_device * dev = dev_priv - > dev ;
struct intel_crtc * crtc ;
/* Make sure no transcoder isn't still depending on us. */
for_each_intel_crtc ( dev , crtc ) {
2016-03-08 17:46:18 +02:00
if ( crtc - > config - > shared_dpll = = pll )
2016-03-08 17:46:15 +02:00
assert_pch_transcoder_disabled ( dev_priv , crtc - > pipe ) ;
}
I915_WRITE ( PCH_DPLL ( pll - > id ) , 0 ) ;
POSTING_READ ( PCH_DPLL ( pll - > id ) ) ;
udelay ( 200 ) ;
}
2016-03-08 17:46:22 +02:00
static struct intel_shared_dpll *
2016-03-08 17:46:23 +02:00
ibx_get_dpll ( struct intel_crtc * crtc , struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder )
2016-03-08 17:46:22 +02:00
{
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
struct intel_shared_dpll * pll ;
enum intel_dpll_id i ;
if ( HAS_PCH_IBX ( dev_priv ) ) {
/* Ironlake PCH has a fixed PLL->PCH pipe mapping. */
i = ( enum intel_dpll_id ) crtc - > pipe ;
pll = & dev_priv - > shared_dplls [ i ] ;
2016-05-27 20:59:19 +03:00
DRM_DEBUG_KMS ( " [CRTC:%d:%s] using pre-allocated %s \n " ,
crtc - > base . base . id , crtc - > base . name , pll - > name ) ;
2016-03-08 17:46:22 +02:00
} else {
pll = intel_find_shared_dpll ( crtc , crtc_state ,
DPLL_ID_PCH_PLL_A ,
DPLL_ID_PCH_PLL_B ) ;
}
2016-05-20 15:47:06 +03:00
if ( ! pll )
return NULL ;
2016-03-08 17:46:22 +02:00
/* reference the pll */
intel_reference_shared_dpll ( pll , crtc_state ) ;
return pll ;
}
2016-03-08 17:46:21 +02:00
static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = {
. mode_set = ibx_pch_dpll_mode_set ,
. enable = ibx_pch_dpll_enable ,
. disable = ibx_pch_dpll_disable ,
. get_hw_state = ibx_pch_dpll_get_hw_state ,
2016-03-08 17:46:15 +02:00
} ;
2016-03-08 17:46:16 +02:00
static void hsw_ddi_wrpll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
I915_WRITE ( WRPLL_CTL ( pll - > id ) , pll - > config . hw_state . wrpll ) ;
POSTING_READ ( WRPLL_CTL ( pll - > id ) ) ;
udelay ( 20 ) ;
}
static void hsw_ddi_spll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
I915_WRITE ( SPLL_CTL , pll - > config . hw_state . spll ) ;
POSTING_READ ( SPLL_CTL ) ;
udelay ( 20 ) ;
}
static void hsw_ddi_wrpll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
uint32_t val ;
val = I915_READ ( WRPLL_CTL ( pll - > id ) ) ;
I915_WRITE ( WRPLL_CTL ( pll - > id ) , val & ~ WRPLL_PLL_ENABLE ) ;
POSTING_READ ( WRPLL_CTL ( pll - > id ) ) ;
}
static void hsw_ddi_spll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
uint32_t val ;
val = I915_READ ( SPLL_CTL ) ;
I915_WRITE ( SPLL_CTL , val & ~ SPLL_PLL_ENABLE ) ;
POSTING_READ ( SPLL_CTL ) ;
}
static bool hsw_ddi_wrpll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
uint32_t val ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
val = I915_READ ( WRPLL_CTL ( pll - > id ) ) ;
hw_state - > wrpll = val ;
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return val & WRPLL_PLL_ENABLE ;
}
static bool hsw_ddi_spll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
uint32_t val ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
val = I915_READ ( SPLL_CTL ) ;
hw_state - > spll = val ;
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return val & SPLL_PLL_ENABLE ;
}
2016-03-08 17:46:23 +02:00
static uint32_t hsw_pll_to_ddi_pll_sel ( struct intel_shared_dpll * pll )
{
switch ( pll - > id ) {
case DPLL_ID_WRPLL1 :
return PORT_CLK_SEL_WRPLL1 ;
case DPLL_ID_WRPLL2 :
return PORT_CLK_SEL_WRPLL2 ;
case DPLL_ID_SPLL :
return PORT_CLK_SEL_SPLL ;
2016-03-08 17:46:26 +02:00
case DPLL_ID_LCPLL_810 :
return PORT_CLK_SEL_LCPLL_810 ;
case DPLL_ID_LCPLL_1350 :
return PORT_CLK_SEL_LCPLL_1350 ;
case DPLL_ID_LCPLL_2700 :
return PORT_CLK_SEL_LCPLL_2700 ;
2016-03-08 17:46:23 +02:00
default :
return PORT_CLK_SEL_NONE ;
}
}
# define LC_FREQ 2700
# define LC_FREQ_2K U64_C(LC_FREQ * 2000)
# define P_MIN 2
# define P_MAX 64
# define P_INC 2
/* Constraints for PLL good behavior */
# define REF_MIN 48
# define REF_MAX 400
# define VCO_MIN 2400
# define VCO_MAX 4800
struct hsw_wrpll_rnp {
unsigned p , n2 , r2 ;
} ;
static unsigned hsw_wrpll_get_budget_for_freq ( int clock )
{
unsigned budget ;
switch ( clock ) {
case 25175000 :
case 25200000 :
case 27000000 :
case 27027000 :
case 37762500 :
case 37800000 :
case 40500000 :
case 40541000 :
case 54000000 :
case 54054000 :
case 59341000 :
case 59400000 :
case 72000000 :
case 74176000 :
case 74250000 :
case 81000000 :
case 81081000 :
case 89012000 :
case 89100000 :
case 108000000 :
case 108108000 :
case 111264000 :
case 111375000 :
case 148352000 :
case 148500000 :
case 162000000 :
case 162162000 :
case 222525000 :
case 222750000 :
case 296703000 :
case 297000000 :
budget = 0 ;
break ;
case 233500000 :
case 245250000 :
case 247750000 :
case 253250000 :
case 298000000 :
budget = 1500 ;
break ;
case 169128000 :
case 169500000 :
case 179500000 :
case 202000000 :
budget = 2000 ;
break ;
case 256250000 :
case 262500000 :
case 270000000 :
case 272500000 :
case 273750000 :
case 280750000 :
case 281250000 :
case 286000000 :
case 291750000 :
budget = 4000 ;
break ;
case 267250000 :
case 268500000 :
budget = 5000 ;
break ;
default :
budget = 1000 ;
break ;
}
return budget ;
}
static void hsw_wrpll_update_rnp ( uint64_t freq2k , unsigned budget ,
unsigned r2 , unsigned n2 , unsigned p ,
struct hsw_wrpll_rnp * best )
{
uint64_t a , b , c , d , diff , diff_best ;
/* No best (r,n,p) yet */
if ( best - > p = = 0 ) {
best - > p = p ;
best - > n2 = n2 ;
best - > r2 = r2 ;
return ;
}
/*
* Output clock is ( LC_FREQ_2K / 2000 ) * N / ( P * R ) , which compares to
* freq2k .
*
* delta = 1e6 *
* abs ( freq2k - ( LC_FREQ_2K * n2 / ( p * r2 ) ) ) /
* freq2k ;
*
* and we would like delta < = budget .
*
* If the discrepancy is above the PPM - based budget , always prefer to
* improve upon the previous solution . However , if you ' re within the
* budget , try to maximize Ref * VCO , that is N / ( P * R ^ 2 ) .
*/
a = freq2k * budget * p * r2 ;
b = freq2k * budget * best - > p * best - > r2 ;
diff = abs_diff ( freq2k * p * r2 , LC_FREQ_2K * n2 ) ;
diff_best = abs_diff ( freq2k * best - > p * best - > r2 ,
LC_FREQ_2K * best - > n2 ) ;
c = 1000000 * diff ;
d = 1000000 * diff_best ;
if ( a < c & & b < d ) {
/* If both are above the budget, pick the closer */
if ( best - > p * best - > r2 * diff < p * r2 * diff_best ) {
best - > p = p ;
best - > n2 = n2 ;
best - > r2 = r2 ;
}
} else if ( a > = c & & b < d ) {
/* If A is below the threshold but B is above it? Update. */
best - > p = p ;
best - > n2 = n2 ;
best - > r2 = r2 ;
} else if ( a > = c & & b > = d ) {
/* Both are below the limit, so pick the higher n2/(r2*r2) */
if ( n2 * best - > r2 * best - > r2 > best - > n2 * r2 * r2 ) {
best - > p = p ;
best - > n2 = n2 ;
best - > r2 = r2 ;
}
}
/* Otherwise a < c && b >= d, do nothing */
}
static void
hsw_ddi_calculate_wrpll ( int clock /* in Hz */ ,
unsigned * r2_out , unsigned * n2_out , unsigned * p_out )
{
uint64_t freq2k ;
unsigned p , n2 , r2 ;
struct hsw_wrpll_rnp best = { 0 , 0 , 0 } ;
unsigned budget ;
freq2k = clock / 100 ;
budget = hsw_wrpll_get_budget_for_freq ( clock ) ;
/* Special case handling for 540 pixel clock: bypass WR PLL entirely
* and directly pass the LC PLL to it . */
if ( freq2k = = 5400000 ) {
* n2_out = 2 ;
* p_out = 1 ;
* r2_out = 2 ;
return ;
}
/*
* Ref = LC_FREQ / R , where Ref is the actual reference input seen by
* the WR PLL .
*
* We want R so that REF_MIN < = Ref < = REF_MAX .
* Injecting R2 = 2 * R gives :
* REF_MAX * r2 > LC_FREQ * 2 and
* REF_MIN * r2 < LC_FREQ * 2
*
* Which means the desired boundaries for r2 are :
* LC_FREQ * 2 / REF_MAX < r2 < LC_FREQ * 2 / REF_MIN
*
*/
for ( r2 = LC_FREQ * 2 / REF_MAX + 1 ;
r2 < = LC_FREQ * 2 / REF_MIN ;
r2 + + ) {
/*
* VCO = N * Ref , that is : VCO = N * LC_FREQ / R
*
* Once again we want VCO_MIN < = VCO < = VCO_MAX .
* Injecting R2 = 2 * R and N2 = 2 * N , we get :
* VCO_MAX * r2 > n2 * LC_FREQ and
* VCO_MIN * r2 < n2 * LC_FREQ )
*
* Which means the desired boundaries for n2 are :
* VCO_MIN * r2 / LC_FREQ < n2 < VCO_MAX * r2 / LC_FREQ
*/
for ( n2 = VCO_MIN * r2 / LC_FREQ + 1 ;
n2 < = VCO_MAX * r2 / LC_FREQ ;
n2 + + ) {
for ( p = P_MIN ; p < = P_MAX ; p + = P_INC )
hsw_wrpll_update_rnp ( freq2k , budget ,
r2 , n2 , p , & best ) ;
}
}
* n2_out = best . n2 ;
* p_out = best . p ;
* r2_out = best . r2 ;
}
2016-03-08 17:46:22 +02:00
static struct intel_shared_dpll *
2016-03-08 17:46:23 +02:00
hsw_get_dpll ( struct intel_crtc * crtc , struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder )
2016-03-08 17:46:22 +02:00
{
2016-03-08 17:46:26 +02:00
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
2016-03-08 17:46:22 +02:00
struct intel_shared_dpll * pll ;
2016-03-08 17:46:23 +02:00
int clock = crtc_state - > port_clock ;
2016-03-08 17:46:22 +02:00
2016-03-08 17:46:26 +02:00
memset ( & crtc_state - > dpll_hw_state , 0 ,
sizeof ( crtc_state - > dpll_hw_state ) ) ;
2016-03-08 17:46:23 +02:00
if ( encoder - > type = = INTEL_OUTPUT_HDMI ) {
uint32_t val ;
unsigned p , n2 , r2 ;
hsw_ddi_calculate_wrpll ( clock * 1000 , & r2 , & n2 , & p ) ;
val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL |
WRPLL_DIVIDER_REFERENCE ( r2 ) | WRPLL_DIVIDER_FEEDBACK ( n2 ) |
WRPLL_DIVIDER_POST ( p ) ;
crtc_state - > dpll_hw_state . wrpll = val ;
pll = intel_find_shared_dpll ( crtc , crtc_state ,
DPLL_ID_WRPLL1 , DPLL_ID_WRPLL2 ) ;
2016-03-08 17:46:26 +02:00
} else if ( encoder - > type = = INTEL_OUTPUT_DISPLAYPORT | |
encoder - > type = = INTEL_OUTPUT_DP_MST | |
encoder - > type = = INTEL_OUTPUT_EDP ) {
enum intel_dpll_id pll_id ;
switch ( clock / 2 ) {
case 81000 :
pll_id = DPLL_ID_LCPLL_810 ;
break ;
case 135000 :
pll_id = DPLL_ID_LCPLL_1350 ;
break ;
case 270000 :
pll_id = DPLL_ID_LCPLL_2700 ;
break ;
default :
DRM_DEBUG_KMS ( " Invalid clock for DP: %d \n " , clock ) ;
return NULL ;
}
pll = intel_get_shared_dpll_by_id ( dev_priv , pll_id ) ;
2016-03-08 17:46:23 +02:00
} else if ( encoder - > type = = INTEL_OUTPUT_ANALOG ) {
if ( WARN_ON ( crtc_state - > port_clock / 2 ! = 135000 ) )
return NULL ;
crtc_state - > dpll_hw_state . spll =
SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC ;
pll = intel_find_shared_dpll ( crtc , crtc_state ,
DPLL_ID_SPLL , DPLL_ID_SPLL ) ;
} else {
return NULL ;
}
if ( ! pll )
return NULL ;
crtc_state - > ddi_pll_sel = hsw_pll_to_ddi_pll_sel ( pll ) ;
intel_reference_shared_dpll ( pll , crtc_state ) ;
2016-03-08 17:46:22 +02:00
return pll ;
}
2016-03-08 17:46:16 +02:00
2016-03-08 17:46:21 +02:00
static const struct intel_shared_dpll_funcs hsw_ddi_wrpll_funcs = {
. enable = hsw_ddi_wrpll_enable ,
. disable = hsw_ddi_wrpll_disable ,
. get_hw_state = hsw_ddi_wrpll_get_hw_state ,
2016-03-08 17:46:16 +02:00
} ;
2016-03-08 17:46:21 +02:00
static const struct intel_shared_dpll_funcs hsw_ddi_spll_funcs = {
. enable = hsw_ddi_spll_enable ,
. disable = hsw_ddi_spll_disable ,
. get_hw_state = hsw_ddi_spll_get_hw_state ,
2016-03-08 17:46:16 +02:00
} ;
2016-03-08 17:46:26 +02:00
static void hsw_ddi_lcpll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
}
static void hsw_ddi_lcpll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
}
static bool hsw_ddi_lcpll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
return true ;
}
static const struct intel_shared_dpll_funcs hsw_ddi_lcpll_funcs = {
. enable = hsw_ddi_lcpll_enable ,
. disable = hsw_ddi_lcpll_disable ,
. get_hw_state = hsw_ddi_lcpll_get_hw_state ,
} ;
2016-03-08 17:46:16 +02:00
struct skl_dpll_regs {
i915_reg_t ctl , cfgcr1 , cfgcr2 ;
} ;
/* this array is indexed by the *shared* pll id */
2016-03-08 17:46:27 +02:00
static const struct skl_dpll_regs skl_dpll_regs [ 4 ] = {
{
/* DPLL 0 */
. ctl = LCPLL1_CTL ,
/* DPLL 0 doesn't support HDMI mode */
} ,
2016-03-08 17:46:16 +02:00
{
/* DPLL 1 */
. ctl = LCPLL2_CTL ,
. cfgcr1 = DPLL_CFGCR1 ( SKL_DPLL1 ) ,
. cfgcr2 = DPLL_CFGCR2 ( SKL_DPLL1 ) ,
} ,
{
/* DPLL 2 */
. ctl = WRPLL_CTL ( 0 ) ,
. cfgcr1 = DPLL_CFGCR1 ( SKL_DPLL2 ) ,
. cfgcr2 = DPLL_CFGCR2 ( SKL_DPLL2 ) ,
} ,
{
/* DPLL 3 */
. ctl = WRPLL_CTL ( 1 ) ,
. cfgcr1 = DPLL_CFGCR1 ( SKL_DPLL3 ) ,
. cfgcr2 = DPLL_CFGCR2 ( SKL_DPLL3 ) ,
} ,
} ;
2016-03-08 17:46:27 +02:00
static void skl_ddi_pll_write_ctrl1 ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
2016-03-08 17:46:16 +02:00
{
uint32_t val ;
val = I915_READ ( DPLL_CTRL1 ) ;
2016-03-08 17:46:27 +02:00
val & = ~ ( DPLL_CTRL1_HDMI_MODE ( pll - > id ) | DPLL_CTRL1_SSC ( pll - > id ) |
DPLL_CTRL1_LINK_RATE_MASK ( pll - > id ) ) ;
val | = pll - > config . hw_state . ctrl1 < < ( pll - > id * 6 ) ;
2016-03-08 17:46:16 +02:00
I915_WRITE ( DPLL_CTRL1 , val ) ;
POSTING_READ ( DPLL_CTRL1 ) ;
2016-03-08 17:46:27 +02:00
}
static void skl_ddi_pll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
const struct skl_dpll_regs * regs = skl_dpll_regs ;
skl_ddi_pll_write_ctrl1 ( dev_priv , pll ) ;
2016-03-08 17:46:16 +02:00
I915_WRITE ( regs [ pll - > id ] . cfgcr1 , pll - > config . hw_state . cfgcr1 ) ;
I915_WRITE ( regs [ pll - > id ] . cfgcr2 , pll - > config . hw_state . cfgcr2 ) ;
POSTING_READ ( regs [ pll - > id ] . cfgcr1 ) ;
POSTING_READ ( regs [ pll - > id ] . cfgcr2 ) ;
/* the enable bit is always bit 31 */
I915_WRITE ( regs [ pll - > id ] . ctl ,
I915_READ ( regs [ pll - > id ] . ctl ) | LCPLL_PLL_ENABLE ) ;
2016-06-30 15:33:12 +01:00
if ( intel_wait_for_register ( dev_priv ,
DPLL_STATUS ,
DPLL_LOCK ( pll - > id ) ,
DPLL_LOCK ( pll - > id ) ,
5 ) )
2016-03-08 17:46:27 +02:00
DRM_ERROR ( " DPLL %d not locked \n " , pll - > id ) ;
}
static void skl_ddi_dpll0_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
skl_ddi_pll_write_ctrl1 ( dev_priv , pll ) ;
2016-03-08 17:46:16 +02:00
}
static void skl_ddi_pll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
const struct skl_dpll_regs * regs = skl_dpll_regs ;
/* the enable bit is always bit 31 */
I915_WRITE ( regs [ pll - > id ] . ctl ,
I915_READ ( regs [ pll - > id ] . ctl ) & ~ LCPLL_PLL_ENABLE ) ;
POSTING_READ ( regs [ pll - > id ] . ctl ) ;
}
2016-03-08 17:46:27 +02:00
static void skl_ddi_dpll0_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
}
2016-03-08 17:46:16 +02:00
static bool skl_ddi_pll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
uint32_t val ;
const struct skl_dpll_regs * regs = skl_dpll_regs ;
bool ret ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
ret = false ;
val = I915_READ ( regs [ pll - > id ] . ctl ) ;
if ( ! ( val & LCPLL_PLL_ENABLE ) )
goto out ;
val = I915_READ ( DPLL_CTRL1 ) ;
2016-03-08 17:46:27 +02:00
hw_state - > ctrl1 = ( val > > ( pll - > id * 6 ) ) & 0x3f ;
2016-03-08 17:46:16 +02:00
/* avoid reading back stale values if HDMI mode is not enabled */
2016-03-08 17:46:27 +02:00
if ( val & DPLL_CTRL1_HDMI_MODE ( pll - > id ) ) {
2016-03-08 17:46:16 +02:00
hw_state - > cfgcr1 = I915_READ ( regs [ pll - > id ] . cfgcr1 ) ;
hw_state - > cfgcr2 = I915_READ ( regs [ pll - > id ] . cfgcr2 ) ;
}
ret = true ;
out :
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return ret ;
}
2016-03-08 17:46:27 +02:00
static bool skl_ddi_dpll0_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
uint32_t val ;
const struct skl_dpll_regs * regs = skl_dpll_regs ;
bool ret ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
ret = false ;
/* DPLL0 is always enabled since it drives CDCLK */
val = I915_READ ( regs [ pll - > id ] . ctl ) ;
if ( WARN_ON ( ! ( val & LCPLL_PLL_ENABLE ) ) )
goto out ;
val = I915_READ ( DPLL_CTRL1 ) ;
hw_state - > ctrl1 = ( val > > ( pll - > id * 6 ) ) & 0x3f ;
ret = true ;
out :
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return ret ;
}
2016-03-08 17:46:24 +02:00
struct skl_wrpll_context {
uint64_t min_deviation ; /* current minimal deviation */
uint64_t central_freq ; /* chosen central freq */
uint64_t dco_freq ; /* chosen dco freq */
unsigned int p ; /* chosen divider */
} ;
static void skl_wrpll_context_init ( struct skl_wrpll_context * ctx )
{
memset ( ctx , 0 , sizeof ( * ctx ) ) ;
ctx - > min_deviation = U64_MAX ;
}
/* DCO freq must be within +1%/-6% of the DCO central freq */
# define SKL_DCO_MAX_PDEVIATION 100
# define SKL_DCO_MAX_NDEVIATION 600
static void skl_wrpll_try_divider ( struct skl_wrpll_context * ctx ,
uint64_t central_freq ,
uint64_t dco_freq ,
unsigned int divider )
{
uint64_t deviation ;
deviation = div64_u64 ( 10000 * abs_diff ( dco_freq , central_freq ) ,
central_freq ) ;
/* positive deviation */
if ( dco_freq > = central_freq ) {
if ( deviation < SKL_DCO_MAX_PDEVIATION & &
deviation < ctx - > min_deviation ) {
ctx - > min_deviation = deviation ;
ctx - > central_freq = central_freq ;
ctx - > dco_freq = dco_freq ;
ctx - > p = divider ;
}
/* negative deviation */
} else if ( deviation < SKL_DCO_MAX_NDEVIATION & &
deviation < ctx - > min_deviation ) {
ctx - > min_deviation = deviation ;
ctx - > central_freq = central_freq ;
ctx - > dco_freq = dco_freq ;
ctx - > p = divider ;
}
}
static void skl_wrpll_get_multipliers ( unsigned int p ,
unsigned int * p0 /* out */ ,
unsigned int * p1 /* out */ ,
unsigned int * p2 /* out */ )
{
/* even dividers */
if ( p % 2 = = 0 ) {
unsigned int half = p / 2 ;
if ( half = = 1 | | half = = 2 | | half = = 3 | | half = = 5 ) {
* p0 = 2 ;
* p1 = 1 ;
* p2 = half ;
} else if ( half % 2 = = 0 ) {
* p0 = 2 ;
* p1 = half / 2 ;
* p2 = 2 ;
} else if ( half % 3 = = 0 ) {
* p0 = 3 ;
* p1 = half / 3 ;
* p2 = 2 ;
} else if ( half % 7 = = 0 ) {
* p0 = 7 ;
* p1 = half / 7 ;
* p2 = 2 ;
}
} else if ( p = = 3 | | p = = 9 ) { /* 3, 5, 7, 9, 15, 21, 35 */
* p0 = 3 ;
* p1 = 1 ;
* p2 = p / 3 ;
} else if ( p = = 5 | | p = = 7 ) {
* p0 = p ;
* p1 = 1 ;
* p2 = 1 ;
} else if ( p = = 15 ) {
* p0 = 3 ;
* p1 = 1 ;
* p2 = 5 ;
} else if ( p = = 21 ) {
* p0 = 7 ;
* p1 = 1 ;
* p2 = 3 ;
} else if ( p = = 35 ) {
* p0 = 7 ;
* p1 = 1 ;
* p2 = 5 ;
}
}
struct skl_wrpll_params {
uint32_t dco_fraction ;
uint32_t dco_integer ;
uint32_t qdiv_ratio ;
uint32_t qdiv_mode ;
uint32_t kdiv ;
uint32_t pdiv ;
uint32_t central_freq ;
} ;
static void skl_wrpll_params_populate ( struct skl_wrpll_params * params ,
uint64_t afe_clock ,
uint64_t central_freq ,
uint32_t p0 , uint32_t p1 , uint32_t p2 )
{
uint64_t dco_freq ;
switch ( central_freq ) {
case 9600000000ULL :
params - > central_freq = 0 ;
break ;
case 9000000000ULL :
params - > central_freq = 1 ;
break ;
case 8400000000ULL :
params - > central_freq = 3 ;
}
switch ( p0 ) {
case 1 :
params - > pdiv = 0 ;
break ;
case 2 :
params - > pdiv = 1 ;
break ;
case 3 :
params - > pdiv = 2 ;
break ;
case 7 :
params - > pdiv = 4 ;
break ;
default :
WARN ( 1 , " Incorrect PDiv \n " ) ;
}
switch ( p2 ) {
case 5 :
params - > kdiv = 0 ;
break ;
case 2 :
params - > kdiv = 1 ;
break ;
case 3 :
params - > kdiv = 2 ;
break ;
case 1 :
params - > kdiv = 3 ;
break ;
default :
WARN ( 1 , " Incorrect KDiv \n " ) ;
}
params - > qdiv_ratio = p1 ;
params - > qdiv_mode = ( params - > qdiv_ratio = = 1 ) ? 0 : 1 ;
dco_freq = p0 * p1 * p2 * afe_clock ;
/*
* Intermediate values are in Hz .
* Divide by MHz to match bsepc
*/
params - > dco_integer = div_u64 ( dco_freq , 24 * MHz ( 1 ) ) ;
params - > dco_fraction =
div_u64 ( ( div_u64 ( dco_freq , 24 ) -
params - > dco_integer * MHz ( 1 ) ) * 0x8000 , MHz ( 1 ) ) ;
}
static bool
skl_ddi_calculate_wrpll ( int clock /* in Hz */ ,
struct skl_wrpll_params * wrpll_params )
{
uint64_t afe_clock = clock * 5 ; /* AFE Clock is 5x Pixel clock */
uint64_t dco_central_freq [ 3 ] = { 8400000000ULL ,
9000000000ULL ,
9600000000ULL } ;
static const int even_dividers [ ] = { 4 , 6 , 8 , 10 , 12 , 14 , 16 , 18 , 20 ,
24 , 28 , 30 , 32 , 36 , 40 , 42 , 44 ,
48 , 52 , 54 , 56 , 60 , 64 , 66 , 68 ,
70 , 72 , 76 , 78 , 80 , 84 , 88 , 90 ,
92 , 96 , 98 } ;
static const int odd_dividers [ ] = { 3 , 5 , 7 , 9 , 15 , 21 , 35 } ;
static const struct {
const int * list ;
int n_dividers ;
} dividers [ ] = {
{ even_dividers , ARRAY_SIZE ( even_dividers ) } ,
{ odd_dividers , ARRAY_SIZE ( odd_dividers ) } ,
} ;
struct skl_wrpll_context ctx ;
unsigned int dco , d , i ;
unsigned int p0 , p1 , p2 ;
skl_wrpll_context_init ( & ctx ) ;
for ( d = 0 ; d < ARRAY_SIZE ( dividers ) ; d + + ) {
for ( dco = 0 ; dco < ARRAY_SIZE ( dco_central_freq ) ; dco + + ) {
for ( i = 0 ; i < dividers [ d ] . n_dividers ; i + + ) {
unsigned int p = dividers [ d ] . list [ i ] ;
uint64_t dco_freq = p * afe_clock ;
skl_wrpll_try_divider ( & ctx ,
dco_central_freq [ dco ] ,
dco_freq ,
p ) ;
/*
* Skip the remaining dividers if we ' re sure to
* have found the definitive divider , we can ' t
* improve a 0 deviation .
*/
if ( ctx . min_deviation = = 0 )
goto skip_remaining_dividers ;
}
}
skip_remaining_dividers :
/*
* If a solution is found with an even divider , prefer
* this one .
*/
if ( d = = 0 & & ctx . p )
break ;
}
if ( ! ctx . p ) {
DRM_DEBUG_DRIVER ( " No valid divider found for %dHz \n " , clock ) ;
return false ;
}
/*
* gcc incorrectly analyses that these can be used without being
* initialized . To be fair , it ' s hard to guess .
*/
p0 = p1 = p2 = 0 ;
skl_wrpll_get_multipliers ( ctx . p , & p0 , & p1 , & p2 ) ;
skl_wrpll_params_populate ( wrpll_params , afe_clock , ctx . central_freq ,
p0 , p1 , p2 ) ;
return true ;
}
2016-03-08 17:46:22 +02:00
static struct intel_shared_dpll *
2016-03-08 17:46:23 +02:00
skl_get_dpll ( struct intel_crtc * crtc , struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder )
2016-03-08 17:46:22 +02:00
{
struct intel_shared_dpll * pll ;
2016-03-08 17:46:24 +02:00
uint32_t ctrl1 , cfgcr1 , cfgcr2 ;
int clock = crtc_state - > port_clock ;
/*
* See comment in intel_dpll_hw_state to understand why we always use 0
* as the DPLL id in this function .
*/
ctrl1 = DPLL_CTRL1_OVERRIDE ( 0 ) ;
if ( encoder - > type = = INTEL_OUTPUT_HDMI ) {
struct skl_wrpll_params wrpll_params = { 0 , } ;
ctrl1 | = DPLL_CTRL1_HDMI_MODE ( 0 ) ;
if ( ! skl_ddi_calculate_wrpll ( clock * 1000 , & wrpll_params ) )
2016-03-16 18:06:58 +02:00
return NULL ;
2016-03-08 17:46:24 +02:00
cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE |
DPLL_CFGCR1_DCO_FRACTION ( wrpll_params . dco_fraction ) |
wrpll_params . dco_integer ;
cfgcr2 = DPLL_CFGCR2_QDIV_RATIO ( wrpll_params . qdiv_ratio ) |
DPLL_CFGCR2_QDIV_MODE ( wrpll_params . qdiv_mode ) |
DPLL_CFGCR2_KDIV ( wrpll_params . kdiv ) |
DPLL_CFGCR2_PDIV ( wrpll_params . pdiv ) |
wrpll_params . central_freq ;
} else if ( encoder - > type = = INTEL_OUTPUT_DISPLAYPORT | |
2016-03-08 17:46:27 +02:00
encoder - > type = = INTEL_OUTPUT_DP_MST | |
encoder - > type = = INTEL_OUTPUT_EDP ) {
2016-03-08 17:46:24 +02:00
switch ( crtc_state - > port_clock / 2 ) {
case 81000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_810 , 0 ) ;
break ;
case 135000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_1350 , 0 ) ;
break ;
case 270000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_2700 , 0 ) ;
break ;
2016-03-08 17:46:27 +02:00
/* eDP 1.4 rates */
case 162000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_1620 , 0 ) ;
break ;
case 108000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_1080 , 0 ) ;
break ;
case 216000 :
ctrl1 | = DPLL_CTRL1_LINK_RATE ( DPLL_CTRL1_LINK_RATE_2160 , 0 ) ;
break ;
2016-03-08 17:46:24 +02:00
}
cfgcr1 = cfgcr2 = 0 ;
} else {
return NULL ;
}
memset ( & crtc_state - > dpll_hw_state , 0 ,
sizeof ( crtc_state - > dpll_hw_state ) ) ;
crtc_state - > dpll_hw_state . ctrl1 = ctrl1 ;
crtc_state - > dpll_hw_state . cfgcr1 = cfgcr1 ;
crtc_state - > dpll_hw_state . cfgcr2 = cfgcr2 ;
2016-03-08 17:46:22 +02:00
2016-03-08 17:46:27 +02:00
if ( encoder - > type = = INTEL_OUTPUT_EDP )
pll = intel_find_shared_dpll ( crtc , crtc_state ,
DPLL_ID_SKL_DPLL0 ,
DPLL_ID_SKL_DPLL0 ) ;
else
pll = intel_find_shared_dpll ( crtc , crtc_state ,
DPLL_ID_SKL_DPLL1 ,
DPLL_ID_SKL_DPLL3 ) ;
2016-03-08 17:46:24 +02:00
if ( ! pll )
return NULL ;
2016-03-08 17:46:27 +02:00
crtc_state - > ddi_pll_sel = pll - > id ;
2016-03-08 17:46:24 +02:00
intel_reference_shared_dpll ( pll , crtc_state ) ;
2016-03-08 17:46:22 +02:00
return pll ;
}
2016-03-08 17:46:21 +02:00
static const struct intel_shared_dpll_funcs skl_ddi_pll_funcs = {
. enable = skl_ddi_pll_enable ,
. disable = skl_ddi_pll_disable ,
. get_hw_state = skl_ddi_pll_get_hw_state ,
2016-03-08 17:46:16 +02:00
} ;
2016-03-08 17:46:27 +02:00
static const struct intel_shared_dpll_funcs skl_ddi_dpll0_funcs = {
. enable = skl_ddi_dpll0_enable ,
. disable = skl_ddi_dpll0_disable ,
. get_hw_state = skl_ddi_dpll0_get_hw_state ,
} ;
2016-03-08 17:46:16 +02:00
static void bxt_ddi_pll_enable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
uint32_t temp ;
enum port port = ( enum port ) pll - > id ; /* 1:1 port->PLL mapping */
/* Non-SSC reference */
2016-04-14 15:37:43 -07:00
temp = I915_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
temp | = PORT_PLL_REF_SEL ;
2016-03-08 17:46:16 +02:00
I915_WRITE ( BXT_PORT_PLL_ENABLE ( port ) , temp ) ;
/* Disable 10 bit clock */
temp = I915_READ ( BXT_PORT_PLL_EBB_4 ( port ) ) ;
temp & = ~ PORT_PLL_10BIT_CLK_ENABLE ;
I915_WRITE ( BXT_PORT_PLL_EBB_4 ( port ) , temp ) ;
/* Write P1 & P2 */
temp = I915_READ ( BXT_PORT_PLL_EBB_0 ( port ) ) ;
temp & = ~ ( PORT_PLL_P1_MASK | PORT_PLL_P2_MASK ) ;
temp | = pll - > config . hw_state . ebb0 ;
I915_WRITE ( BXT_PORT_PLL_EBB_0 ( port ) , temp ) ;
/* Write M2 integer */
temp = I915_READ ( BXT_PORT_PLL ( port , 0 ) ) ;
temp & = ~ PORT_PLL_M2_MASK ;
temp | = pll - > config . hw_state . pll0 ;
I915_WRITE ( BXT_PORT_PLL ( port , 0 ) , temp ) ;
/* Write N */
temp = I915_READ ( BXT_PORT_PLL ( port , 1 ) ) ;
temp & = ~ PORT_PLL_N_MASK ;
temp | = pll - > config . hw_state . pll1 ;
I915_WRITE ( BXT_PORT_PLL ( port , 1 ) , temp ) ;
/* Write M2 fraction */
temp = I915_READ ( BXT_PORT_PLL ( port , 2 ) ) ;
temp & = ~ PORT_PLL_M2_FRAC_MASK ;
temp | = pll - > config . hw_state . pll2 ;
I915_WRITE ( BXT_PORT_PLL ( port , 2 ) , temp ) ;
/* Write M2 fraction enable */
temp = I915_READ ( BXT_PORT_PLL ( port , 3 ) ) ;
temp & = ~ PORT_PLL_M2_FRAC_ENABLE ;
temp | = pll - > config . hw_state . pll3 ;
I915_WRITE ( BXT_PORT_PLL ( port , 3 ) , temp ) ;
/* Write coeff */
temp = I915_READ ( BXT_PORT_PLL ( port , 6 ) ) ;
temp & = ~ PORT_PLL_PROP_COEFF_MASK ;
temp & = ~ PORT_PLL_INT_COEFF_MASK ;
temp & = ~ PORT_PLL_GAIN_CTL_MASK ;
temp | = pll - > config . hw_state . pll6 ;
I915_WRITE ( BXT_PORT_PLL ( port , 6 ) , temp ) ;
/* Write calibration val */
temp = I915_READ ( BXT_PORT_PLL ( port , 8 ) ) ;
temp & = ~ PORT_PLL_TARGET_CNT_MASK ;
temp | = pll - > config . hw_state . pll8 ;
I915_WRITE ( BXT_PORT_PLL ( port , 8 ) , temp ) ;
temp = I915_READ ( BXT_PORT_PLL ( port , 9 ) ) ;
temp & = ~ PORT_PLL_LOCK_THRESHOLD_MASK ;
temp | = pll - > config . hw_state . pll9 ;
I915_WRITE ( BXT_PORT_PLL ( port , 9 ) , temp ) ;
temp = I915_READ ( BXT_PORT_PLL ( port , 10 ) ) ;
temp & = ~ PORT_PLL_DCO_AMP_OVR_EN_H ;
temp & = ~ PORT_PLL_DCO_AMP_MASK ;
temp | = pll - > config . hw_state . pll10 ;
I915_WRITE ( BXT_PORT_PLL ( port , 10 ) , temp ) ;
/* Recalibrate with new settings */
temp = I915_READ ( BXT_PORT_PLL_EBB_4 ( port ) ) ;
temp | = PORT_PLL_RECALIBRATE ;
I915_WRITE ( BXT_PORT_PLL_EBB_4 ( port ) , temp ) ;
temp & = ~ PORT_PLL_10BIT_CLK_ENABLE ;
temp | = pll - > config . hw_state . ebb4 ;
I915_WRITE ( BXT_PORT_PLL_EBB_4 ( port ) , temp ) ;
/* Enable PLL */
temp = I915_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
temp | = PORT_PLL_ENABLE ;
I915_WRITE ( BXT_PORT_PLL_ENABLE ( port ) , temp ) ;
POSTING_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
2016-06-28 13:37:30 +03:00
if ( wait_for_us ( ( I915_READ ( BXT_PORT_PLL_ENABLE ( port ) ) & PORT_PLL_LOCK ) ,
200 ) )
2016-03-08 17:46:16 +02:00
DRM_ERROR ( " PLL %d not locked \n " , port ) ;
/*
* While we write to the group register to program all lanes at once we
* can read only lane registers and we pick lanes 0 / 1 for that .
*/
temp = I915_READ ( BXT_PORT_PCS_DW12_LN01 ( port ) ) ;
temp & = ~ LANE_STAGGER_MASK ;
temp & = ~ LANESTAGGER_STRAP_OVRD ;
temp | = pll - > config . hw_state . pcsdw12 ;
I915_WRITE ( BXT_PORT_PCS_DW12_GRP ( port ) , temp ) ;
}
static void bxt_ddi_pll_disable ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll )
{
enum port port = ( enum port ) pll - > id ; /* 1:1 port->PLL mapping */
uint32_t temp ;
temp = I915_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
temp & = ~ PORT_PLL_ENABLE ;
I915_WRITE ( BXT_PORT_PLL_ENABLE ( port ) , temp ) ;
POSTING_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
}
static bool bxt_ddi_pll_get_hw_state ( struct drm_i915_private * dev_priv ,
struct intel_shared_dpll * pll ,
struct intel_dpll_hw_state * hw_state )
{
enum port port = ( enum port ) pll - > id ; /* 1:1 port->PLL mapping */
uint32_t val ;
bool ret ;
if ( ! intel_display_power_get_if_enabled ( dev_priv , POWER_DOMAIN_PLLS ) )
return false ;
ret = false ;
val = I915_READ ( BXT_PORT_PLL_ENABLE ( port ) ) ;
if ( ! ( val & PORT_PLL_ENABLE ) )
goto out ;
hw_state - > ebb0 = I915_READ ( BXT_PORT_PLL_EBB_0 ( port ) ) ;
hw_state - > ebb0 & = PORT_PLL_P1_MASK | PORT_PLL_P2_MASK ;
hw_state - > ebb4 = I915_READ ( BXT_PORT_PLL_EBB_4 ( port ) ) ;
hw_state - > ebb4 & = PORT_PLL_10BIT_CLK_ENABLE ;
hw_state - > pll0 = I915_READ ( BXT_PORT_PLL ( port , 0 ) ) ;
hw_state - > pll0 & = PORT_PLL_M2_MASK ;
hw_state - > pll1 = I915_READ ( BXT_PORT_PLL ( port , 1 ) ) ;
hw_state - > pll1 & = PORT_PLL_N_MASK ;
hw_state - > pll2 = I915_READ ( BXT_PORT_PLL ( port , 2 ) ) ;
hw_state - > pll2 & = PORT_PLL_M2_FRAC_MASK ;
hw_state - > pll3 = I915_READ ( BXT_PORT_PLL ( port , 3 ) ) ;
hw_state - > pll3 & = PORT_PLL_M2_FRAC_ENABLE ;
hw_state - > pll6 = I915_READ ( BXT_PORT_PLL ( port , 6 ) ) ;
hw_state - > pll6 & = PORT_PLL_PROP_COEFF_MASK |
PORT_PLL_INT_COEFF_MASK |
PORT_PLL_GAIN_CTL_MASK ;
hw_state - > pll8 = I915_READ ( BXT_PORT_PLL ( port , 8 ) ) ;
hw_state - > pll8 & = PORT_PLL_TARGET_CNT_MASK ;
hw_state - > pll9 = I915_READ ( BXT_PORT_PLL ( port , 9 ) ) ;
hw_state - > pll9 & = PORT_PLL_LOCK_THRESHOLD_MASK ;
hw_state - > pll10 = I915_READ ( BXT_PORT_PLL ( port , 10 ) ) ;
hw_state - > pll10 & = PORT_PLL_DCO_AMP_OVR_EN_H |
PORT_PLL_DCO_AMP_MASK ;
/*
* While we write to the group register to program all lanes at once we
* can read only lane registers . We configure all lanes the same way , so
* here just read out lanes 0 / 1 and output a note if lanes 2 / 3 differ .
*/
hw_state - > pcsdw12 = I915_READ ( BXT_PORT_PCS_DW12_LN01 ( port ) ) ;
if ( I915_READ ( BXT_PORT_PCS_DW12_LN23 ( port ) ) ! = hw_state - > pcsdw12 )
DRM_DEBUG_DRIVER ( " lane stagger config different for lane 01 (%08x) and 23 (%08x) \n " ,
hw_state - > pcsdw12 ,
I915_READ ( BXT_PORT_PCS_DW12_LN23 ( port ) ) ) ;
hw_state - > pcsdw12 & = LANE_STAGGER_MASK | LANESTAGGER_STRAP_OVRD ;
ret = true ;
out :
intel_display_power_put ( dev_priv , POWER_DOMAIN_PLLS ) ;
return ret ;
}
2016-03-08 17:46:25 +02:00
/* bxt clock parameters */
struct bxt_clk_div {
int clock ;
uint32_t p1 ;
uint32_t p2 ;
uint32_t m2_int ;
uint32_t m2_frac ;
bool m2_frac_en ;
uint32_t n ;
} ;
/* pre-calculated values for DP linkrates */
static const struct bxt_clk_div bxt_dp_clk_val [ ] = {
{ 162000 , 4 , 2 , 32 , 1677722 , 1 , 1 } ,
{ 270000 , 4 , 1 , 27 , 0 , 0 , 1 } ,
{ 540000 , 2 , 1 , 27 , 0 , 0 , 1 } ,
{ 216000 , 3 , 2 , 32 , 1677722 , 1 , 1 } ,
{ 243000 , 4 , 1 , 24 , 1258291 , 1 , 1 } ,
{ 324000 , 4 , 1 , 32 , 1677722 , 1 , 1 } ,
{ 432000 , 3 , 1 , 32 , 1677722 , 1 , 1 }
} ;
2016-03-08 17:46:22 +02:00
static struct intel_shared_dpll *
2016-03-08 17:46:23 +02:00
bxt_get_dpll ( struct intel_crtc * crtc , struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder )
2016-03-08 17:46:22 +02:00
{
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
struct intel_shared_dpll * pll ;
enum intel_dpll_id i ;
2016-03-08 17:46:25 +02:00
struct intel_digital_port * intel_dig_port ;
struct bxt_clk_div clk_div = { 0 } ;
int vco = 0 ;
uint32_t prop_coef , int_coef , gain_ctl , targ_cnt ;
uint32_t lanestagger ;
int clock = crtc_state - > port_clock ;
2016-03-08 17:46:22 +02:00
2016-03-08 17:46:25 +02:00
if ( encoder - > type = = INTEL_OUTPUT_HDMI ) {
2016-05-04 12:11:57 +03:00
struct dpll best_clock ;
2016-03-08 17:46:25 +02:00
/* Calculate HDMI div */
/*
* FIXME : tie the following calculation into
* i9xx_crtc_compute_clock
*/
if ( ! bxt_find_best_dpll ( crtc_state , clock , & best_clock ) ) {
DRM_DEBUG_DRIVER ( " no PLL dividers found for clock %d pipe %c \n " ,
clock , pipe_name ( crtc - > pipe ) ) ;
return NULL ;
}
clk_div . p1 = best_clock . p1 ;
clk_div . p2 = best_clock . p2 ;
WARN_ON ( best_clock . m1 ! = 2 ) ;
clk_div . n = best_clock . n ;
clk_div . m2_int = best_clock . m2 > > 22 ;
clk_div . m2_frac = best_clock . m2 & ( ( 1 < < 22 ) - 1 ) ;
clk_div . m2_frac_en = clk_div . m2_frac ! = 0 ;
vco = best_clock . vco ;
} else if ( encoder - > type = = INTEL_OUTPUT_DISPLAYPORT | |
encoder - > type = = INTEL_OUTPUT_EDP ) {
int i ;
clk_div = bxt_dp_clk_val [ 0 ] ;
for ( i = 0 ; i < ARRAY_SIZE ( bxt_dp_clk_val ) ; + + i ) {
if ( bxt_dp_clk_val [ i ] . clock = = clock ) {
clk_div = bxt_dp_clk_val [ i ] ;
break ;
}
}
vco = clock * 10 / 2 * clk_div . p1 * clk_div . p2 ;
}
if ( vco > = 6200000 & & vco < = 6700000 ) {
prop_coef = 4 ;
int_coef = 9 ;
gain_ctl = 3 ;
targ_cnt = 8 ;
} else if ( ( vco > 5400000 & & vco < 6200000 ) | |
( vco > = 4800000 & & vco < 5400000 ) ) {
prop_coef = 5 ;
int_coef = 11 ;
gain_ctl = 3 ;
targ_cnt = 9 ;
} else if ( vco = = 5400000 ) {
prop_coef = 3 ;
int_coef = 8 ;
gain_ctl = 1 ;
targ_cnt = 9 ;
} else {
DRM_ERROR ( " Invalid VCO \n " ) ;
2016-03-08 17:46:22 +02:00
return NULL ;
2016-03-08 17:46:25 +02:00
}
memset ( & crtc_state - > dpll_hw_state , 0 ,
sizeof ( crtc_state - > dpll_hw_state ) ) ;
if ( clock > 270000 )
lanestagger = 0x18 ;
else if ( clock > 135000 )
lanestagger = 0x0d ;
else if ( clock > 67000 )
lanestagger = 0x07 ;
else if ( clock > 33000 )
lanestagger = 0x04 ;
else
lanestagger = 0x02 ;
crtc_state - > dpll_hw_state . ebb0 =
PORT_PLL_P1 ( clk_div . p1 ) | PORT_PLL_P2 ( clk_div . p2 ) ;
crtc_state - > dpll_hw_state . pll0 = clk_div . m2_int ;
crtc_state - > dpll_hw_state . pll1 = PORT_PLL_N ( clk_div . n ) ;
crtc_state - > dpll_hw_state . pll2 = clk_div . m2_frac ;
if ( clk_div . m2_frac_en )
crtc_state - > dpll_hw_state . pll3 =
PORT_PLL_M2_FRAC_ENABLE ;
crtc_state - > dpll_hw_state . pll6 =
prop_coef | PORT_PLL_INT_COEFF ( int_coef ) ;
crtc_state - > dpll_hw_state . pll6 | =
PORT_PLL_GAIN_CTL ( gain_ctl ) ;
crtc_state - > dpll_hw_state . pll8 = targ_cnt ;
crtc_state - > dpll_hw_state . pll9 = 5 < < PORT_PLL_LOCK_THRESHOLD_SHIFT ;
crtc_state - > dpll_hw_state . pll10 =
PORT_PLL_DCO_AMP ( PORT_PLL_DCO_AMP_DEFAULT )
| PORT_PLL_DCO_AMP_OVR_EN_H ;
crtc_state - > dpll_hw_state . ebb4 = PORT_PLL_10BIT_CLK_ENABLE ;
crtc_state - > dpll_hw_state . pcsdw12 =
LANESTAGGER_STRAP_OVRD | lanestagger ;
2016-03-08 17:46:22 +02:00
intel_dig_port = enc_to_dig_port ( & encoder - > base ) ;
2016-03-08 17:46:25 +02:00
2016-03-08 17:46:22 +02:00
/* 1:1 mapping between ports and PLLs */
2016-03-08 17:46:25 +02:00
i = ( enum intel_dpll_id ) intel_dig_port - > port ;
pll = intel_get_shared_dpll_by_id ( dev_priv , i ) ;
2016-05-27 20:59:19 +03:00
DRM_DEBUG_KMS ( " [CRTC:%d:%s] using pre-allocated %s \n " ,
crtc - > base . base . id , crtc - > base . name , pll - > name ) ;
2016-03-08 17:46:22 +02:00
intel_reference_shared_dpll ( pll , crtc_state ) ;
2016-03-08 17:46:25 +02:00
/* shared DPLL id 0 is DPLL A */
crtc_state - > ddi_pll_sel = pll - > id ;
2016-03-08 17:46:22 +02:00
return pll ;
}
2016-03-08 17:46:21 +02:00
static const struct intel_shared_dpll_funcs bxt_ddi_pll_funcs = {
. enable = bxt_ddi_pll_enable ,
. disable = bxt_ddi_pll_disable ,
. get_hw_state = bxt_ddi_pll_get_hw_state ,
} ;
2016-03-08 17:46:16 +02:00
static void intel_ddi_pll_init ( struct drm_device * dev )
{
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( dev ) ;
2016-03-08 17:46:16 +02:00
2016-05-13 23:41:29 +03:00
if ( INTEL_GEN ( dev_priv ) < 9 ) {
uint32_t val = I915_READ ( LCPLL_CTL ) ;
2016-05-13 23:41:27 +03:00
2016-03-08 17:46:16 +02:00
/*
* The LCPLL register should be turned on by the BIOS . For now
* let ' s just check its state and print errors in case
* something is wrong . Don ' t even try to turn it on .
*/
if ( val & LCPLL_CD_SOURCE_FCLK )
DRM_ERROR ( " CDCLK source is not LCPLL \n " ) ;
if ( val & LCPLL_PLL_DISABLE )
DRM_ERROR ( " LCPLL is disabled \n " ) ;
}
}
2016-03-08 17:46:21 +02:00
struct dpll_info {
const char * name ;
const int id ;
const struct intel_shared_dpll_funcs * funcs ;
2016-03-08 17:46:26 +02:00
uint32_t flags ;
2016-03-08 17:46:21 +02:00
} ;
2016-03-08 17:46:22 +02:00
struct intel_dpll_mgr {
const struct dpll_info * dpll_info ;
struct intel_shared_dpll * ( * get_dpll ) ( struct intel_crtc * crtc ,
2016-03-08 17:46:23 +02:00
struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder ) ;
2016-03-08 17:46:22 +02:00
} ;
2016-03-08 17:46:21 +02:00
static const struct dpll_info pch_plls [ ] = {
2016-03-08 17:46:26 +02:00
{ " PCH DPLL A " , DPLL_ID_PCH_PLL_A , & ibx_pch_dpll_funcs , 0 } ,
{ " PCH DPLL B " , DPLL_ID_PCH_PLL_B , & ibx_pch_dpll_funcs , 0 } ,
{ NULL , - 1 , NULL , 0 } ,
2016-03-08 17:46:21 +02:00
} ;
2016-03-08 17:46:22 +02:00
static const struct intel_dpll_mgr pch_pll_mgr = {
. dpll_info = pch_plls ,
. get_dpll = ibx_get_dpll ,
} ;
2016-03-08 17:46:21 +02:00
static const struct dpll_info hsw_plls [ ] = {
2016-03-08 17:46:26 +02:00
{ " WRPLL 1 " , DPLL_ID_WRPLL1 , & hsw_ddi_wrpll_funcs , 0 } ,
{ " WRPLL 2 " , DPLL_ID_WRPLL2 , & hsw_ddi_wrpll_funcs , 0 } ,
{ " SPLL " , DPLL_ID_SPLL , & hsw_ddi_spll_funcs , 0 } ,
{ " LCPLL 810 " , DPLL_ID_LCPLL_810 , & hsw_ddi_lcpll_funcs , INTEL_DPLL_ALWAYS_ON } ,
{ " LCPLL 1350 " , DPLL_ID_LCPLL_1350 , & hsw_ddi_lcpll_funcs , INTEL_DPLL_ALWAYS_ON } ,
{ " LCPLL 2700 " , DPLL_ID_LCPLL_2700 , & hsw_ddi_lcpll_funcs , INTEL_DPLL_ALWAYS_ON } ,
2016-03-08 17:46:21 +02:00
{ NULL , - 1 , NULL , } ,
} ;
2016-03-08 17:46:22 +02:00
static const struct intel_dpll_mgr hsw_pll_mgr = {
. dpll_info = hsw_plls ,
. get_dpll = hsw_get_dpll ,
} ;
2016-03-08 17:46:21 +02:00
static const struct dpll_info skl_plls [ ] = {
2016-03-08 17:46:27 +02:00
{ " DPLL 0 " , DPLL_ID_SKL_DPLL0 , & skl_ddi_dpll0_funcs , INTEL_DPLL_ALWAYS_ON } ,
2016-05-11 22:04:33 +03:00
{ " DPLL 1 " , DPLL_ID_SKL_DPLL1 , & skl_ddi_pll_funcs , 0 } ,
{ " DPLL 2 " , DPLL_ID_SKL_DPLL2 , & skl_ddi_pll_funcs , 0 } ,
{ " DPLL 3 " , DPLL_ID_SKL_DPLL3 , & skl_ddi_pll_funcs , 0 } ,
2016-03-08 17:46:21 +02:00
{ NULL , - 1 , NULL , } ,
} ;
2016-03-08 17:46:22 +02:00
static const struct intel_dpll_mgr skl_pll_mgr = {
. dpll_info = skl_plls ,
. get_dpll = skl_get_dpll ,
} ;
2016-03-08 17:46:21 +02:00
static const struct dpll_info bxt_plls [ ] = {
2016-03-14 19:55:34 +02:00
{ " PORT PLL A " , DPLL_ID_SKL_DPLL0 , & bxt_ddi_pll_funcs , 0 } ,
{ " PORT PLL B " , DPLL_ID_SKL_DPLL1 , & bxt_ddi_pll_funcs , 0 } ,
{ " PORT PLL C " , DPLL_ID_SKL_DPLL2 , & bxt_ddi_pll_funcs , 0 } ,
2016-03-08 17:46:21 +02:00
{ NULL , - 1 , NULL , } ,
} ;
2016-03-08 17:46:22 +02:00
static const struct intel_dpll_mgr bxt_pll_mgr = {
. dpll_info = bxt_plls ,
. get_dpll = bxt_get_dpll ,
} ;
2016-03-08 17:46:15 +02:00
void intel_shared_dpll_init ( struct drm_device * dev )
{
2016-07-04 11:34:36 +01:00
struct drm_i915_private * dev_priv = to_i915 ( dev ) ;
2016-03-08 17:46:22 +02:00
const struct intel_dpll_mgr * dpll_mgr = NULL ;
const struct dpll_info * dpll_info ;
2016-03-08 17:46:21 +02:00
int i ;
2016-03-08 17:46:15 +02:00
2016-03-08 17:46:21 +02:00
if ( IS_SKYLAKE ( dev ) | | IS_KABYLAKE ( dev ) )
2016-03-08 17:46:22 +02:00
dpll_mgr = & skl_pll_mgr ;
2016-03-14 19:55:34 +02:00
else if ( IS_BROXTON ( dev ) )
2016-03-08 17:46:22 +02:00
dpll_mgr = & bxt_pll_mgr ;
2016-03-08 17:46:21 +02:00
else if ( HAS_DDI ( dev ) )
2016-03-08 17:46:22 +02:00
dpll_mgr = & hsw_pll_mgr ;
2016-03-08 17:46:15 +02:00
else if ( HAS_PCH_IBX ( dev ) | | HAS_PCH_CPT ( dev ) )
2016-03-08 17:46:22 +02:00
dpll_mgr = & pch_pll_mgr ;
2016-03-08 17:46:21 +02:00
2016-03-08 17:46:22 +02:00
if ( ! dpll_mgr ) {
2016-03-08 17:46:15 +02:00
dev_priv - > num_shared_dpll = 0 ;
2016-03-08 17:46:21 +02:00
return ;
}
2016-03-08 17:46:22 +02:00
dpll_info = dpll_mgr - > dpll_info ;
2016-03-08 17:46:21 +02:00
for ( i = 0 ; dpll_info [ i ] . id > = 0 ; i + + ) {
WARN_ON ( i ! = dpll_info [ i ] . id ) ;
dev_priv - > shared_dplls [ i ] . id = dpll_info [ i ] . id ;
dev_priv - > shared_dplls [ i ] . name = dpll_info [ i ] . name ;
dev_priv - > shared_dplls [ i ] . funcs = * dpll_info [ i ] . funcs ;
2016-03-08 17:46:26 +02:00
dev_priv - > shared_dplls [ i ] . flags = dpll_info [ i ] . flags ;
2016-03-08 17:46:21 +02:00
}
2016-03-08 17:46:22 +02:00
dev_priv - > dpll_mgr = dpll_mgr ;
2016-03-08 17:46:21 +02:00
dev_priv - > num_shared_dpll = i ;
2016-03-23 14:51:12 +01:00
mutex_init ( & dev_priv - > dpll_lock ) ;
2016-03-08 17:46:15 +02:00
BUG_ON ( dev_priv - > num_shared_dpll > I915_NUM_PLLS ) ;
2016-03-08 17:46:21 +02:00
/* FIXME: Move this to a more suitable place */
if ( HAS_DDI ( dev ) )
intel_ddi_pll_init ( dev ) ;
2016-03-08 17:46:15 +02:00
}
2016-03-08 17:46:22 +02:00
struct intel_shared_dpll *
intel_get_shared_dpll ( struct intel_crtc * crtc ,
2016-03-08 17:46:23 +02:00
struct intel_crtc_state * crtc_state ,
struct intel_encoder * encoder )
2016-03-08 17:46:22 +02:00
{
struct drm_i915_private * dev_priv = to_i915 ( crtc - > base . dev ) ;
const struct intel_dpll_mgr * dpll_mgr = dev_priv - > dpll_mgr ;
if ( WARN_ON ( ! dpll_mgr ) )
return NULL ;
2016-03-08 17:46:23 +02:00
return dpll_mgr - > get_dpll ( crtc , crtc_state , encoder ) ;
2016-03-08 17:46:22 +02:00
}