2011-11-03 22:22:37 +04:00
/*
* Copyright © 2006 - 2011 Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Authors :
* Eric Anholt < eric @ anholt . net >
*/
# include <linux/i2c.h>
# include <linux/pm_runtime.h>
# include <drm/drmP.h>
# include "framebuffer.h"
# include "psb_drv.h"
# include "psb_intel_drv.h"
# include "psb_intel_reg.h"
# include "psb_intel_display.h"
# include "power.h"
# include "cdv_device.h"
struct cdv_intel_range_t {
int min , max ;
} ;
struct cdv_intel_p2_t {
int dot_limit ;
int p2_slow , p2_fast ;
} ;
struct cdv_intel_clock_t {
/* given values */
int n ;
int m1 , m2 ;
int p1 , p2 ;
/* derived values */
int dot ;
int vco ;
int m ;
int p ;
} ;
# define INTEL_P2_NUM 2
struct cdv_intel_limit_t {
struct cdv_intel_range_t dot , vco , n , m , m1 , m2 , p , p1 ;
struct cdv_intel_p2_t p2 ;
} ;
# 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
static const struct cdv_intel_limit_t cdv_intel_limits [ ] = {
{ /* CDV_SIGNLE_LVDS_96MHz */
. 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 } ,
. p2 = { . dot_limit = 200000 ,
. p2_slow = 14 , . p2_fast = 14 } ,
} ,
{ /* 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 } ,
} ,
{ /* 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 } ,
} ,
{ /* 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 } ,
} ,
} ;
# 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)
static int cdv_sb_read ( struct drm_device * dev , u32 reg , u32 * val )
{
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 ;
}
static int cdv_sb_write ( struct drm_device * dev , u32 reg , u32 val )
{
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 .
*/
static void cdv_sb_reset ( struct drm_device * dev )
{
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 ,
struct cdv_intel_clock_t * clock )
{
struct psb_intel_crtc * psb_crtc =
to_psb_intel_crtc ( crtc ) ;
int pipe = psb_crtc - > pipe ;
u32 m , n_vco , p ;
int ret = 0 ;
int dpll_reg = ( pipe = = 0 ) ? DPLL_A : DPLL_B ;
u32 ref_value ;
cdv_sb_reset ( dev ) ;
if ( ( REG_READ ( dpll_reg ) & DPLL_SYNCLOCK_ENABLE ) = = 0 ) {
DRM_ERROR ( " Attempting to set DPLL with refclk disabled \n " ) ;
return - EBUSY ;
}
/* 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 .
*/
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 ;
/* always Program the Lane Register for the Pipe A*/
if ( pipe = = 0 ) {
/* Program the Lane0/1 for HDMI B */
u32 lane_reg , lane_value ;
lane_reg = PSB_LANE0 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE ;
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 ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
/* Program the Lane2/3 for HDMI C */
lane_reg = PSB_LANE2 ;
cdv_sb_read ( dev , lane_reg , & lane_value ) ;
lane_value & = ~ ( LANE_PLL_MASK ) ;
lane_value | = LANE_PLL_ENABLE ;
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 ;
cdv_sb_write ( dev , lane_reg , lane_value ) ;
}
return 0 ;
}
/*
2011-12-20 01:41:22 +04:00
* Returns whether any encoder on the specified pipe is of the specified type
2011-11-03 22:22:37 +04:00
*/
bool cdv_intel_pipe_has_type ( struct drm_crtc * crtc , int type )
{
struct drm_device * dev = crtc - > dev ;
struct drm_mode_config * mode_config = & dev - > mode_config ;
struct drm_connector * l_entry ;
list_for_each_entry ( l_entry , & mode_config - > connector_list , head ) {
if ( l_entry - > encoder & & l_entry - > encoder - > crtc = = crtc ) {
2011-12-20 01:41:22 +04:00
struct psb_intel_encoder * psb_intel_encoder =
psb_intel_attached_encoder ( l_entry ) ;
if ( psb_intel_encoder - > type = = type )
2011-11-03 22:22:37 +04:00
return true ;
}
}
return false ;
}
static const struct cdv_intel_limit_t * cdv_intel_limit ( struct drm_crtc * crtc ,
int refclk )
{
const struct cdv_intel_limit_t * limit ;
if ( cdv_intel_pipe_has_type ( crtc , INTEL_OUTPUT_LVDS ) ) {
/*
* 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 ] ;
} 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 */
static void cdv_intel_clock ( struct drm_device * dev ,
int refclk , struct cdv_intel_clock_t * clock )
{
clock - > m = clock - > m2 + 2 ;
clock - > p = clock - > p1 * clock - > p2 ;
clock - > vco = ( refclk * clock - > m ) / clock - > n ;
clock - > dot = clock - > vco / clock - > p ;
}
# define INTELPllInvalid(s) { /* ErrorF (s) */ ; return false; }
static bool cdv_intel_PLL_is_valid ( struct drm_crtc * crtc ,
const struct cdv_intel_limit_t * limit ,
struct cdv_intel_clock_t * clock )
{
if ( clock - > p1 < limit - > p1 . min | | limit - > p1 . max < clock - > p1 )
INTELPllInvalid ( " p1 out of range \n " ) ;
if ( clock - > p < limit - > p . min | | limit - > p . max < clock - > p )
INTELPllInvalid ( " p out of range \n " ) ;
/* unnecessary to check the range of m(m1/M2)/n again */
if ( clock - > vco < limit - > vco . min | | limit - > vco . max < clock - > vco )
INTELPllInvalid ( " vco out of range \n " ) ;
/* XXX: We may need to be checking "Dot clock"
* depending on the multiplier , connector , etc . ,
* rather than just a single range .
*/
if ( clock - > dot < limit - > dot . min | | limit - > dot . max < clock - > dot )
INTELPllInvalid ( " dot out of range \n " ) ;
return true ;
}
static bool cdv_intel_find_best_PLL ( struct drm_crtc * crtc , int target ,
int refclk ,
struct cdv_intel_clock_t * best_clock )
{
struct drm_device * dev = crtc - > dev ;
struct cdv_intel_clock_t clock ;
const struct cdv_intel_limit_t * limit = cdv_intel_limit ( crtc , refclk ) ;
int err = target ;
if ( cdv_intel_pipe_has_type ( crtc , INTEL_OUTPUT_LVDS ) & &
( REG_READ ( LVDS ) & LVDS_PORT_EN ) ! = 0 ) {
/*
* For LVDS , if the panel is on , just rely on its current
* settings for dual - channel . We haven ' t figured out how to
* reliably set up different single / dual channel state , if we
* even can .
*/
if ( ( REG_READ ( LVDS ) & LVDS_CLKB_POWER_MASK ) = =
LVDS_CLKB_POWER_UP )
clock . p2 = limit - > p2 . p2_fast ;
else
clock . p2 = limit - > p2 . p2_slow ;
} else {
if ( target < limit - > p2 . dot_limit )
clock . p2 = limit - > p2 . p2_slow ;
else
clock . p2 = limit - > p2 . p2_fast ;
}
memset ( best_clock , 0 , sizeof ( * best_clock ) ) ;
clock . m1 = 0 ;
/* m1 is reserved as 0 in CDV, n is a ring counter.
So skip the m1 loop */
for ( clock . n = limit - > n . min ; clock . n < = limit - > n . max ; clock . n + + ) {
for ( clock . m2 = limit - > m2 . min ; clock . m2 < = limit - > m2 . max ;
clock . m2 + + ) {
for ( clock . p1 = limit - > p1 . min ;
clock . p1 < = limit - > p1 . max ;
clock . p1 + + ) {
int this_err ;
cdv_intel_clock ( dev , refclk , & clock ) ;
if ( ! cdv_intel_PLL_is_valid ( crtc ,
limit , & clock ) )
continue ;
this_err = abs ( clock . dot - target ) ;
if ( this_err < err ) {
* best_clock = clock ;
err = this_err ;
}
}
}
}
return err ! = target ;
}
int cdv_intel_pipe_set_base ( struct drm_crtc * crtc ,
int x , int y , struct drm_framebuffer * old_fb )
{
struct drm_device * dev = crtc - > dev ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
struct psb_framebuffer * psbfb = to_psb_fb ( crtc - > fb ) ;
int pipe = psb_intel_crtc - > pipe ;
unsigned long start , offset ;
int dspbase = ( pipe = = 0 ? DSPABASE : DSPBBASE ) ;
int dspsurf = ( pipe = = 0 ? DSPASURF : DSPBSURF ) ;
int dspstride = ( pipe = = 0 ) ? DSPASTRIDE : DSPBSTRIDE ;
int dspcntr_reg = ( pipe = = 0 ) ? DSPACNTR : DSPBCNTR ;
u32 dspcntr ;
int ret = 0 ;
if ( ! gma_power_begin ( dev , true ) )
return 0 ;
/* no fb bound */
if ( ! crtc - > fb ) {
dev_err ( dev - > dev , " No FB bound \n " ) ;
goto psb_intel_pipe_cleaner ;
}
/* We are displaying this buffer, make sure it is actually loaded
into the GTT */
ret = psb_gtt_pin ( psbfb - > gtt ) ;
if ( ret < 0 )
goto psb_intel_pipe_set_base_exit ;
start = psbfb - > gtt - > offset ;
2011-12-20 02:06:49 +04:00
offset = y * crtc - > fb - > pitches [ 0 ] + x * ( crtc - > fb - > bits_per_pixel / 8 ) ;
2011-11-03 22:22:37 +04:00
2011-12-20 02:06:49 +04:00
REG_WRITE ( dspstride , crtc - > fb - > pitches [ 0 ] ) ;
2011-11-03 22:22:37 +04:00
dspcntr = REG_READ ( dspcntr_reg ) ;
dspcntr & = ~ DISPPLANE_PIXFORMAT_MASK ;
switch ( crtc - > fb - > bits_per_pixel ) {
case 8 :
dspcntr | = DISPPLANE_8BPP ;
break ;
case 16 :
if ( crtc - > fb - > depth = = 15 )
dspcntr | = DISPPLANE_15_16BPP ;
else
dspcntr | = DISPPLANE_16BPP ;
break ;
case 24 :
case 32 :
dspcntr | = DISPPLANE_32BPP_NO_ALPHA ;
break ;
default :
dev_err ( dev - > dev , " Unknown color depth \n " ) ;
ret = - EINVAL ;
goto psb_intel_pipe_set_base_exit ;
}
REG_WRITE ( dspcntr_reg , dspcntr ) ;
dev_dbg ( dev - > dev ,
" Writing base %08lX %08lX %d %d \n " , start , offset , x , y ) ;
REG_WRITE ( dspbase , offset ) ;
REG_READ ( dspbase ) ;
REG_WRITE ( dspsurf , start ) ;
REG_READ ( dspsurf ) ;
psb_intel_pipe_cleaner :
/* If there was a previous display we can now unpin it */
if ( old_fb )
psb_gtt_unpin ( to_psb_fb ( old_fb ) - > gtt ) ;
psb_intel_pipe_set_base_exit :
gma_power_end ( dev ) ;
return ret ;
}
/**
* Sets the power management mode of the pipe and plane .
*
* This code should probably grow support for turning the cursor off and back
* on appropriately at the same time as we ' re turning the pipe off / on .
*/
static void cdv_intel_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
struct drm_device * dev = crtc - > dev ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
int dpll_reg = ( pipe = = 0 ) ? DPLL_A : DPLL_B ;
int dspcntr_reg = ( pipe = = 0 ) ? DSPACNTR : DSPBCNTR ;
int dspbase_reg = ( pipe = = 0 ) ? DSPABASE : DSPBBASE ;
int pipeconf_reg = ( pipe = = 0 ) ? PIPEACONF : PIPEBCONF ;
u32 temp ;
bool enabled ;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on , we should map those modes to DRM_MODE_DPMS_OFF in the CRTC .
*/
switch ( mode ) {
case DRM_MODE_DPMS_ON :
case DRM_MODE_DPMS_STANDBY :
case DRM_MODE_DPMS_SUSPEND :
/* Enable the DPLL */
temp = REG_READ ( dpll_reg ) ;
if ( ( temp & DPLL_VCO_ENABLE ) = = 0 ) {
REG_WRITE ( dpll_reg , temp ) ;
REG_READ ( dpll_reg ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
REG_WRITE ( dpll_reg , temp | DPLL_VCO_ENABLE ) ;
REG_READ ( dpll_reg ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
REG_WRITE ( dpll_reg , temp | DPLL_VCO_ENABLE ) ;
REG_READ ( dpll_reg ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
}
/* Jim Bish - switch plan and pipe per scott */
/* Enable the plane */
temp = REG_READ ( dspcntr_reg ) ;
if ( ( temp & DISPLAY_PLANE_ENABLE ) = = 0 ) {
REG_WRITE ( dspcntr_reg ,
temp | DISPLAY_PLANE_ENABLE ) ;
/* Flush the plane changes */
REG_WRITE ( dspbase_reg , REG_READ ( dspbase_reg ) ) ;
}
udelay ( 150 ) ;
/* Enable the pipe */
temp = REG_READ ( pipeconf_reg ) ;
if ( ( temp & PIPEACONF_ENABLE ) = = 0 )
REG_WRITE ( pipeconf_reg , temp | PIPEACONF_ENABLE ) ;
psb_intel_crtc_load_lut ( crtc ) ;
/* Give the overlay scaler a chance to enable
* if it ' s on this pipe */
/* psb_intel_crtc_dpms_video(crtc, true); TODO */
break ;
case DRM_MODE_DPMS_OFF :
/* Give the overlay scaler a chance to disable
* if it ' s on this pipe */
/* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
/* Disable the VGA plane that we never use */
REG_WRITE ( VGACNTRL , VGA_DISP_DISABLE ) ;
/* Jim Bish - changed pipe/plane here as well. */
/* Wait for vblank for the disable to take effect */
cdv_intel_wait_for_vblank ( dev ) ;
/* Next, disable display pipes */
temp = REG_READ ( pipeconf_reg ) ;
if ( ( temp & PIPEACONF_ENABLE ) ! = 0 ) {
REG_WRITE ( pipeconf_reg , temp & ~ PIPEACONF_ENABLE ) ;
REG_READ ( pipeconf_reg ) ;
}
/* Wait for vblank for the disable to take effect. */
cdv_intel_wait_for_vblank ( dev ) ;
udelay ( 150 ) ;
/* Disable display plane */
temp = REG_READ ( dspcntr_reg ) ;
if ( ( temp & DISPLAY_PLANE_ENABLE ) ! = 0 ) {
REG_WRITE ( dspcntr_reg ,
temp & ~ DISPLAY_PLANE_ENABLE ) ;
/* Flush the plane changes */
REG_WRITE ( dspbase_reg , REG_READ ( dspbase_reg ) ) ;
REG_READ ( dspbase_reg ) ;
}
temp = REG_READ ( dpll_reg ) ;
if ( ( temp & DPLL_VCO_ENABLE ) ! = 0 ) {
REG_WRITE ( dpll_reg , temp & ~ DPLL_VCO_ENABLE ) ;
REG_READ ( dpll_reg ) ;
}
/* Wait for the clocks to turn off. */
udelay ( 150 ) ;
break ;
}
enabled = crtc - > enabled & & mode ! = DRM_MODE_DPMS_OFF ;
/*Set FIFO Watermarks*/
REG_WRITE ( DSPARB , 0x3F3E ) ;
}
static void cdv_intel_crtc_prepare ( struct drm_crtc * crtc )
{
struct drm_crtc_helper_funcs * crtc_funcs = crtc - > helper_private ;
crtc_funcs - > dpms ( crtc , DRM_MODE_DPMS_OFF ) ;
}
static void cdv_intel_crtc_commit ( struct drm_crtc * crtc )
{
struct drm_crtc_helper_funcs * crtc_funcs = crtc - > helper_private ;
crtc_funcs - > dpms ( crtc , DRM_MODE_DPMS_ON ) ;
}
void cdv_intel_encoder_prepare ( struct drm_encoder * encoder )
{
struct drm_encoder_helper_funcs * encoder_funcs =
encoder - > helper_private ;
/* lvds has its own version of prepare see cdv_intel_lvds_prepare */
encoder_funcs - > dpms ( encoder , DRM_MODE_DPMS_OFF ) ;
}
void cdv_intel_encoder_commit ( struct drm_encoder * encoder )
{
struct drm_encoder_helper_funcs * encoder_funcs =
encoder - > helper_private ;
/* lvds has its own version of commit see cdv_intel_lvds_commit */
encoder_funcs - > dpms ( encoder , DRM_MODE_DPMS_ON ) ;
}
static bool cdv_intel_crtc_mode_fixup ( struct drm_crtc * crtc ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
return true ;
}
/**
* 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 ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
int dpll_reg = ( pipe = = 0 ) ? DPLL_A : DPLL_B ;
int dpll_md_reg = ( psb_intel_crtc - > pipe = = 0 ) ? DPLL_A_MD : DPLL_B_MD ;
int dspcntr_reg = ( pipe = = 0 ) ? DSPACNTR : DSPBCNTR ;
int pipeconf_reg = ( pipe = = 0 ) ? PIPEACONF : PIPEBCONF ;
int htot_reg = ( pipe = = 0 ) ? HTOTAL_A : HTOTAL_B ;
int hblank_reg = ( pipe = = 0 ) ? HBLANK_A : HBLANK_B ;
int hsync_reg = ( pipe = = 0 ) ? HSYNC_A : HSYNC_B ;
int vtot_reg = ( pipe = = 0 ) ? VTOTAL_A : VTOTAL_B ;
int vblank_reg = ( pipe = = 0 ) ? VBLANK_A : VBLANK_B ;
int vsync_reg = ( pipe = = 0 ) ? VSYNC_A : VSYNC_B ;
int dspsize_reg = ( pipe = = 0 ) ? DSPASIZE : DSPBSIZE ;
int dsppos_reg = ( pipe = = 0 ) ? DSPAPOS : DSPBPOS ;
int pipesrc_reg = ( pipe = = 0 ) ? PIPEASRC : PIPEBSRC ;
int refclk ;
struct cdv_intel_clock_t clock ;
u32 dpll = 0 , dspcntr , pipeconf ;
bool ok , is_sdvo = false , is_dvo = false ;
bool is_crt = false , is_lvds = false , is_tv = false ;
bool is_hdmi = false ;
struct drm_mode_config * mode_config = & dev - > mode_config ;
struct drm_connector * connector ;
list_for_each_entry ( connector , & mode_config - > connector_list , head ) {
2011-12-20 01:41:22 +04:00
struct psb_intel_encoder * psb_intel_encoder =
psb_intel_attached_encoder ( connector ) ;
2011-11-03 22:22:37 +04:00
if ( ! connector - > encoder
| | connector - > encoder - > crtc ! = crtc )
continue ;
2011-12-20 01:41:22 +04:00
switch ( psb_intel_encoder - > type ) {
2011-11-03 22:22:37 +04:00
case INTEL_OUTPUT_LVDS :
is_lvds = true ;
break ;
case INTEL_OUTPUT_SDVO :
is_sdvo = true ;
break ;
case INTEL_OUTPUT_DVO :
is_dvo = true ;
break ;
case INTEL_OUTPUT_TVOUT :
is_tv = true ;
break ;
case INTEL_OUTPUT_ANALOG :
is_crt = true ;
break ;
case INTEL_OUTPUT_HDMI :
is_hdmi = true ;
break ;
}
}
refclk = 96000 ;
/* Hack selection about ref clk for CRT */
/* Select 27MHz as the reference clk for HDMI */
if ( is_crt | | is_hdmi )
refclk = 27000 ;
drm_mode_debug_printmodeline ( adjusted_mode ) ;
ok = cdv_intel_find_best_PLL ( crtc , adjusted_mode - > clock , refclk ,
& clock ) ;
if ( ! ok ) {
dev_err ( dev - > dev , " Couldn't find PLL settings for mode! \n " ) ;
return 0 ;
}
dpll = DPLL_VGA_MODE_DIS ;
if ( is_tv ) {
/* XXX: just matching BIOS for now */
/* dpll |= PLL_REF_INPUT_TVCLKINBC; */
dpll | = 3 ;
}
dpll | = PLL_REF_INPUT_DREFCLK ;
dpll | = DPLL_SYNCLOCK_ENABLE ;
dpll | = DPLL_VGA_MODE_DIS ;
if ( is_lvds )
dpll | = DPLLB_MODE_LVDS ;
else
dpll | = DPLLB_MODE_DAC_SERIAL ;
/* dpll |= (2 << 11); */
/* setup pipeconf */
pipeconf = REG_READ ( pipeconf_reg ) ;
/* 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 ;
REG_WRITE ( dpll_reg , dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE ) ;
REG_READ ( dpll_reg ) ;
cdv_dpll_set_clock_cdv ( dev , crtc , & clock ) ;
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 ) ;
REG_WRITE ( dpll_reg ,
( REG_READ ( dpll_reg ) & ~ DPLL_LOCK ) | DPLL_VCO_ENABLE ) ;
REG_READ ( dpll_reg ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ; /* 42 usec w/o calibration, 110 with. rounded up. */
if ( ! ( REG_READ ( dpll_reg ) & DPLL_LOCK ) ) {
dev_err ( dev - > dev , " Failed to get DPLL lock \n " ) ;
return - EBUSY ;
}
{
int sdvo_pixel_multiply = adjusted_mode - > clock / mode - > clock ;
REG_WRITE ( dpll_md_reg , ( 0 < < DPLL_MD_UDI_DIVIDER_SHIFT ) | ( ( sdvo_pixel_multiply - 1 ) < < DPLL_MD_UDI_MULTIPLIER_SHIFT ) ) ;
}
REG_WRITE ( htot_reg , ( adjusted_mode - > crtc_hdisplay - 1 ) |
( ( adjusted_mode - > crtc_htotal - 1 ) < < 16 ) ) ;
REG_WRITE ( hblank_reg , ( adjusted_mode - > crtc_hblank_start - 1 ) |
( ( adjusted_mode - > crtc_hblank_end - 1 ) < < 16 ) ) ;
REG_WRITE ( hsync_reg , ( adjusted_mode - > crtc_hsync_start - 1 ) |
( ( adjusted_mode - > crtc_hsync_end - 1 ) < < 16 ) ) ;
REG_WRITE ( vtot_reg , ( adjusted_mode - > crtc_vdisplay - 1 ) |
( ( adjusted_mode - > crtc_vtotal - 1 ) < < 16 ) ) ;
REG_WRITE ( vblank_reg , ( adjusted_mode - > crtc_vblank_start - 1 ) |
( ( adjusted_mode - > crtc_vblank_end - 1 ) < < 16 ) ) ;
REG_WRITE ( vsync_reg , ( adjusted_mode - > crtc_vsync_start - 1 ) |
( ( 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 .
*/
REG_WRITE ( dspsize_reg ,
( ( mode - > vdisplay - 1 ) < < 16 ) | ( mode - > hdisplay - 1 ) ) ;
REG_WRITE ( dsppos_reg , 0 ) ;
REG_WRITE ( pipesrc_reg ,
( ( mode - > hdisplay - 1 ) < < 16 ) | ( mode - > vdisplay - 1 ) ) ;
REG_WRITE ( pipeconf_reg , pipeconf ) ;
REG_READ ( pipeconf_reg ) ;
cdv_intel_wait_for_vblank ( dev ) ;
REG_WRITE ( dspcntr_reg , dspcntr ) ;
/* Flush the plane changes */
{
struct drm_crtc_helper_funcs * crtc_funcs =
crtc - > helper_private ;
crtc_funcs - > mode_set_base ( crtc , x , y , old_fb ) ;
}
cdv_intel_wait_for_vblank ( dev ) ;
return 0 ;
}
/** Loads the palette/gamma unit for the CRTC with the prepared values */
void cdv_intel_crtc_load_lut ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int palreg = PALETTE_A ;
int i ;
/* The clocks have to be on to load the palette. */
if ( ! crtc - > enabled )
return ;
switch ( psb_intel_crtc - > pipe ) {
case 0 :
break ;
case 1 :
palreg = PALETTE_B ;
break ;
case 2 :
palreg = PALETTE_C ;
break ;
default :
dev_err ( dev - > dev , " Illegal Pipe Number. \n " ) ;
return ;
}
if ( gma_power_begin ( dev , false ) ) {
for ( i = 0 ; i < 256 ; i + + ) {
REG_WRITE ( palreg + 4 * i ,
( ( psb_intel_crtc - > lut_r [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) < < 16 ) |
( ( psb_intel_crtc - > lut_g [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) < < 8 ) |
( psb_intel_crtc - > lut_b [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) ) ;
}
gma_power_end ( dev ) ;
} else {
for ( i = 0 ; i < 256 ; i + + ) {
2012-03-08 20:00:31 +04:00
dev_priv - > regs . save_palette_a [ i ] =
2011-11-03 22:22:37 +04:00
( ( psb_intel_crtc - > lut_r [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) < < 16 ) |
( ( psb_intel_crtc - > lut_g [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) < < 8 ) |
( psb_intel_crtc - > lut_b [ i ] +
psb_intel_crtc - > lut_adj [ i ] ) ;
}
}
}
/**
* Save HW states of giving crtc
*/
static void cdv_intel_crtc_save ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
/* struct drm_psb_private *dev_priv =
( struct drm_psb_private * ) dev - > dev_private ; */
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
struct psb_intel_crtc_state * crtc_state = psb_intel_crtc - > crtc_state ;
int pipeA = ( psb_intel_crtc - > pipe = = 0 ) ;
uint32_t paletteReg ;
int i ;
if ( ! crtc_state ) {
dev_dbg ( dev - > dev , " No CRTC state found \n " ) ;
return ;
}
crtc_state - > saveDSPCNTR = REG_READ ( pipeA ? DSPACNTR : DSPBCNTR ) ;
crtc_state - > savePIPECONF = REG_READ ( pipeA ? PIPEACONF : PIPEBCONF ) ;
crtc_state - > savePIPESRC = REG_READ ( pipeA ? PIPEASRC : PIPEBSRC ) ;
crtc_state - > saveFP0 = REG_READ ( pipeA ? FPA0 : FPB0 ) ;
crtc_state - > saveFP1 = REG_READ ( pipeA ? FPA1 : FPB1 ) ;
crtc_state - > saveDPLL = REG_READ ( pipeA ? DPLL_A : DPLL_B ) ;
crtc_state - > saveHTOTAL = REG_READ ( pipeA ? HTOTAL_A : HTOTAL_B ) ;
crtc_state - > saveHBLANK = REG_READ ( pipeA ? HBLANK_A : HBLANK_B ) ;
crtc_state - > saveHSYNC = REG_READ ( pipeA ? HSYNC_A : HSYNC_B ) ;
crtc_state - > saveVTOTAL = REG_READ ( pipeA ? VTOTAL_A : VTOTAL_B ) ;
crtc_state - > saveVBLANK = REG_READ ( pipeA ? VBLANK_A : VBLANK_B ) ;
crtc_state - > saveVSYNC = REG_READ ( pipeA ? VSYNC_A : VSYNC_B ) ;
crtc_state - > saveDSPSTRIDE = REG_READ ( pipeA ? DSPASTRIDE : DSPBSTRIDE ) ;
/*NOTE: DSPSIZE DSPPOS only for psb*/
crtc_state - > saveDSPSIZE = REG_READ ( pipeA ? DSPASIZE : DSPBSIZE ) ;
crtc_state - > saveDSPPOS = REG_READ ( pipeA ? DSPAPOS : DSPBPOS ) ;
crtc_state - > saveDSPBASE = REG_READ ( pipeA ? DSPABASE : DSPBBASE ) ;
DRM_DEBUG ( " (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x) \n " ,
crtc_state - > saveDSPCNTR ,
crtc_state - > savePIPECONF ,
crtc_state - > savePIPESRC ,
crtc_state - > saveFP0 ,
crtc_state - > saveFP1 ,
crtc_state - > saveDPLL ,
crtc_state - > saveHTOTAL ,
crtc_state - > saveHBLANK ,
crtc_state - > saveHSYNC ,
crtc_state - > saveVTOTAL ,
crtc_state - > saveVBLANK ,
crtc_state - > saveVSYNC ,
crtc_state - > saveDSPSTRIDE ,
crtc_state - > saveDSPSIZE ,
crtc_state - > saveDSPPOS ,
crtc_state - > saveDSPBASE
) ;
paletteReg = pipeA ? PALETTE_A : PALETTE_B ;
for ( i = 0 ; i < 256 ; + + i )
crtc_state - > savePalette [ i ] = REG_READ ( paletteReg + ( i < < 2 ) ) ;
}
/**
* Restore HW states of giving crtc
*/
static void cdv_intel_crtc_restore ( struct drm_crtc * crtc )
{
struct drm_device * dev = crtc - > dev ;
/* struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ; */
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
struct psb_intel_crtc_state * crtc_state = psb_intel_crtc - > crtc_state ;
/* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */
int pipeA = ( psb_intel_crtc - > pipe = = 0 ) ;
uint32_t paletteReg ;
int i ;
if ( ! crtc_state ) {
dev_dbg ( dev - > dev , " No crtc state \n " ) ;
return ;
}
DRM_DEBUG (
" current:(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x) \n " ,
REG_READ ( pipeA ? DSPACNTR : DSPBCNTR ) ,
REG_READ ( pipeA ? PIPEACONF : PIPEBCONF ) ,
REG_READ ( pipeA ? PIPEASRC : PIPEBSRC ) ,
REG_READ ( pipeA ? FPA0 : FPB0 ) ,
REG_READ ( pipeA ? FPA1 : FPB1 ) ,
REG_READ ( pipeA ? DPLL_A : DPLL_B ) ,
REG_READ ( pipeA ? HTOTAL_A : HTOTAL_B ) ,
REG_READ ( pipeA ? HBLANK_A : HBLANK_B ) ,
REG_READ ( pipeA ? HSYNC_A : HSYNC_B ) ,
REG_READ ( pipeA ? VTOTAL_A : VTOTAL_B ) ,
REG_READ ( pipeA ? VBLANK_A : VBLANK_B ) ,
REG_READ ( pipeA ? VSYNC_A : VSYNC_B ) ,
REG_READ ( pipeA ? DSPASTRIDE : DSPBSTRIDE ) ,
REG_READ ( pipeA ? DSPASIZE : DSPBSIZE ) ,
REG_READ ( pipeA ? DSPAPOS : DSPBPOS ) ,
REG_READ ( pipeA ? DSPABASE : DSPBBASE )
) ;
DRM_DEBUG (
" saved: (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x) \n " ,
crtc_state - > saveDSPCNTR ,
crtc_state - > savePIPECONF ,
crtc_state - > savePIPESRC ,
crtc_state - > saveFP0 ,
crtc_state - > saveFP1 ,
crtc_state - > saveDPLL ,
crtc_state - > saveHTOTAL ,
crtc_state - > saveHBLANK ,
crtc_state - > saveHSYNC ,
crtc_state - > saveVTOTAL ,
crtc_state - > saveVBLANK ,
crtc_state - > saveVSYNC ,
crtc_state - > saveDSPSTRIDE ,
crtc_state - > saveDSPSIZE ,
crtc_state - > saveDSPPOS ,
crtc_state - > saveDSPBASE
) ;
if ( crtc_state - > saveDPLL & DPLL_VCO_ENABLE ) {
REG_WRITE ( pipeA ? DPLL_A : DPLL_B ,
crtc_state - > saveDPLL & ~ DPLL_VCO_ENABLE ) ;
REG_READ ( pipeA ? DPLL_A : DPLL_B ) ;
DRM_DEBUG ( " write dpll: %x \n " ,
REG_READ ( pipeA ? DPLL_A : DPLL_B ) ) ;
udelay ( 150 ) ;
}
REG_WRITE ( pipeA ? FPA0 : FPB0 , crtc_state - > saveFP0 ) ;
REG_READ ( pipeA ? FPA0 : FPB0 ) ;
REG_WRITE ( pipeA ? FPA1 : FPB1 , crtc_state - > saveFP1 ) ;
REG_READ ( pipeA ? FPA1 : FPB1 ) ;
REG_WRITE ( pipeA ? DPLL_A : DPLL_B , crtc_state - > saveDPLL ) ;
REG_READ ( pipeA ? DPLL_A : DPLL_B ) ;
udelay ( 150 ) ;
REG_WRITE ( pipeA ? HTOTAL_A : HTOTAL_B , crtc_state - > saveHTOTAL ) ;
REG_WRITE ( pipeA ? HBLANK_A : HBLANK_B , crtc_state - > saveHBLANK ) ;
REG_WRITE ( pipeA ? HSYNC_A : HSYNC_B , crtc_state - > saveHSYNC ) ;
REG_WRITE ( pipeA ? VTOTAL_A : VTOTAL_B , crtc_state - > saveVTOTAL ) ;
REG_WRITE ( pipeA ? VBLANK_A : VBLANK_B , crtc_state - > saveVBLANK ) ;
REG_WRITE ( pipeA ? VSYNC_A : VSYNC_B , crtc_state - > saveVSYNC ) ;
REG_WRITE ( pipeA ? DSPASTRIDE : DSPBSTRIDE , crtc_state - > saveDSPSTRIDE ) ;
REG_WRITE ( pipeA ? DSPASIZE : DSPBSIZE , crtc_state - > saveDSPSIZE ) ;
REG_WRITE ( pipeA ? DSPAPOS : DSPBPOS , crtc_state - > saveDSPPOS ) ;
REG_WRITE ( pipeA ? PIPEASRC : PIPEBSRC , crtc_state - > savePIPESRC ) ;
REG_WRITE ( pipeA ? DSPABASE : DSPBBASE , crtc_state - > saveDSPBASE ) ;
REG_WRITE ( pipeA ? PIPEACONF : PIPEBCONF , crtc_state - > savePIPECONF ) ;
cdv_intel_wait_for_vblank ( dev ) ;
REG_WRITE ( pipeA ? DSPACNTR : DSPBCNTR , crtc_state - > saveDSPCNTR ) ;
REG_WRITE ( pipeA ? DSPABASE : DSPBBASE , crtc_state - > saveDSPBASE ) ;
cdv_intel_wait_for_vblank ( dev ) ;
paletteReg = pipeA ? PALETTE_A : PALETTE_B ;
for ( i = 0 ; i < 256 ; + + i )
REG_WRITE ( paletteReg + ( i < < 2 ) , crtc_state - > savePalette [ i ] ) ;
}
static int cdv_intel_crtc_cursor_set ( struct drm_crtc * crtc ,
struct drm_file * file_priv ,
uint32_t handle ,
uint32_t width , uint32_t height )
{
struct drm_device * dev = crtc - > dev ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
uint32_t control = ( pipe = = 0 ) ? CURACNTR : CURBCNTR ;
uint32_t base = ( pipe = = 0 ) ? CURABASE : CURBBASE ;
uint32_t temp ;
size_t addr = 0 ;
struct gtt_range * gt ;
struct drm_gem_object * obj ;
int ret ;
/* if we want to turn of the cursor ignore width and height */
if ( ! handle ) {
/* turn off the cursor */
temp = CURSOR_MODE_DISABLE ;
if ( gma_power_begin ( dev , false ) ) {
REG_WRITE ( control , temp ) ;
REG_WRITE ( base , 0 ) ;
gma_power_end ( dev ) ;
}
/* unpin the old GEM object */
if ( psb_intel_crtc - > cursor_obj ) {
gt = container_of ( psb_intel_crtc - > cursor_obj ,
struct gtt_range , gem ) ;
psb_gtt_unpin ( gt ) ;
drm_gem_object_unreference ( psb_intel_crtc - > cursor_obj ) ;
psb_intel_crtc - > cursor_obj = NULL ;
}
return 0 ;
}
/* Currently we only support 64x64 cursors */
if ( width ! = 64 | | height ! = 64 ) {
dev_dbg ( dev - > dev , " we currently only support 64x64 cursors \n " ) ;
return - EINVAL ;
}
obj = drm_gem_object_lookup ( dev , file_priv , handle ) ;
if ( ! obj )
return - ENOENT ;
if ( obj - > size < width * height * 4 ) {
dev_dbg ( dev - > dev , " buffer is to small \n " ) ;
return - ENOMEM ;
}
gt = container_of ( obj , struct gtt_range , gem ) ;
/* Pin the memory into the GTT */
ret = psb_gtt_pin ( gt ) ;
if ( ret ) {
dev_err ( dev - > dev , " Can not pin down handle 0x%x \n " , handle ) ;
return ret ;
}
addr = gt - > offset ; /* Or resource.start ??? */
psb_intel_crtc - > cursor_addr = addr ;
temp = 0 ;
/* set the pipe for the cursor */
temp | = ( pipe < < 28 ) ;
temp | = CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE ;
if ( gma_power_begin ( dev , false ) ) {
REG_WRITE ( control , temp ) ;
REG_WRITE ( base , addr ) ;
gma_power_end ( dev ) ;
}
/* unpin the old GEM object */
if ( psb_intel_crtc - > cursor_obj ) {
gt = container_of ( psb_intel_crtc - > cursor_obj ,
struct gtt_range , gem ) ;
psb_gtt_unpin ( gt ) ;
drm_gem_object_unreference ( psb_intel_crtc - > cursor_obj ) ;
psb_intel_crtc - > cursor_obj = obj ;
}
return 0 ;
}
static int cdv_intel_crtc_cursor_move ( struct drm_crtc * crtc , int x , int y )
{
struct drm_device * dev = crtc - > dev ;
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
uint32_t temp = 0 ;
uint32_t adder ;
if ( x < 0 ) {
temp | = ( CURSOR_POS_SIGN < < CURSOR_X_SHIFT ) ;
x = - x ;
}
if ( y < 0 ) {
temp | = ( CURSOR_POS_SIGN < < CURSOR_Y_SHIFT ) ;
y = - y ;
}
temp | = ( ( x & CURSOR_POS_MASK ) < < CURSOR_X_SHIFT ) ;
temp | = ( ( y & CURSOR_POS_MASK ) < < CURSOR_Y_SHIFT ) ;
adder = psb_intel_crtc - > cursor_addr ;
if ( gma_power_begin ( dev , false ) ) {
REG_WRITE ( ( pipe = = 0 ) ? CURAPOS : CURBPOS , temp ) ;
REG_WRITE ( ( pipe = = 0 ) ? CURABASE : CURBBASE , adder ) ;
gma_power_end ( dev ) ;
}
return 0 ;
}
static void cdv_intel_crtc_gamma_set ( struct drm_crtc * crtc , u16 * red ,
u16 * green , u16 * blue , uint32_t start , uint32_t size )
{
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int i ;
int end = ( start + size > 256 ) ? 256 : start + size ;
for ( i = start ; i < end ; i + + ) {
psb_intel_crtc - > lut_r [ i ] = red [ i ] > > 8 ;
psb_intel_crtc - > lut_g [ i ] = green [ i ] > > 8 ;
psb_intel_crtc - > lut_b [ i ] = blue [ i ] > > 8 ;
}
cdv_intel_crtc_load_lut ( crtc ) ;
}
static int cdv_crtc_set_config ( struct drm_mode_set * set )
{
int ret = 0 ;
struct drm_device * dev = set - > crtc - > dev ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
if ( ! dev_priv - > rpm_enabled )
return drm_crtc_helper_set_config ( set ) ;
pm_runtime_forbid ( & dev - > pdev - > dev ) ;
ret = drm_crtc_helper_set_config ( set ) ;
pm_runtime_allow ( & dev - > pdev - > dev ) ;
return ret ;
}
/** 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 ? */
static void i8xx_clock ( int refclk , struct cdv_intel_clock_t * clock )
{
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 )
{
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
u32 dpll ;
u32 fp ;
struct cdv_intel_clock_t clock ;
bool is_lvds ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
if ( gma_power_begin ( dev , false ) ) {
dpll = REG_READ ( ( pipe = = 0 ) ? DPLL_A : DPLL_B ) ;
if ( ( dpll & DISPLAY_RATE_SELECT_FPA1 ) = = 0 )
fp = REG_READ ( ( pipe = = 0 ) ? FPA0 : FPB0 ) ;
else
fp = REG_READ ( ( pipe = = 0 ) ? FPA1 : FPB1 ) ;
is_lvds = ( pipe = = 1 ) & & ( REG_READ ( LVDS ) & LVDS_PORT_EN ) ;
gma_power_end ( dev ) ;
} else {
dpll = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveDPLL_A : dev_priv - > regs . saveDPLL_B ;
2011-11-03 22:22:37 +04:00
if ( ( dpll & DISPLAY_RATE_SELECT_FPA1 ) = = 0 )
fp = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveFPA0 :
dev_priv - > regs . saveFPB0 ;
2011-11-03 22:22:37 +04:00
else
fp = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveFPA1 :
dev_priv - > regs . saveFPB1 ;
2011-11-03 22:22:37 +04:00
2012-03-08 20:00:31 +04:00
is_lvds = ( pipe = = 1 ) & &
( dev_priv - > regs . saveLVDS & LVDS_PORT_EN ) ;
2011-11-03 22:22:37 +04: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 )
{
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
int pipe = psb_intel_crtc - > pipe ;
struct drm_display_mode * mode ;
int htot ;
int hsync ;
int vtot ;
int vsync ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
if ( gma_power_begin ( dev , false ) ) {
htot = REG_READ ( ( pipe = = 0 ) ? HTOTAL_A : HTOTAL_B ) ;
hsync = REG_READ ( ( pipe = = 0 ) ? HSYNC_A : HSYNC_B ) ;
vtot = REG_READ ( ( pipe = = 0 ) ? VTOTAL_A : VTOTAL_B ) ;
vsync = REG_READ ( ( pipe = = 0 ) ? VSYNC_A : VSYNC_B ) ;
gma_power_end ( dev ) ;
} else {
htot = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveHTOTAL_A :
dev_priv - > regs . saveHTOTAL_B ;
2011-11-03 22:22:37 +04:00
hsync = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveHSYNC_A :
dev_priv - > regs . saveHSYNC_B ;
2011-11-03 22:22:37 +04:00
vtot = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveVTOTAL_A :
dev_priv - > regs . saveVTOTAL_B ;
2011-11-03 22:22:37 +04:00
vsync = ( pipe = = 0 ) ?
2012-03-08 20:00:31 +04:00
dev_priv - > regs . saveVSYNC_A :
dev_priv - > regs . saveVSYNC_B ;
2011-11-03 22:22:37 +04: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 ;
}
static void cdv_intel_crtc_destroy ( struct drm_crtc * crtc )
{
struct psb_intel_crtc * psb_intel_crtc = to_psb_intel_crtc ( crtc ) ;
kfree ( psb_intel_crtc - > crtc_state ) ;
drm_crtc_cleanup ( crtc ) ;
kfree ( psb_intel_crtc ) ;
}
const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = {
. dpms = cdv_intel_crtc_dpms ,
. mode_fixup = cdv_intel_crtc_mode_fixup ,
. mode_set = cdv_intel_crtc_mode_set ,
. mode_set_base = cdv_intel_pipe_set_base ,
. prepare = cdv_intel_crtc_prepare ,
. commit = cdv_intel_crtc_commit ,
} ;
const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
. save = cdv_intel_crtc_save ,
. restore = cdv_intel_crtc_restore ,
. cursor_set = cdv_intel_crtc_cursor_set ,
. cursor_move = cdv_intel_crtc_cursor_move ,
. gamma_set = cdv_intel_crtc_gamma_set ,
. set_config = cdv_crtc_set_config ,
. destroy = cdv_intel_crtc_destroy ,
} ;
/*
* Set the default value of cursor control and base register
* to zero . This is a workaround for h / w defect on oaktrail
*/
void cdv_intel_cursor_init ( struct drm_device * dev , int pipe )
{
uint32_t control ;
uint32_t base ;
switch ( pipe ) {
case 0 :
control = CURACNTR ;
base = CURABASE ;
break ;
case 1 :
control = CURBCNTR ;
base = CURBBASE ;
break ;
case 2 :
control = CURCCNTR ;
base = CURCBASE ;
break ;
default :
return ;
}
REG_WRITE ( control , 0 ) ;
REG_WRITE ( base , 0 ) ;
}