2011-11-03 18:22:26 +00:00
/*
* Copyright © 2009 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 .
*/
# 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"
2013-07-02 17:02:22 +02:00
# include "gma_display.h"
2011-11-03 18:22:26 +00:00
# include "power.h"
2013-11-06 22:31:18 +01:00
# define MRST_LIMIT_LVDS_100L 0
# define MRST_LIMIT_LVDS_83 1
# define MRST_LIMIT_LVDS_100 2
# define MRST_LIMIT_SDVO 3
2011-11-03 18:22:26 +00:00
# define MRST_DOT_MIN 19750
# define MRST_DOT_MAX 120000
# define MRST_M_MIN_100L 20
# define MRST_M_MIN_100 10
# define MRST_M_MIN_83 12
# define MRST_M_MAX_100L 34
# define MRST_M_MAX_100 17
# define MRST_M_MAX_83 20
# define MRST_P1_MIN 2
# define MRST_P1_MAX_0 7
# define MRST_P1_MAX_1 8
2013-11-06 22:31:18 +01:00
static bool mrst_lvds_find_best_pll ( const struct gma_limit_t * limit ,
struct drm_crtc * crtc , int target ,
int refclk , struct gma_clock_t * best_clock ) ;
static bool mrst_sdvo_find_best_pll ( const struct gma_limit_t * limit ,
struct drm_crtc * crtc , int target ,
int refclk , struct gma_clock_t * best_clock ) ;
static const struct gma_limit_t mrst_limits [ ] = {
2011-11-03 18:22:26 +00:00
{ /* MRST_LIMIT_LVDS_100L */
. dot = { . min = MRST_DOT_MIN , . max = MRST_DOT_MAX } ,
. m = { . min = MRST_M_MIN_100L , . max = MRST_M_MAX_100L } ,
. p1 = { . min = MRST_P1_MIN , . max = MRST_P1_MAX_1 } ,
2013-11-06 22:31:18 +01:00
. find_pll = mrst_lvds_find_best_pll ,
2011-11-03 18:22:26 +00:00
} ,
{ /* MRST_LIMIT_LVDS_83L */
. dot = { . min = MRST_DOT_MIN , . max = MRST_DOT_MAX } ,
. m = { . min = MRST_M_MIN_83 , . max = MRST_M_MAX_83 } ,
. p1 = { . min = MRST_P1_MIN , . max = MRST_P1_MAX_0 } ,
2013-11-06 22:31:18 +01:00
. find_pll = mrst_lvds_find_best_pll ,
2011-11-03 18:22:26 +00:00
} ,
{ /* MRST_LIMIT_LVDS_100 */
. dot = { . min = MRST_DOT_MIN , . max = MRST_DOT_MAX } ,
. m = { . min = MRST_M_MIN_100 , . max = MRST_M_MAX_100 } ,
. p1 = { . min = MRST_P1_MIN , . max = MRST_P1_MAX_1 } ,
2013-11-06 22:31:18 +01:00
. find_pll = mrst_lvds_find_best_pll ,
} ,
{ /* MRST_LIMIT_SDVO */
. vco = { . min = 1400000 , . max = 2800000 } ,
. n = { . min = 3 , . max = 7 } ,
. m = { . min = 80 , . max = 137 } ,
. p1 = { . min = 1 , . max = 2 } ,
. p2 = { . dot_limit = 200000 , . p2_slow = 10 , . p2_fast = 10 } ,
. find_pll = mrst_sdvo_find_best_pll ,
2011-11-03 18:22:26 +00:00
} ,
} ;
# define MRST_M_MIN 10
static const u32 oaktrail_m_converts [ ] = {
0x2B , 0x15 , 0x2A , 0x35 , 0x1A , 0x0D , 0x26 , 0x33 , 0x19 , 0x2C ,
0x36 , 0x3B , 0x1D , 0x2E , 0x37 , 0x1B , 0x2D , 0x16 , 0x0B , 0x25 ,
0x12 , 0x09 , 0x24 , 0x32 , 0x39 , 0x1c ,
} ;
2013-11-06 22:31:18 +01:00
static const struct gma_limit_t * mrst_limit ( struct drm_crtc * crtc ,
int refclk )
2011-11-03 18:22:26 +00:00
{
2013-11-06 22:31:18 +01:00
const struct gma_limit_t * limit = NULL ;
2011-11-03 18:22:26 +00:00
struct drm_device * dev = crtc - > dev ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
2013-07-02 17:02:22 +02:00
if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_LVDS )
| | gma_pipe_has_type ( crtc , INTEL_OUTPUT_MIPI ) ) {
2011-11-03 18:22:26 +00:00
switch ( dev_priv - > core_freq ) {
case 100 :
2013-11-06 22:31:18 +01:00
limit = & mrst_limits [ MRST_LIMIT_LVDS_100L ] ;
2011-11-03 18:22:26 +00:00
break ;
case 166 :
2013-11-06 22:31:18 +01:00
limit = & mrst_limits [ MRST_LIMIT_LVDS_83 ] ;
2011-11-03 18:22:26 +00:00
break ;
case 200 :
2013-11-06 22:31:18 +01:00
limit = & mrst_limits [ MRST_LIMIT_LVDS_100 ] ;
2011-11-03 18:22:26 +00:00
break ;
}
2013-11-06 22:31:18 +01:00
} else if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_SDVO ) ) {
limit = & mrst_limits [ MRST_LIMIT_SDVO ] ;
2011-11-03 18:22:26 +00:00
} else {
limit = NULL ;
2013-11-06 22:31:18 +01:00
dev_err ( dev - > dev , " mrst_limit Wrong display type. \n " ) ;
2011-11-03 18:22:26 +00:00
}
return limit ;
}
/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
2013-11-06 22:31:18 +01:00
static void mrst_lvds_clock ( int refclk , struct gma_clock_t * clock )
2011-11-03 18:22:26 +00:00
{
clock - > dot = ( refclk * clock - > m ) / ( 14 * clock - > p1 ) ;
}
2013-11-06 22:31:18 +01:00
static void mrst_print_pll ( struct gma_clock_t * clock )
2011-11-03 18:22:26 +00:00
{
2013-11-06 22:31:18 +01:00
DRM_DEBUG_DRIVER ( " dotclock=%d, m=%d, m1=%d, m2=%d, n=%d, p1=%d, p2=%d \n " ,
clock - > dot , clock - > m , clock - > m1 , clock - > m2 , clock - > n ,
clock - > p1 , clock - > p2 ) ;
}
static bool mrst_sdvo_find_best_pll ( const struct gma_limit_t * limit ,
struct drm_crtc * crtc , int target ,
int refclk , struct gma_clock_t * best_clock )
{
struct gma_clock_t clock ;
u32 target_vco , actual_freq ;
s32 freq_error , min_error = 100000 ;
memset ( best_clock , 0 , sizeof ( * best_clock ) ) ;
for ( clock . m = limit - > m . min ; clock . m < = limit - > m . max ; clock . m + + ) {
for ( clock . n = limit - > n . min ; clock . n < = limit - > n . max ;
clock . n + + ) {
for ( clock . p1 = limit - > p1 . min ;
clock . p1 < = limit - > p1 . max ; clock . p1 + + ) {
/* p2 value always stored in p2_slow on SDVO */
clock . p = clock . p1 * limit - > p2 . p2_slow ;
target_vco = target * clock . p ;
/* VCO will increase at this point so break */
if ( target_vco > limit - > vco . max )
break ;
if ( target_vco < limit - > vco . min )
continue ;
actual_freq = ( refclk * clock . m ) /
( clock . n * clock . p ) ;
freq_error = 10000 -
( ( target * 10000 ) / actual_freq ) ;
if ( freq_error < - min_error ) {
/* freq_error will start to decrease at
this point so break */
break ;
}
if ( freq_error < 0 )
freq_error = - freq_error ;
if ( freq_error < min_error ) {
min_error = freq_error ;
* best_clock = clock ;
}
}
}
if ( min_error = = 0 )
break ;
}
return min_error = = 0 ;
2011-11-03 18:22:26 +00:00
}
/**
* Returns a set of divisors for the desired target clock with the given refclk ,
* or FALSE . Divisor values are the actual divisors for
*/
2013-11-06 22:31:18 +01:00
static bool mrst_lvds_find_best_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:26 +00:00
{
2013-11-06 22:31:18 +01:00
struct gma_clock_t clock ;
2011-11-03 18:22:26 +00:00
int err = target ;
memset ( best_clock , 0 , sizeof ( * best_clock ) ) ;
for ( clock . m = limit - > m . min ; clock . m < = limit - > m . max ; clock . m + + ) {
for ( clock . p1 = limit - > p1 . min ; clock . p1 < = limit - > p1 . max ;
clock . p1 + + ) {
int this_err ;
2013-11-06 22:31:18 +01:00
mrst_lvds_clock ( refclk , & clock ) ;
2011-11-03 18:22:26 +00:00
this_err = abs ( clock . dot - target ) ;
if ( this_err < err ) {
* best_clock = clock ;
err = this_err ;
}
}
}
return err ! = target ;
}
/**
* 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 oaktrail_crtc_dpms ( struct drm_crtc * crtc , int mode )
{
struct drm_device * dev = crtc - > dev ;
2012-05-11 11:31:22 +01:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
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:26 +00:00
u32 temp ;
2013-11-07 00:14:18 +01:00
int i ;
int need_aux = gma_pipe_has_type ( crtc , INTEL_OUTPUT_SDVO ) ? 1 : 0 ;
2011-11-03 18:22:26 +00:00
2013-11-07 00:22:59 +01:00
if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_HDMI ) ) {
2012-11-06 13:49:23 +00:00
oaktrail_crtc_hdmi_dpms ( crtc , mode ) ;
return ;
}
2011-11-03 18:22:26 +00:00
if ( ! gma_power_begin ( dev , true ) )
return ;
/* 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 :
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
/* Enable the DPLL */
temp = REG_READ_WITH_AUX ( map - > dpll , i ) ;
if ( ( temp & DPLL_VCO_ENABLE ) = = 0 ) {
REG_WRITE_WITH_AUX ( map - > dpll , temp , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
REG_WRITE_WITH_AUX ( map - > dpll ,
temp | DPLL_VCO_ENABLE , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
REG_WRITE_WITH_AUX ( map - > dpll ,
temp | DPLL_VCO_ENABLE , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
}
/* Enable the pipe */
temp = REG_READ_WITH_AUX ( map - > conf , i ) ;
if ( ( temp & PIPEACONF_ENABLE ) = = 0 ) {
REG_WRITE_WITH_AUX ( map - > conf ,
temp | PIPEACONF_ENABLE , i ) ;
}
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
/* Enable the plane */
temp = REG_READ_WITH_AUX ( map - > cntr , i ) ;
if ( ( temp & DISPLAY_PLANE_ENABLE ) = = 0 ) {
REG_WRITE_WITH_AUX ( map - > cntr ,
temp | DISPLAY_PLANE_ENABLE ,
i ) ;
/* Flush the plane changes */
REG_WRITE_WITH_AUX ( map - > base ,
REG_READ_WITH_AUX ( map - > base , i ) , i ) ;
}
}
2013-07-10 18:39:58 +02:00
gma_crtc_load_lut ( crtc ) ;
2011-11-03 18:22:26 +00:00
/* 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 */
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
/* Disable the VGA plane that we never use */
REG_WRITE_WITH_AUX ( VGACNTRL , VGA_DISP_DISABLE , i ) ;
/* Disable display plane */
temp = REG_READ_WITH_AUX ( map - > cntr , i ) ;
if ( ( temp & DISPLAY_PLANE_ENABLE ) ! = 0 ) {
REG_WRITE_WITH_AUX ( map - > cntr ,
temp & ~ DISPLAY_PLANE_ENABLE , i ) ;
/* Flush the plane changes */
REG_WRITE_WITH_AUX ( map - > base ,
REG_READ ( map - > base ) , i ) ;
REG_READ_WITH_AUX ( map - > base , i ) ;
}
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
/* Next, disable display pipes */
temp = REG_READ_WITH_AUX ( map - > conf , i ) ;
if ( ( temp & PIPEACONF_ENABLE ) ! = 0 ) {
REG_WRITE_WITH_AUX ( map - > conf ,
temp & ~ PIPEACONF_ENABLE , i ) ;
REG_READ_WITH_AUX ( map - > conf , i ) ;
}
/* Wait for for the pipe disable to take effect. */
gma_wait_for_vblank ( dev ) ;
temp = REG_READ_WITH_AUX ( map - > dpll , i ) ;
if ( ( temp & DPLL_VCO_ENABLE ) ! = 0 ) {
REG_WRITE_WITH_AUX ( map - > dpll ,
temp & ~ DPLL_VCO_ENABLE , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
}
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
/* Wait for the clocks to turn off. */
udelay ( 150 ) ;
2011-11-03 18:22:26 +00:00
}
break ;
}
2013-11-07 02:34:12 +01:00
/* Set FIFO Watermarks (values taken from EMGD) */
REG_WRITE ( DSPARB , 0x3f80 ) ;
REG_WRITE ( DSPFW1 , 0x3f8f0404 ) ;
REG_WRITE ( DSPFW2 , 0x04040f04 ) ;
2011-11-03 18:22:26 +00:00
REG_WRITE ( DSPFW3 , 0x0 ) ;
2013-11-07 02:34:12 +01:00
REG_WRITE ( DSPFW4 , 0x04040404 ) ;
2011-11-03 18:22:26 +00:00
REG_WRITE ( DSPFW5 , 0x04040404 ) ;
REG_WRITE ( DSPFW6 , 0x78 ) ;
2013-11-07 02:34:12 +01:00
REG_WRITE ( DSPCHICKENBIT , REG_READ ( DSPCHICKENBIT ) | 0xc040 ) ;
2011-11-03 18:22:26 +00:00
gma_power_end ( dev ) ;
}
/**
* Return the pipe currently connected to the panel fitter ,
* or - 1 if the panel fitter is not present or not in use
*/
static int oaktrail_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 ) & 3 ;
}
static int oaktrail_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 ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
2011-11-03 18:22:26 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2013-07-22 01:31:23 +02:00
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:26 +00:00
int refclk = 0 ;
2013-11-06 22:31:18 +01:00
struct gma_clock_t clock ;
const struct gma_limit_t * limit ;
2011-11-03 18:22:26 +00:00
u32 dpll = 0 , fp = 0 , dspcntr , pipeconf ;
bool ok , is_sdvo = false ;
2012-03-08 16:12:16 +00:00
bool is_lvds = false ;
2011-11-03 18:22:26 +00:00
bool is_mipi = false ;
struct drm_mode_config * mode_config = & dev - > mode_config ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = NULL ;
2011-11-03 18:22:26 +00:00
uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN ;
2011-12-19 21:41:33 +00:00
struct drm_connector * connector ;
2013-11-07 00:14:18 +01:00
int i ;
int need_aux = gma_pipe_has_type ( crtc , INTEL_OUTPUT_SDVO ) ? 1 : 0 ;
2011-11-03 18:22:26 +00:00
2013-11-07 00:22:59 +01:00
if ( gma_pipe_has_type ( crtc , INTEL_OUTPUT_HDMI ) )
2012-11-06 13:49:23 +00:00
return oaktrail_crtc_hdmi_mode_set ( crtc , mode , adjusted_mode , x , y , old_fb ) ;
2011-11-03 18:22:26 +00:00
if ( ! gma_power_begin ( dev , true ) )
return 0 ;
2013-07-22 01:31:23 +02:00
memcpy ( & gma_crtc - > saved_mode ,
2011-11-03 18:22:26 +00:00
mode ,
sizeof ( struct drm_display_mode ) ) ;
2013-07-22 01:31:23 +02:00
memcpy ( & gma_crtc - > saved_adjusted_mode ,
2011-11-03 18:22:26 +00:00
adjusted_mode ,
sizeof ( struct drm_display_mode ) ) ;
2011-12-19 21:41:33 +00:00
list_for_each_entry ( connector , & mode_config - > connector_list , head ) {
if ( ! connector - > encoder | | connector - > encoder - > crtc ! = crtc )
2011-11-03 18:22:26 +00:00
continue ;
2013-07-22 17:45:26 +02:00
gma_encoder = gma_attached_encoder ( connector ) ;
2011-12-19 21:41:33 +00:00
2013-07-22 17:45:26 +02:00
switch ( gma_encoder - > type ) {
2011-11-03 18:22:26 +00:00
case INTEL_OUTPUT_LVDS :
is_lvds = true ;
break ;
case INTEL_OUTPUT_SDVO :
is_sdvo = true ;
break ;
case INTEL_OUTPUT_MIPI :
is_mipi = true ;
break ;
}
}
/* Disable the VGA plane that we never use */
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + )
REG_WRITE_WITH_AUX ( VGACNTRL , VGA_DISP_DISABLE , i ) ;
2011-11-03 18:22:26 +00:00
/* Disable the panel fitter if it was on our pipe */
if ( oaktrail_panel_fitter_pipe ( dev ) = = pipe )
REG_WRITE ( PFIT_CONTROL , 0 ) ;
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
REG_WRITE_WITH_AUX ( map - > src , ( ( mode - > crtc_hdisplay - 1 ) < < 16 ) |
( mode - > crtc_vdisplay - 1 ) , i ) ;
}
2011-11-03 18:22:26 +00:00
2013-07-22 17:45:26 +02:00
if ( gma_encoder )
2012-10-11 20:38:23 -05:00
drm_object_property_get_value ( & connector - > base ,
2011-11-03 18:22:26 +00:00
dev - > mode_config . scaling_mode_property , & scalingType ) ;
if ( scalingType = = DRM_MODE_SCALE_NO_SCALE ) {
/* Moorestown doesn't have register support for centering so
* we need to mess with the h / vblank and h / vsync start and
* ends to get centering */
int offsetX = 0 , offsetY = 0 ;
offsetX = ( adjusted_mode - > crtc_hdisplay -
mode - > crtc_hdisplay ) / 2 ;
offsetY = ( adjusted_mode - > crtc_vdisplay -
mode - > crtc_vdisplay ) / 2 ;
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
REG_WRITE_WITH_AUX ( map - > htotal , ( mode - > crtc_hdisplay - 1 ) |
( ( adjusted_mode - > crtc_htotal - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vtotal , ( mode - > crtc_vdisplay - 1 ) |
( ( adjusted_mode - > crtc_vtotal - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > hblank ,
( adjusted_mode - > crtc_hblank_start - offsetX - 1 ) |
( ( adjusted_mode - > crtc_hblank_end - offsetX - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > hsync ,
( adjusted_mode - > crtc_hsync_start - offsetX - 1 ) |
( ( adjusted_mode - > crtc_hsync_end - offsetX - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vblank ,
( adjusted_mode - > crtc_vblank_start - offsetY - 1 ) |
( ( adjusted_mode - > crtc_vblank_end - offsetY - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vsync ,
( adjusted_mode - > crtc_vsync_start - offsetY - 1 ) |
( ( adjusted_mode - > crtc_vsync_end - offsetY - 1 ) < < 16 ) , i ) ;
}
2011-11-03 18:22:26 +00:00
} else {
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
REG_WRITE_WITH_AUX ( map - > htotal , ( adjusted_mode - > crtc_hdisplay - 1 ) |
( ( adjusted_mode - > crtc_htotal - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vtotal , ( adjusted_mode - > crtc_vdisplay - 1 ) |
( ( adjusted_mode - > crtc_vtotal - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > hblank , ( adjusted_mode - > crtc_hblank_start - 1 ) |
( ( adjusted_mode - > crtc_hblank_end - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > hsync , ( adjusted_mode - > crtc_hsync_start - 1 ) |
( ( adjusted_mode - > crtc_hsync_end - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vblank , ( adjusted_mode - > crtc_vblank_start - 1 ) |
( ( adjusted_mode - > crtc_vblank_end - 1 ) < < 16 ) , i ) ;
REG_WRITE_WITH_AUX ( map - > vsync , ( adjusted_mode - > crtc_vsync_start - 1 ) |
( ( adjusted_mode - > crtc_vsync_end - 1 ) < < 16 ) , i ) ;
}
2011-11-03 18:22:26 +00:00
}
/* Flush the plane changes */
{
struct drm_crtc_helper_funcs * crtc_funcs =
crtc - > helper_private ;
crtc_funcs - > mode_set_base ( crtc , x , y , old_fb ) ;
}
/* setup pipeconf */
2012-05-11 11:31:22 +01:00
pipeconf = REG_READ ( map - > conf ) ;
2011-11-03 18:22:26 +00:00
/* Set up the display plane register */
2012-05-11 11:31:22 +01:00
dspcntr = REG_READ ( map - > cntr ) ;
2011-11-03 18:22:26 +00:00
dspcntr | = DISPPLANE_GAMMA_ENABLE ;
if ( pipe = = 0 )
dspcntr | = DISPPLANE_SEL_PIPE_A ;
else
dspcntr | = DISPPLANE_SEL_PIPE_B ;
if ( is_mipi )
goto oaktrail_crtc_mode_set_exit ;
dpll = 0 ; /*BIT16 = 0 for 100MHz reference */
2013-11-06 22:31:18 +01:00
refclk = is_sdvo ? 96000 : dev_priv - > core_freq * 1000 ;
limit = mrst_limit ( crtc , refclk ) ;
ok = limit - > find_pll ( limit , crtc , adjusted_mode - > clock ,
refclk , & clock ) ;
2011-11-03 18:22:26 +00:00
2013-11-06 22:31:18 +01:00
if ( is_sdvo ) {
/* Convert calculated values to register values */
clock . p1 = ( 1L < < ( clock . p1 - 1 ) ) ;
clock . m - = 2 ;
clock . n = ( 1L < < ( clock . n - 1 ) ) ;
2011-11-03 18:22:26 +00:00
}
2013-11-06 22:31:18 +01:00
if ( ! ok )
DRM_ERROR ( " Failed to find proper PLL settings " ) ;
mrst_print_pll ( & clock ) ;
if ( is_sdvo )
fp = clock . n < < 16 | clock . m ;
else
fp = oaktrail_m_converts [ ( clock . m - MRST_M_MIN ) ] < < 8 ;
2011-11-03 18:22:26 +00:00
dpll | = DPLL_VGA_MODE_DIS ;
dpll | = DPLL_VCO_ENABLE ;
if ( is_lvds )
dpll | = DPLLA_MODE_LVDS ;
else
dpll | = DPLLB_MODE_DAC_SERIAL ;
if ( is_sdvo ) {
int sdvo_pixel_multiply =
adjusted_mode - > clock / mode - > clock ;
dpll | = DPLL_DVO_HIGH_SPEED ;
dpll | =
( sdvo_pixel_multiply -
1 ) < < SDVO_MULTIPLIER_SHIFT_HIRES ;
}
/* compute bitmask from p1 value */
2013-11-06 22:31:18 +01:00
if ( is_sdvo )
dpll | = clock . p1 < < 16 ; // dpll |= (1 << (clock.p1 - 1)) << 16;
else
dpll | = ( 1 < < ( clock . p1 - 2 ) ) < < 17 ;
2011-11-03 18:22:26 +00:00
dpll | = DPLL_VCO_ENABLE ;
if ( dpll & DPLL_VCO_ENABLE ) {
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
REG_WRITE_WITH_AUX ( map - > fp0 , fp , i ) ;
REG_WRITE_WITH_AUX ( map - > dpll , dpll & ~ DPLL_VCO_ENABLE , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Check the DPLLA lock bit PIPEACONF[29] */
udelay ( 150 ) ;
}
2011-11-03 18:22:26 +00:00
}
2013-11-07 00:14:18 +01:00
for ( i = 0 ; i < = need_aux ; i + + ) {
REG_WRITE_WITH_AUX ( map - > fp0 , fp , i ) ;
REG_WRITE_WITH_AUX ( map - > dpll , dpll , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
/* write it again -- the BIOS does, after all */
REG_WRITE_WITH_AUX ( map - > dpll , dpll , i ) ;
REG_READ_WITH_AUX ( map - > dpll , i ) ;
/* Wait for the clocks to stabilize. */
udelay ( 150 ) ;
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
REG_WRITE_WITH_AUX ( map - > conf , pipeconf , i ) ;
REG_READ_WITH_AUX ( map - > conf , i ) ;
gma_wait_for_vblank ( dev ) ;
2011-11-03 18:22:26 +00:00
2013-11-07 00:14:18 +01:00
REG_WRITE_WITH_AUX ( map - > cntr , dspcntr , i ) ;
gma_wait_for_vblank ( dev ) ;
}
2011-11-03 18:22:26 +00:00
oaktrail_crtc_mode_set_exit :
gma_power_end ( dev ) ;
return 0 ;
}
2012-03-08 16:12:04 +00:00
static int oaktrail_pipe_set_base ( struct drm_crtc * crtc ,
2011-11-03 18:22:26 +00:00
int x , int y , struct drm_framebuffer * old_fb )
{
struct drm_device * dev = crtc - > dev ;
2012-05-11 11:31:22 +01:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( crtc ) ;
2011-11-03 18:22:26 +00:00
struct psb_framebuffer * psbfb = to_psb_fb ( crtc - > fb ) ;
2013-07-22 01:31:23 +02:00
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:26 +00:00
unsigned long start , offset ;
2011-11-29 22:21:03 +00:00
2011-11-03 18:22:26 +00:00
u32 dspcntr ;
int ret = 0 ;
/* no fb bound */
if ( ! crtc - > fb ) {
dev_dbg ( dev - > dev , " No FB bound \n " ) ;
return 0 ;
}
if ( ! gma_power_begin ( dev , true ) )
return 0 ;
start = psbfb - > gtt - > offset ;
2011-12-20 00:06:49 +02:00
offset = y * crtc - > fb - > pitches [ 0 ] + x * ( crtc - > fb - > bits_per_pixel / 8 ) ;
2011-11-03 18:22:26 +00:00
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > stride , crtc - > fb - > pitches [ 0 ] ) ;
2011-11-03 18:22:26 +00:00
2012-05-11 11:31:22 +01:00
dspcntr = REG_READ ( map - > cntr ) ;
2011-11-03 18:22:26 +00:00
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 pipe_set_base_exit ;
}
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > cntr , dspcntr ) ;
2011-11-03 18:22:26 +00:00
2012-05-11 11:31:22 +01:00
REG_WRITE ( map - > base , offset ) ;
REG_READ ( map - > base ) ;
REG_WRITE ( map - > surf , start ) ;
REG_READ ( map - > surf ) ;
2011-11-03 18:22:26 +00:00
pipe_set_base_exit :
gma_power_end ( dev ) ;
return ret ;
}
const struct drm_crtc_helper_funcs oaktrail_helper_funcs = {
. dpms = oaktrail_crtc_dpms ,
2013-07-10 21:52:19 +02:00
. mode_fixup = gma_crtc_mode_fixup ,
2011-11-03 18:22:26 +00:00
. mode_set = oaktrail_crtc_mode_set ,
. mode_set_base = oaktrail_pipe_set_base ,
2013-07-10 21:52:19 +02:00
. prepare = gma_crtc_prepare ,
. commit = gma_crtc_commit ,
2011-11-03 18:22:26 +00:00
} ;
2013-11-06 22:31:18 +01:00
/* Not used yet */
const struct gma_clock_funcs mrst_clock_funcs = {
. clock = mrst_lvds_clock ,
. limit = mrst_limit ,
. pll_is_valid = gma_pll_is_valid ,
} ;