2019-05-29 16:57:49 -07:00
// SPDX-License-Identifier: GPL-2.0-only
2011-11-03 18:22:37 +00:00
/*
* Copyright © 2006 - 2011 Intel Corporation
*
* Authors :
* Eric Anholt < eric @ anholt . net >
*/
2019-05-19 21:55:26 +02:00
# include <linux/delay.h>
2011-11-03 18:22:37 +00:00
# include <linux/i2c.h>
2019-05-19 21:55:26 +02:00
# include <drm/drm_crtc.h>
2023-01-16 14:12:21 +01:00
# include <drm/drm_modeset_helper_vtables.h>
2019-05-19 21:55:26 +02:00
# include "cdv_device.h"
2011-11-03 18:22:37 +00:00
# include "framebuffer.h"
2019-05-19 21:55:26 +02:00
# include "gma_display.h"
# include "power.h"
2011-11-03 18:22:37 +00:00
# include "psb_drv.h"
# include "psb_intel_drv.h"
# include "psb_intel_reg.h"
2013-07-01 01:42:16 +02:00
static bool cdv_intel_find_dp_pll ( const struct gma_limit_t * limit ,
struct drm_crtc * crtc , int target ,
int refclk , struct gma_clock_t * best_clock ) ;
2011-11-03 18:22:37 +00:00
2012-08-08 13:53:48 +00:00
2011-11-03 18:22:37 +00:00
# define CDV_LIMIT_SINGLE_LVDS_96 0
# define CDV_LIMIT_SINGLE_LVDS_100 1
# define CDV_LIMIT_DAC_HDMI_27 2
# define CDV_LIMIT_DAC_HDMI_96 3
2012-08-08 13:54:41 +00:00
# define CDV_LIMIT_DP_27 4
# define CDV_LIMIT_DP_100 5
2011-11-03 18:22:37 +00:00
2013-07-01 01:42:16 +02:00
static const struct gma_limit_t cdv_intel_limits [ ] = {
2012-08-13 16:27:09 +00:00
{ /* CDV_SINGLE_LVDS_96MHz */
2011-11-03 18:22:37 +00:00
. dot = { . min = 20000 , . max = 115500 } ,
. vco = { . min = 1800000 , . max = 3600000 } ,
. n = { . min = 2 , . max = 6 } ,
. m = { . min = 60 , . max = 160 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 58 , . max = 158 } ,
. p = { . min = 28 , . max = 140 } ,
. p1 = { . min = 2 , . max = 10 } ,
2013-07-01 01:42:16 +02:00
. p2 = { . dot_limit = 200000 , . p2_slow = 14 , . p2_fast = 14 } ,
. find_pll = gma_find_best_pll ,
2011-11-03 18:22:37 +00:00
} ,
{ /* CDV_SINGLE_LVDS_100MHz */
. dot = { . min = 20000 , . max = 115500 } ,
. vco = { . min = 1800000 , . max = 3600000 } ,
. n = { . min = 2 , . max = 6 } ,
. m = { . min = 60 , . max = 160 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 58 , . max = 158 } ,
. p = { . min = 28 , . max = 140 } ,
. p1 = { . min = 2 , . max = 10 } ,
/* The single-channel range is 25-112Mhz, and dual-channel
* is 80 - 224 Mhz . Prefer single channel as much as possible .
*/
. p2 = { . dot_limit = 200000 , . p2_slow = 14 , . p2_fast = 14 } ,
2013-07-01 01:42:16 +02:00
. find_pll = gma_find_best_pll ,
2011-11-03 18:22:37 +00:00
} ,
{ /* CDV_DAC_HDMI_27MHz */
. dot = { . min = 20000 , . max = 400000 } ,
. vco = { . min = 1809000 , . max = 3564000 } ,
. n = { . min = 1 , . max = 1 } ,
. m = { . min = 67 , . max = 132 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 65 , . max = 130 } ,
. p = { . min = 5 , . max = 90 } ,
. p1 = { . min = 1 , . max = 9 } ,
. p2 = { . dot_limit = 225000 , . p2_slow = 10 , . p2_fast = 5 } ,
2013-07-01 01:42:16 +02:00
. find_pll = gma_find_best_pll ,
2011-11-03 18:22:37 +00:00
} ,
{ /* CDV_DAC_HDMI_96MHz */
. dot = { . min = 20000 , . max = 400000 } ,
. vco = { . min = 1800000 , . max = 3600000 } ,
. n = { . min = 2 , . max = 6 } ,
. m = { . min = 60 , . max = 160 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 58 , . max = 158 } ,
. p = { . min = 5 , . max = 100 } ,
. p1 = { . min = 1 , . max = 10 } ,
. p2 = { . dot_limit = 225000 , . p2_slow = 10 , . p2_fast = 5 } ,
2013-07-01 01:42:16 +02:00
. find_pll = gma_find_best_pll ,
2011-11-03 18:22:37 +00:00
} ,
2012-08-08 13:54:41 +00:00
{ /* CDV_DP_27MHz */
. dot = { . min = 160000 , . max = 272000 } ,
. vco = { . min = 1809000 , . max = 3564000 } ,
. n = { . min = 1 , . max = 1 } ,
. m = { . min = 67 , . max = 132 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 65 , . max = 130 } ,
. p = { . min = 5 , . max = 90 } ,
. p1 = { . min = 1 , . max = 9 } ,
. p2 = { . dot_limit = 225000 , . p2_slow = 10 , . p2_fast = 10 } ,
. find_pll = cdv_intel_find_dp_pll ,
} ,
{ /* CDV_DP_100MHz */
. dot = { . min = 160000 , . max = 272000 } ,
. vco = { . min = 1800000 , . max = 3600000 } ,
. n = { . min = 2 , . max = 6 } ,
. m = { . min = 60 , . max = 164 } ,
. m1 = { . min = 0 , . max = 0 } ,
. m2 = { . min = 58 , . max = 162 } ,
. p = { . min = 5 , . max = 100 } ,
. p1 = { . min = 1 , . max = 10 } ,
. p2 = { . dot_limit = 225000 , . p2_slow = 10 , . p2_fast = 10 } ,
. find_pll = cdv_intel_find_dp_pll ,
2016-02-16 14:17:45 +00:00
}
2011-11-03 18:22:37 +00:00
} ;
# define _wait_for(COND, MS, W) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies ( MS ) ; \
int ret__ = 0 ; \
while ( ! ( COND ) ) { \
if ( time_after ( jiffies , timeout__ ) ) { \
ret__ = - ETIMEDOUT ; \
break ; \
} \
if ( W & & ! in_dbg_master ( ) ) \
msleep ( W ) ; \
} \
ret__ ; \
} )
# define wait_for(COND, MS) _wait_for(COND, MS, 1)
2012-08-08 13:55:03 +00:00
int cdv_sb_read ( struct drm_device * dev , u32 reg , u32 * val )
2011-11-03 18:22:37 +00:00
{
int ret ;
ret = wait_for ( ( REG_READ ( SB_PCKT ) & SB_BUSY ) = = 0 , 1000 ) ;
if ( ret ) {
DRM_ERROR ( " timeout waiting for SB to idle before read \n " ) ;
return ret ;
}
REG_WRITE ( SB_ADDR , reg ) ;
REG_WRITE ( SB_PCKT ,
SET_FIELD ( SB_OPCODE_READ , SB_OPCODE ) |
SET_FIELD ( SB_DEST_DPLL , SB_DEST ) |
SET_FIELD ( 0xf , SB_BYTE_ENABLE ) ) ;
ret = wait_for ( ( REG_READ ( SB_PCKT ) & SB_BUSY ) = = 0 , 1000 ) ;
if ( ret ) {
DRM_ERROR ( " timeout waiting for SB to idle after read \n " ) ;
return ret ;
}
* val = REG_READ ( SB_DATA ) ;
return 0 ;
}
2012-08-08 13:55:03 +00:00
int cdv_sb_write ( struct drm_device * dev , u32 reg , u32 val )
2011-11-03 18:22:37 +00:00
{
int ret ;
static bool dpio_debug = true ;
u32 temp ;
if ( dpio_debug ) {
if ( cdv_sb_read ( dev , reg , & temp ) = = 0 )
DRM_DEBUG_KMS ( " 0x%08x: 0x%08x (before) \n " , reg , temp ) ;
DRM_DEBUG_KMS ( " 0x%08x: 0x%08x \n " , reg , val ) ;
}
ret = wait_for ( ( REG_READ ( SB_PCKT ) & SB_BUSY ) = = 0 , 1000 ) ;
if ( ret ) {
DRM_ERROR ( " timeout waiting for SB to idle before write \n " ) ;
return ret ;
}
REG_WRITE ( SB_ADDR , reg ) ;
REG_WRITE ( SB_DATA , val ) ;
REG_WRITE ( SB_PCKT ,
SET_FIELD ( SB_OPCODE_WRITE , SB_OPCODE ) |
SET_FIELD ( SB_DEST_DPLL , SB_DEST ) |
SET_FIELD ( 0xf , SB_BYTE_ENABLE ) ) ;
ret = wait_for ( ( REG_READ ( SB_PCKT ) & SB_BUSY ) = = 0 , 1000 ) ;
if ( ret ) {
DRM_ERROR ( " timeout waiting for SB to idle after write \n " ) ;
return ret ;
}
if ( dpio_debug ) {
if ( cdv_sb_read ( dev , reg , & temp ) = = 0 )
DRM_DEBUG_KMS ( " 0x%08x: 0x%08x (after) \n " , reg , temp ) ;
}
return 0 ;
}
/* Reset the DPIO configuration register. The BIOS does this at every
* mode set .
*/
2012-08-08 13:55:03 +00:00
void cdv_sb_reset ( struct drm_device * dev )
2011-11-03 18:22:37 +00:00
{
REG_WRITE ( DPIO_CFG , 0 ) ;
REG_READ ( DPIO_CFG ) ;
REG_WRITE ( DPIO_CFG , DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N ) ;
}
/* Unlike most Intel display engines, on Cedarview the DPLL registers
* are behind this sideband bus . They must be programmed while the
* DPLL reference clock is on in the DPLL control register , but before
* the DPLL is enabled in the DPLL control register .
*/
static int
cdv_dpll_set_clock_cdv ( struct drm_device * dev , struct drm_crtc * crtc ,
2013-07-01 01:42:16 +02:00
struct gma_clock_t * clock , bool is_lvds , u32 ddi_select )
2011-11-03 18:22:37 +00:00
{
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
int pipe = gma_crtc - > pipe ;
2011-11-03 18:22:37 +00:00
u32 m , n_vco , p ;
int ret = 0 ;
int dpll_reg = ( pipe = = 0 ) ? DPLL_A : DPLL_B ;
2012-04-25 14:36:48 +01:00
int ref_sfr = ( pipe = = 0 ) ? SB_REF_DPLLA : SB_REF_DPLLB ;
2011-11-03 18:22:37 +00:00
u32 ref_value ;
2012-04-25 14:38:07 +01:00
u32 lane_reg , lane_value ;
2011-11-03 18:22:37 +00:00
cdv_sb_reset ( dev ) ;
2012-04-25 14:38:07 +01:00
REG_WRITE ( dpll_reg , DPLL_SYNCLOCK_ENABLE | DPLL_VGA_MODE_DIS ) ;
udelay ( 100 ) ;
2011-11-03 18:22:37 +00:00
/* Follow the BIOS and write the REF/SFR Register. Hardcoded value */
ref_value = 0x68A701 ;
cdv_sb_write ( dev , SB_REF_SFR ( pipe ) , ref_value ) ;
/* We don't know what the other fields of these regs are, so
* leave them in place .
*/
2016-02-16 14:17:45 +00:00
/*
2012-04-25 14:36:48 +01:00
* The BIT 14 : 13 of 0x8010 / 0x8030 is used to select the ref clk
* for the pipe A / B . Display spec 1.06 has wrong definition .
* Correct definition is like below :
*
* refclka mean use clock from same PLL
*
* if DPLLA sets 01 and DPLLB sets 01 , they use clock from their pll
*
* if DPLLA sets 01 and DPLLB sets 02 , both use clk from DPLLA
*
2016-02-16 14:17:45 +00:00
*/
2012-04-25 14:36:48 +01:00
ret = cdv_sb_read ( dev , ref_sfr , & ref_value ) ;
if ( ret )
return ret ;
ref_value & = ~ ( REF_CLK_MASK ) ;
/* use DPLL_A for pipeB on CRT/HDMI */
2012-08-08 13:54:41 +00:00
if ( pipe = = 1 & & ! is_lvds & & ! ( ddi_select & DP_MASK ) ) {
2012-04-25 14:36:48 +01:00
DRM_DEBUG_KMS ( " use DPLLA for pipe B \n " ) ;
ref_value | = REF_CLK_DPLLA ;
} else {
DRM_DEBUG_KMS ( " use their DPLL for pipe A/B \n " ) ;
ref_value | = REF_CLK_DPLL ;
}
ret = cdv_sb_write ( dev , ref_sfr , ref_value ) ;
if ( ret )
return ret ;
2011-11-03 18:22:37 +00:00
ret = cdv_sb_read ( dev , SB_M ( pipe ) , & m ) ;
if ( ret )
return ret ;
m & = ~ SB_M_DIVIDER_MASK ;
m | = ( ( clock - > m2 ) < < SB_M_DIVIDER_SHIFT ) ;
ret = cdv_sb_write ( dev , SB_M ( pipe ) , m ) ;
if ( ret )
return ret ;
ret = cdv_sb_read ( dev , SB_N_VCO ( pipe ) , & n_vco ) ;
if ( ret )
return ret ;
/* Follow the BIOS to program the N_DIVIDER REG */
n_vco & = 0xFFFF ;
n_vco | = 0x107 ;
n_vco & = ~ ( SB_N_VCO_SEL_MASK |
SB_N_DIVIDER_MASK |
SB_N_CB_TUNE_MASK ) ;
n_vco | = ( ( clock - > n ) < < SB_N_DIVIDER_SHIFT ) ;
if ( clock - > vco < 2250000 ) {
n_vco | = ( 2 < < SB_N_CB_TUNE_SHIFT ) ;
n_vco | = ( 0 < < SB_N_VCO_SEL_SHIFT ) ;
} else if ( clock - > vco < 2750000 ) {
n_vco | = ( 1 < < SB_N_CB_TUNE_SHIFT ) ;
n_vco | = ( 1 < < SB_N_VCO_SEL_SHIFT ) ;
} else if ( clock - > vco < 3300000 ) {
n_vco | = ( 0 < < SB_N_CB_TUNE_SHIFT ) ;
n_vco | = ( 2 < < SB_N_VCO_SEL_SHIFT ) ;
} else {
n_vco | = ( 0 < < SB_N_CB_TUNE_SHIFT ) ;
n_vco | = ( 3 < < SB_N_VCO_SEL_SHIFT ) ;
}
ret = cdv_sb_write ( dev , SB_N_VCO ( pipe ) , n_vco ) ;
if ( ret )
return ret ;
ret = cdv_sb_read ( dev , SB_P ( pipe ) , & p ) ;
if ( ret )
return ret ;
p & = ~ ( SB_P2_DIVIDER_MASK | SB_P1_DIVIDER_MASK ) ;
p | = SET_FIELD ( clock - > p1 , SB_P1_DIVIDER ) ;
switch ( clock - > p2 ) {
case 5 :
p | = SET_FIELD ( SB_P2_5 , SB_P2_DIVIDER ) ;
break ;
case 10 :
p | = SET_FIELD ( SB_P2_10 , SB_P2_DIVIDER ) ;
break ;
case 14 :
p | = SET_FIELD ( SB_P2_14 , SB_P2_DIVIDER ) ;
break ;
case 7 :
p | = SET_FIELD ( SB_P2_7 , SB_P2_DIVIDER ) ;
break ;
default :
DRM_ERROR ( " Bad P2 clock: %d \n " , clock - > p2 ) ;
return - EINVAL ;
}
ret = cdv_sb_write ( dev , SB_P ( pipe ) , p ) ;
if ( ret )
return ret ;
2012-08-08 13:53:48 +00:00
if ( ddi_select ) {
if ( ( ddi_select & DDI_MASK ) = = DDI0_SELECT ) {
lane_reg = PSB_LANE0 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE | LANE_PLL_PIPE ( pipe ) ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
lane_reg = PSB_LANE1 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE | LANE_PLL_PIPE ( pipe ) ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
} else {
lane_reg = PSB_LANE2 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE | LANE_PLL_PIPE ( pipe ) ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
lane_reg = PSB_LANE3 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE | LANE_PLL_PIPE ( pipe ) ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
}
}
2011-11-03 18:22:37 +00:00
return 0 ;
}
2013-07-01 01:42:16 +02:00
static const struct gma_limit_t * cdv_intel_limit ( struct drm_crtc * crtc ,
int refclk )
2011-11-03 18:22:37 +00:00
{
2013-07-01 01:42:16 +02:00
const struct gma_limit_t * limit ;
2013-07-02 17:02:22 +02:00
if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_LVDS ) ) {
2011-11-03 18:22:37 +00:00
/*
* Now only single - channel LVDS is supported on CDV . If it is
* incorrect , please add the dual - channel LVDS .
*/
if ( refclk = = 96000 )
limit = & cdv_intel_limits [ CDV_LIMIT_SINGLE_LVDS_96 ] ;
else
limit = & cdv_intel_limits [ CDV_LIMIT_SINGLE_LVDS_100 ] ;
2013-07-02 17:02:22 +02:00
} else if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_DISPLAYPORT ) | |
gma_pipe_has_type ( crtc , INTEL_OUTPUT_EDP ) ) {
2012-08-08 13:54:41 +00:00
if ( refclk = = 27000 )
limit = & cdv_intel_limits [ CDV_LIMIT_DP_27 ] ;
else
limit = & cdv_intel_limits [ CDV_LIMIT_DP_100 ] ;
2011-11-03 18:22:37 +00:00
} else {
if ( refclk = = 27000 )
limit = & cdv_intel_limits [ CDV_LIMIT_DAC_HDMI_27 ] ;
else
limit = & cdv_intel_limits [ CDV_LIMIT_DAC_HDMI_96 ] ;
}
return limit ;
}
/* m1 is reserved as 0 in CDV, n is a ring counter */
2013-07-01 01:42:16 +02:00
static void cdv_intel_clock ( int refclk , struct gma_clock_t * clock )
2011-11-03 18:22:37 +00:00
{
clock - > m = clock - > m2 + 2 ;
clock - > p = clock - > p1 * clock - > p2 ;
clock - > vco = ( refclk * clock - > m ) / clock - > n ;
clock - > dot = clock - > vco / clock - > p ;
}
2013-07-01 01:42:16 +02:00
static bool cdv_intel_find_dp_pll ( const struct gma_limit_t * limit ,
struct drm_crtc * crtc , int target ,
int refclk ,
struct gma_clock_t * best_clock )
2011-11-03 18:22:37 +00:00
{
2014-03-11 23:57:04 +01:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
2013-07-01 01:42:16 +02:00
struct gma_clock_t clock ;
2014-03-11 23:57:04 +01:00
2019-10-17 23:29:53 -05:00
memset ( & clock , 0 , sizeof ( clock ) ) ;
2014-03-11 23:57:04 +01:00
switch ( refclk ) {
case 27000 :
2012-08-08 13:54:41 +00:00
if ( target < 200000 ) {
clock . p1 = 2 ;
clock . p2 = 10 ;
clock . n = 1 ;
clock . m1 = 0 ;
clock . m2 = 118 ;
} else {
clock . p1 = 1 ;
clock . p2 = 10 ;
clock . n = 1 ;
clock . m1 = 0 ;
clock . m2 = 98 ;
}
2014-03-11 23:57:04 +01:00
break ;
case 100000 :
2012-08-08 13:54:41 +00:00
if ( target < 200000 ) {
clock . p1 = 2 ;
clock . p2 = 10 ;
clock . n = 5 ;
clock . m1 = 0 ;
clock . m2 = 160 ;
} else {
clock . p1 = 1 ;
clock . p2 = 10 ;
clock . n = 5 ;
clock . m1 = 0 ;
clock . m2 = 133 ;
}
2014-03-11 23:57:04 +01:00
break ;
default :
2012-08-08 13:54:41 +00:00
return false ;
2014-03-11 23:57:04 +01:00
}
gma_crtc - > clock_funcs - > clock ( refclk , & clock ) ;
2013-07-01 01:42:16 +02:00
memcpy ( best_clock , & clock , sizeof ( struct gma_clock_t ) ) ;
2012-08-08 13:54:41 +00:00
return true ;
}
2012-04-25 14:36:48 +01:00
# define FIFO_PIPEA (1 << 0)
# define FIFO_PIPEB (1 << 1)
static bool cdv_intel_pipe_enabled ( struct drm_device * dev , int pipe )
{
struct drm_crtc * crtc ;
2021-09-20 16:10:47 +02:00
struct drm_psb_private * dev_priv = to_drm_psb_private ( dev ) ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = NULL ;
2012-04-25 14:36:48 +01:00
crtc = dev_priv - > pipe_to_crtc_mapping [ pipe ] ;
2013-07-22 01:31:23 +02:00
gma_crtc = to_gma_crtc ( crtc ) ;
2012-04-25 14:36:48 +01:00
2014-04-01 15:22:40 -07:00
if ( crtc - > primary - > fb = = NULL | | ! gma_crtc - > active )
2012-04-25 14:36:48 +01:00
return false ;
return true ;
}
2013-08-15 00:54:44 +02:00
void cdv_disable_sr ( struct drm_device * dev )
2012-04-25 14:36:48 +01:00
{
if ( REG_READ ( FW_BLC_SELF ) & FW_BLC_SELF_EN ) {
/* Disable self-refresh before adjust WM */
REG_WRITE ( FW_BLC_SELF , ( REG_READ ( FW_BLC_SELF ) & ~ FW_BLC_SELF_EN ) ) ;
REG_READ ( FW_BLC_SELF ) ;
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2012-04-25 14:36:48 +01:00
/* Cedarview workaround to write ovelay plane, which force to leave
* MAX_FIFO state .
*/
REG_WRITE ( OV_OVADD , 0 /*dev_priv->ovl_offset*/ ) ;
REG_READ ( OV_OVADD ) ;
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2012-04-25 14:36:48 +01:00
}
}
2013-08-14 19:14:17 +02:00
void cdv_update_wm ( struct drm_device * dev , struct drm_crtc * crtc )
2012-04-25 14:36:48 +01:00
{
2021-09-20 16:10:47 +02:00
struct drm_psb_private * dev_priv = to_drm_psb_private ( dev ) ;
2014-03-11 23:57:04 +01:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
2012-04-25 14:36:48 +01:00
2014-03-11 23:57:04 +01:00
/* Is only one pipe enabled? */
if ( cdv_intel_pipe_enabled ( dev , 0 ) ^ cdv_intel_pipe_enabled ( dev , 1 ) ) {
2012-04-25 14:36:48 +01:00
u32 fw ;
fw = REG_READ ( DSPFW1 ) ;
fw & = ~ DSP_FIFO_SR_WM_MASK ;
fw | = ( 0x7e < < DSP_FIFO_SR_WM_SHIFT ) ;
fw & = ~ CURSOR_B_FIFO_WM_MASK ;
fw | = ( 0x4 < < CURSOR_B_FIFO_WM_SHIFT ) ;
REG_WRITE ( DSPFW1 , fw ) ;
fw = REG_READ ( DSPFW2 ) ;
fw & = ~ CURSOR_A_FIFO_WM_MASK ;
fw | = ( 0x6 < < CURSOR_A_FIFO_WM_SHIFT ) ;
fw & = ~ DSP_PLANE_C_FIFO_WM_MASK ;
fw | = ( 0x8 < < DSP_PLANE_C_FIFO_WM_SHIFT ) ;
REG_WRITE ( DSPFW2 , fw ) ;
REG_WRITE ( DSPFW3 , 0x36000000 ) ;
/* ignore FW4 */
2014-03-11 23:57:04 +01:00
/* Is pipe b lvds ? */
if ( gma_crtc - > pipe = = 1 & &
gma_pipe_has_type ( crtc , INTEL_OUTPUT_LVDS ) ) {
2012-04-25 14:36:48 +01:00
REG_WRITE ( DSPFW5 , 0x00040330 ) ;
} else {
fw = ( 3 < < DSP_PLANE_B_FIFO_WM1_SHIFT ) |
( 4 < < DSP_PLANE_A_FIFO_WM1_SHIFT ) |
( 3 < < CURSOR_B_FIFO_WM1_SHIFT ) |
( 4 < < CURSOR_FIFO_SR_WM1_SHIFT ) ;
REG_WRITE ( DSPFW5 , fw ) ;
}
REG_WRITE ( DSPFW6 , 0x10 ) ;
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2012-04-25 14:36:48 +01:00
/* enable self-refresh for single pipe active */
REG_WRITE ( FW_BLC_SELF , FW_BLC_SELF_EN ) ;
REG_READ ( FW_BLC_SELF ) ;
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2012-04-25 14:36:48 +01:00
} else {
/* HW team suggested values... */
REG_WRITE ( DSPFW1 , 0x3f880808 ) ;
REG_WRITE ( DSPFW2 , 0x0b020202 ) ;
REG_WRITE ( DSPFW3 , 0x24000000 ) ;
REG_WRITE ( DSPFW4 , 0x08030202 ) ;
REG_WRITE ( DSPFW5 , 0x01010101 ) ;
REG_WRITE ( DSPFW6 , 0x1d0 ) ;
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2012-04-25 14:36:48 +01:00
2013-08-15 00:54:44 +02:00
dev_priv - > ops - > disable_sr ( dev ) ;
2012-04-25 14:36:48 +01:00
}
}
2021-01-15 18:15:47 +00:00
/*
2011-11-03 18:22:37 +00:00
* Return the pipe currently connected to the panel fitter ,
* or - 1 if the panel fitter is not present or not in use
*/
static int cdv_intel_panel_fitter_pipe ( struct drm_device * dev )
{
u32 pfit_control ;
pfit_control = REG_READ ( PFIT_CONTROL ) ;
/* See if the panel fitter is in use */
if ( ( pfit_control & PFIT_ENABLE ) = = 0 )
return - 1 ;
return ( pfit_control > > 29 ) & 0x3 ;
}
static int cdv_intel_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 drm_device * dev = crtc - > dev ;
2021-09-20 16:10:47 +02:00
struct drm_psb_private * dev_priv = to_drm_psb_private ( dev ) ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
int pipe = gma_crtc - > pipe ;
2012-05-11 11:31:22 +01:00
const struct psb_offset * map = & dev_priv - > regmap [ pipe ] ;
2011-11-03 18:22:37 +00:00
int refclk ;
2013-07-01 01:42:16 +02:00
struct gma_clock_t clock ;
2011-11-03 18:22:37 +00:00
u32 dpll = 0 , dspcntr , pipeconf ;
2012-03-08 16:10:10 +00:00
bool ok ;
2021-02-01 14:26:14 +01:00
bool is_lvds = false ;
2019-11-15 22:27:07 +08:00
bool is_dp = false ;
2022-03-22 14:17:38 +01:00
struct drm_connector_list_iter conn_iter ;
2011-11-03 18:22:37 +00:00
struct drm_connector * connector ;
2013-07-01 01:42:16 +02:00
const struct gma_limit_t * limit ;
2012-08-08 13:53:48 +00:00
u32 ddi_select = 0 ;
2012-08-08 13:55:55 +00:00
bool is_edp = false ;
2011-11-03 18:22:37 +00:00
2022-03-22 14:17:38 +01:00
drm_connector_list_iter_begin ( dev , & conn_iter ) ;
drm_for_each_connector_iter ( connector , & conn_iter ) {
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder =
2013-07-11 01:02:01 +02:00
gma_attached_encoder ( connector ) ;
2011-11-03 18:22:37 +00:00
if ( ! connector - > encoder
| | connector - > encoder - > crtc ! = crtc )
continue ;
2013-07-22 17:45:26 +02:00
ddi_select = gma_encoder - > ddi_select ;
switch ( gma_encoder - > type ) {
2011-11-03 18:22:37 +00:00
case INTEL_OUTPUT_LVDS :
is_lvds = true ;
break ;
case INTEL_OUTPUT_ANALOG :
case INTEL_OUTPUT_HDMI :
break ;
2012-08-08 13:54:41 +00:00
case INTEL_OUTPUT_DISPLAYPORT :
is_dp = true ;
break ;
2012-08-08 13:55:55 +00:00
case INTEL_OUTPUT_EDP :
is_edp = true ;
break ;
2012-08-08 13:53:48 +00:00
default :
2022-03-22 14:17:38 +01:00
drm_connector_list_iter_end ( & conn_iter ) ;
2012-08-08 13:53:48 +00:00
DRM_ERROR ( " invalid output type. \n " ) ;
return 0 ;
2011-11-03 18:22:37 +00:00
}
2022-03-22 14:17:38 +01:00
break ;
2011-11-03 18:22:37 +00:00
}
2022-03-22 14:17:38 +01:00
drm_connector_list_iter_end ( & conn_iter ) ;
2011-11-03 18:22:37 +00:00
2012-04-25 14:36:48 +01:00
if ( dev_priv - > dplla_96mhz )
/* low-end sku, 96/100 mhz */
refclk = 96000 ;
else
/* high-end sku, 27/100 mhz */
2011-11-03 18:22:37 +00:00
refclk = 27000 ;
2012-08-08 13:55:55 +00:00
if ( is_dp | | is_edp ) {
/*
* Based on the spec the low - end SKU has only CRT / LVDS . So it is
* unnecessary to consider it for DP / eDP .
* On the high - end SKU , it will use the 27 / 100 M reference clk
* for DP / eDP . When using SSC clock , the ref clk is 100 MHz . Otherwise
* it will be 27 MHz . From the VBIOS code it seems that the pipe A choose
* 27 MHz for DP / eDP while the Pipe B chooses the 100 MHz .
2016-02-16 14:17:45 +00:00
*/
2012-08-08 13:54:41 +00:00
if ( pipe = = 0 )
refclk = 27000 ;
else
refclk = 100000 ;
}
2011-11-03 18:22:37 +00:00
2012-04-25 14:36:48 +01:00
if ( is_lvds & & dev_priv - > lvds_use_ssc ) {
refclk = dev_priv - > lvds_ssc_freq * 1000 ;
DRM_DEBUG_KMS ( " Use SSC reference clock %d Mhz \n " , dev_priv - > lvds_ssc_freq ) ;
}
2011-11-03 18:22:37 +00:00
drm_mode_debug_printmodeline ( adjusted_mode ) ;
2016-02-16 14:17:45 +00:00
2013-07-22 01:31:23 +02:00
limit = gma_crtc - > clock_funcs - > limit ( crtc , refclk ) ;
2011-11-03 18:22:37 +00:00
2012-08-08 13:53:48 +00:00
ok = limit - > find_pll ( limit , crtc , adjusted_mode - > clock , refclk ,
2011-11-03 18:22:37 +00:00
& clock ) ;
if ( ! ok ) {
2013-07-01 01:42:16 +02:00
DRM_ERROR ( " Couldn't find PLL settings for mode! target: %d, actual: %d " ,
adjusted_mode - > clock , clock . dot ) ;
2011-11-03 18:22:37 +00:00
return 0 ;
}
dpll = DPLL_VGA_MODE_DIS ;
2012-08-22 12:00:28 +00:00
if ( is_dp | | is_edp ) {
cdv_intel_dp_set_m_n ( crtc , mode , adjusted_mode ) ;
2012-08-08 13:54:41 +00:00
} else {
REG_WRITE ( PIPE_GMCH_DATA_M ( pipe ) , 0 ) ;
REG_WRITE ( PIPE_GMCH_DATA_N ( pipe ) , 0 ) ;
REG_WRITE ( PIPE_DP_LINK_M ( pipe ) , 0 ) ;
REG_WRITE ( PIPE_DP_LINK_N ( pipe ) , 0 ) ;
}
2011-11-03 18:22:37 +00:00
dpll | = DPLL_SYNCLOCK_ENABLE ;
2012-04-25 14:36:48 +01:00
/* if (is_lvds)
2011-11-03 18:22:37 +00:00
dpll | = DPLLB_MODE_LVDS ;
else
2012-04-25 14:36:48 +01:00
dpll | = DPLLB_MODE_DAC_SERIAL ; */
2011-11-03 18:22:37 +00:00
/* dpll |= (2 << 11); */
/* setup pipeconf */
2012-05-11 11:31:22 +01:00
pipeconf = REG_READ ( map - > conf ) ;
2011-11-03 18:22:37 +00:00
2012-08-08 13:55:55 +00:00
pipeconf & = ~ ( PIPE_BPC_MASK ) ;
if ( is_edp ) {
switch ( dev_priv - > edp . bpp ) {
case 24 :
pipeconf | = PIPE_8BPC ;
break ;
case 18 :
pipeconf | = PIPE_6BPC ;
break ;
case 30 :
pipeconf | = PIPE_10BPC ;
break ;
default :
pipeconf | = PIPE_8BPC ;
break ;
}
} else if ( is_lvds ) {
/* the BPC will be 6 if it is 18-bit LVDS panel */
if ( ( REG_READ ( LVDS ) & LVDS_A3_POWER_MASK ) = = LVDS_A3_POWER_UP )
pipeconf | = PIPE_8BPC ;
else
pipeconf | = PIPE_6BPC ;
} else
pipeconf | = PIPE_8BPC ;
2016-02-16 14:17:45 +00:00
2011-11-03 18:22:37 +00:00
/* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE ;
if ( pipe = = 0 )
dspcntr | = DISPPLANE_SEL_PIPE_A ;
else
dspcntr | = DISPPLANE_SEL_PIPE_B ;
dspcntr | = DISPLAY_PLANE_ENABLE ;
pipeconf | = PIPEACONF_ENABLE ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > dpll , dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE ) ;
REG_READ ( map - > dpll ) ;
2011-11-03 18:22:37 +00:00
2012-08-08 13:53:48 +00:00
cdv_dpll_set_clock_cdv ( dev , crtc , & clock , is_lvds , ddi_select ) ;
2011-11-03 18:22:37 +00:00
udelay ( 150 ) ;
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn ' t turn
* things on .
*/
if ( is_lvds ) {
u32 lvds = REG_READ ( LVDS ) ;
lvds | =
LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP |
LVDS_PIPEB_SELECT ;
/* Set the B0-B3 data pairs corresponding to
* whether we ' re going to
* set the DPLLs for dual - channel mode or not .
*/
if ( clock . p2 = = 7 )
lvds | = LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP ;
else
lvds & = ~ ( LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP ) ;
/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
* appropriately here , but we need to look more
* thoroughly into how panels behave in the two modes .
*/
REG_WRITE ( LVDS , lvds ) ;
REG_READ ( LVDS ) ;
}
dpll | = DPLL_VCO_ENABLE ;
/* Disable the panel fitter if it was on our pipe */
if ( cdv_intel_panel_fitter_pipe ( dev ) = = pipe )
REG_WRITE ( PFIT_CONTROL , 0 ) ;
DRM_DEBUG_KMS ( " Mode for pipe %c: \n " , pipe = = 0 ? ' A ' : ' B ' ) ;
drm_mode_debug_printmodeline ( mode ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > dpll ,
( REG_READ ( map - > dpll ) & ~ DPLL_LOCK ) | DPLL_VCO_ENABLE ) ;
REG_READ ( map - > dpll ) ;
2011-11-03 18:22:37 +00:00
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ; /* 42 usec w/o calibration, 110 with. rounded up. */
2012-05-11 11:31:22 +01:00
if ( ! ( REG_READ ( map - > dpll ) & DPLL_LOCK ) ) {
2011-11-03 18:22:37 +00:00
dev_err ( dev - > dev , " Failed to get DPLL lock \n " ) ;
return - EBUSY ;
}
{
int sdvo_pixel_multiply = adjusted_mode - > clock / mode - > clock ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > dpll_md , ( 0 < < DPLL_MD_UDI_DIVIDER_SHIFT ) | ( ( sdvo_pixel_multiply - 1 ) < < DPLL_MD_UDI_MULTIPLIER_SHIFT ) ) ;
2011-11-03 18:22:37 +00:00
}
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > htotal , ( adjusted_mode - > crtc_hdisplay - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_htotal - 1 ) < < 16 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > hblank , ( adjusted_mode - > crtc_hblank_start - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_hblank_end - 1 ) < < 16 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > hsync , ( adjusted_mode - > crtc_hsync_start - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_hsync_end - 1 ) < < 16 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > vtotal , ( adjusted_mode - > crtc_vdisplay - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_vtotal - 1 ) < < 16 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > vblank , ( adjusted_mode - > crtc_vblank_start - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_vblank_end - 1 ) < < 16 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > vsync , ( adjusted_mode - > crtc_vsync_start - 1 ) |
2011-11-03 18:22:37 +00:00
( ( adjusted_mode - > crtc_vsync_end - 1 ) < < 16 ) ) ;
/* pipesrc and dspsize control the size that is scaled from,
* which should always be the user ' s requested size .
*/
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > size ,
2011-11-03 18:22:37 +00:00
( ( mode - > vdisplay - 1 ) < < 16 ) | ( mode - > hdisplay - 1 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > pos , 0 ) ;
REG_WRITE ( map - > src ,
2011-11-03 18:22:37 +00:00
( ( mode - > hdisplay - 1 ) < < 16 ) | ( mode - > vdisplay - 1 ) ) ;
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > conf , pipeconf ) ;
REG_READ ( map - > conf ) ;
2011-11-03 18:22:37 +00:00
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2011-11-03 18:22:37 +00:00
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > cntr , dspcntr ) ;
2011-11-03 18:22:37 +00:00
/* Flush the plane changes */
{
2015-03-11 11:51:01 +02:00
const struct drm_crtc_helper_funcs * crtc_funcs =
2011-11-03 18:22:37 +00:00
crtc - > helper_private ;
crtc_funcs - > mode_set_base ( crtc , x , y , old_fb ) ;
}
2013-07-09 20:03:01 +02:00
gma_wait_for_vblank ( dev ) ;
2011-11-03 18:22:37 +00:00
return 0 ;
}
/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
/* FIXME: why are we using this, should it be cdv_ in this tree ? */
2013-07-01 01:42:16 +02:00
static void i8xx_clock ( int refclk , struct gma_clock_t * clock )
2011-11-03 18:22:37 +00:00
{
clock - > m = 5 * ( clock - > m1 + 2 ) + ( clock - > m2 + 2 ) ;
clock - > p = clock - > p1 * clock - > p2 ;
clock - > vco = refclk * clock - > m / ( clock - > n + 2 ) ;
clock - > dot = clock - > vco / clock - > p ;
}
/* Returns the clock of the currently programmed mode of the given pipe. */
static int cdv_intel_crtc_clock_get ( struct drm_device * dev ,
struct drm_crtc * crtc )
{
2021-09-20 16:10:47 +02:00
struct drm_psb_private * dev_priv = to_drm_psb_private ( dev ) ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
int pipe = gma_crtc - > pipe ;
2012-05-11 11:31:22 +01:00
const struct psb_offset * map = & dev_priv - > regmap [ pipe ] ;
2011-11-03 18:22:37 +00:00
u32 dpll ;
u32 fp ;
2013-07-01 01:42:16 +02:00
struct gma_clock_t clock ;
2011-11-03 18:22:37 +00:00
bool is_lvds ;
2012-05-11 11:30:16 +01:00
struct psb_pipe * p = & dev_priv - > regs . pipe [ pipe ] ;
2011-11-03 18:22:37 +00:00
if ( gma_power_begin ( dev , false ) ) {
2012-05-11 11:31:22 +01:00
dpll = REG_READ ( map - > dpll ) ;
2011-11-03 18:22:37 +00:00
if ( ( dpll & DISPLAY_RATE_SELECT_FPA1 ) = = 0 )
2012-05-11 11:31:22 +01:00
fp = REG_READ ( map - > fp0 ) ;
2011-11-03 18:22:37 +00:00
else
2012-05-11 11:31:22 +01:00
fp = REG_READ ( map - > fp1 ) ;
2011-11-03 18:22:37 +00:00
is_lvds = ( pipe = = 1 ) & & ( REG_READ ( LVDS ) & LVDS_PORT_EN ) ;
gma_power_end ( dev ) ;
} else {
2012-05-11 11:30:16 +01:00
dpll = p - > dpll ;
2011-11-03 18:22:37 +00:00
if ( ( dpll & DISPLAY_RATE_SELECT_FPA1 ) = = 0 )
2012-05-11 11:30:16 +01:00
fp = p - > fp0 ;
2011-11-03 18:22:37 +00:00
else
2012-05-11 11:30:16 +01:00
fp = p - > fp1 ;
2011-11-03 18:22:37 +00:00
2012-03-08 16:00:31 +00:00
is_lvds = ( pipe = = 1 ) & &
2012-03-08 16:02:05 +00:00
( dev_priv - > regs . psb . saveLVDS & LVDS_PORT_EN ) ;
2011-11-03 18:22:37 +00:00
}
clock . m1 = ( fp & FP_M1_DIV_MASK ) > > FP_M1_DIV_SHIFT ;
clock . m2 = ( fp & FP_M2_DIV_MASK ) > > FP_M2_DIV_SHIFT ;
clock . n = ( fp & FP_N_DIV_MASK ) > > FP_N_DIV_SHIFT ;
if ( is_lvds ) {
clock . p1 =
ffs ( ( dpll &
DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS ) > >
DPLL_FPA01_P1_POST_DIV_SHIFT ) ;
if ( clock . p1 = = 0 ) {
clock . p1 = 4 ;
dev_err ( dev - > dev , " PLL %d \n " , dpll ) ;
}
clock . p2 = 14 ;
if ( ( dpll & PLL_REF_INPUT_MASK ) = =
PLLB_REF_INPUT_SPREADSPECTRUMIN ) {
/* XXX: might not be 66MHz */
i8xx_clock ( 66000 , & clock ) ;
} else
i8xx_clock ( 48000 , & clock ) ;
} else {
if ( dpll & PLL_P1_DIVIDE_BY_TWO )
clock . p1 = 2 ;
else {
clock . p1 =
( ( dpll &
DPLL_FPA01_P1_POST_DIV_MASK_I830 ) > >
DPLL_FPA01_P1_POST_DIV_SHIFT ) + 2 ;
}
if ( dpll & PLL_P2_DIVIDE_BY_4 )
clock . p2 = 4 ;
else
clock . p2 = 2 ;
i8xx_clock ( 48000 , & clock ) ;
}
/* XXX: It would be nice to validate the clocks, but we can't reuse
* i830PllIsValid ( ) because it relies on the xf86_config connector
* configuration being accurate , which it isn ' t necessarily .
*/
return clock . dot ;
}
/** Returns the currently programmed mode of the given pipe. */
struct drm_display_mode * cdv_intel_crtc_mode_get ( struct drm_device * dev ,
struct drm_crtc * crtc )
{
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
int pipe = gma_crtc - > pipe ;
2021-09-20 16:10:47 +02:00
struct drm_psb_private * dev_priv = to_drm_psb_private ( dev ) ;
2012-05-11 11:31:22 +01:00
struct psb_pipe * p = & dev_priv - > regs . pipe [ pipe ] ;
const struct psb_offset * map = & dev_priv - > regmap [ pipe ] ;
2011-11-03 18:22:37 +00:00
struct drm_display_mode * mode ;
int htot ;
int hsync ;
int vtot ;
int vsync ;
if ( gma_power_begin ( dev , false ) ) {
2012-05-11 11:31:22 +01:00
htot = REG_READ ( map - > htotal ) ;
hsync = REG_READ ( map - > hsync ) ;
vtot = REG_READ ( map - > vtotal ) ;
vsync = REG_READ ( map - > vsync ) ;
2011-11-03 18:22:37 +00:00
gma_power_end ( dev ) ;
} else {
2012-05-11 11:30:16 +01:00
htot = p - > htotal ;
hsync = p - > hsync ;
vtot = p - > vtotal ;
vsync = p - > vsync ;
2011-11-03 18:22:37 +00:00
}
mode = kzalloc ( sizeof ( * mode ) , GFP_KERNEL ) ;
if ( ! mode )
return NULL ;
mode - > clock = cdv_intel_crtc_clock_get ( dev , crtc ) ;
mode - > hdisplay = ( htot & 0xffff ) + 1 ;
mode - > htotal = ( ( htot & 0xffff0000 ) > > 16 ) + 1 ;
mode - > hsync_start = ( hsync & 0xffff ) + 1 ;
mode - > hsync_end = ( ( hsync & 0xffff0000 ) > > 16 ) + 1 ;
mode - > vdisplay = ( vtot & 0xffff ) + 1 ;
mode - > vtotal = ( ( vtot & 0xffff0000 ) > > 16 ) + 1 ;
mode - > vsync_start = ( vsync & 0xffff ) + 1 ;
mode - > vsync_end = ( ( vsync & 0xffff0000 ) > > 16 ) + 1 ;
drm_mode_set_name ( mode ) ;
drm_mode_set_crtcinfo ( mode , 0 ) ;
return mode ;
}
const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = {
2013-07-10 18:12:11 +02:00
. dpms = gma_crtc_dpms ,
2011-11-03 18:22:37 +00:00
. mode_set = cdv_intel_crtc_mode_set ,
2013-07-10 17:58:04 +02:00
. mode_set_base = gma_pipe_set_base ,
2013-07-09 20:03:01 +02:00
. prepare = gma_crtc_prepare ,
. commit = gma_crtc_commit ,
. disable = gma_crtc_disable ,
2011-11-03 18:22:37 +00:00
} ;
2013-07-01 01:42:16 +02:00
const struct gma_clock_funcs cdv_clock_funcs = {
. clock = cdv_intel_clock ,
. limit = cdv_intel_limit ,
. pll_is_valid = gma_pll_is_valid ,
} ;