2011-11-03 18:22:15 +00:00
/*
* Copyright © 2006 - 2007 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 >
* Dave Airlie < airlied @ linux . ie >
* Jesse Barnes < jesse . barnes @ intel . com >
*/
# include <linux/i2c.h>
# include <drm/drmP.h>
# include "intel_bios.h"
# include "psb_drv.h"
# include "psb_intel_drv.h"
# include "psb_intel_reg.h"
# include "power.h"
# include <linux/pm_runtime.h>
/*
* LVDS I2C backlight control macros
*/
# define BRIGHTNESS_MAX_LEVEL 100
# define BRIGHTNESS_MASK 0xFF
# define BLC_I2C_TYPE 0x01
# define BLC_PWM_TYPT 0x02
# define BLC_POLARITY_NORMAL 0
# define BLC_POLARITY_INVERSE 1
# define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE)
# define PSB_BLC_MIN_PWM_REG_FREQ (0x2)
# define PSB_BLC_PWM_PRECISION_FACTOR (10)
# define PSB_BACKLIGHT_PWM_CTL_SHIFT (16)
# define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
struct psb_intel_lvds_priv {
/*
* Saved LVDO output states
*/
uint32_t savePP_ON ;
uint32_t savePP_OFF ;
uint32_t saveLVDS ;
uint32_t savePP_CONTROL ;
uint32_t savePP_CYCLE ;
uint32_t savePFIT_CONTROL ;
uint32_t savePFIT_PGM_RATIOS ;
uint32_t saveBLC_PWM_CTL ;
2011-12-19 21:40:45 +00:00
struct psb_intel_i2c_chan * i2c_bus ;
struct psb_intel_i2c_chan * ddc_bus ;
2011-11-03 18:22:15 +00:00
} ;
/*
* Returns the maximum level of the backlight duty cycle field .
*/
static u32 psb_intel_lvds_get_max_backlight ( struct drm_device * dev )
{
struct drm_psb_private * dev_priv = dev - > dev_private ;
2011-11-29 22:26:58 +00:00
u32 ret ;
2011-11-03 18:22:15 +00:00
if ( gma_power_begin ( dev , false ) ) {
2011-11-29 22:26:58 +00:00
ret = REG_READ ( BLC_PWM_CTL ) ;
2011-11-03 18:22:15 +00:00
gma_power_end ( dev ) ;
2011-11-29 22:26:58 +00:00
} else /* Powered off, use the saved value */
2012-03-08 16:00:31 +00:00
ret = dev_priv - > regs . saveBLC_PWM_CTL ;
2011-11-29 22:26:58 +00:00
/* Top 15bits hold the frequency mask */
ret = ( ret & BACKLIGHT_MODULATION_FREQ_MASK ) > >
BACKLIGHT_MODULATION_FREQ_SHIFT ;
ret * = 2 ; /* Return a 16bit range as needed for setting */
if ( ret = = 0 )
dev_err ( dev - > dev , " BL bug: Reg %08x save %08X \n " ,
2012-03-08 16:00:31 +00:00
REG_READ ( BLC_PWM_CTL ) , dev_priv - > regs . saveBLC_PWM_CTL ) ;
2011-11-29 22:26:58 +00:00
return ret ;
2011-11-03 18:22:15 +00:00
}
/*
* Set LVDS backlight level by I2C command
*
* FIXME : at some point we need to both track this for PM and also
* disable runtime pm on MRST if the brightness is nil ( ie blanked )
*/
static int psb_lvds_i2c_set_brightness ( struct drm_device * dev ,
unsigned int level )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
struct psb_intel_i2c_chan * lvds_i2c_bus = dev_priv - > lvds_i2c_bus ;
u8 out_buf [ 2 ] ;
unsigned int blc_i2c_brightness ;
struct i2c_msg msgs [ ] = {
{
. addr = lvds_i2c_bus - > slave_addr ,
. flags = 0 ,
. len = 2 ,
. buf = out_buf ,
}
} ;
blc_i2c_brightness = BRIGHTNESS_MASK & ( ( unsigned int ) level *
BRIGHTNESS_MASK /
BRIGHTNESS_MAX_LEVEL ) ;
if ( dev_priv - > lvds_bl - > pol = = BLC_POLARITY_INVERSE )
blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness ;
out_buf [ 0 ] = dev_priv - > lvds_bl - > brightnesscmd ;
out_buf [ 1 ] = ( u8 ) blc_i2c_brightness ;
if ( i2c_transfer ( & lvds_i2c_bus - > adapter , msgs , 1 ) = = 1 ) {
dev_dbg ( dev - > dev , " I2C set brightness.(command, value) (%d, %d) \n " ,
dev_priv - > lvds_bl - > brightnesscmd ,
blc_i2c_brightness ) ;
return 0 ;
}
dev_err ( dev - > dev , " I2C transfer error \n " ) ;
return - 1 ;
}
static int psb_lvds_pwm_set_brightness ( struct drm_device * dev , int level )
{
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
u32 max_pwm_blc ;
u32 blc_pwm_duty_cycle ;
max_pwm_blc = psb_intel_lvds_get_max_backlight ( dev ) ;
/*BLC_PWM_CTL Should be initiated while backlight device init*/
2011-11-29 22:26:58 +00:00
BUG_ON ( max_pwm_blc = = 0 ) ;
2011-11-03 18:22:15 +00:00
blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL ;
if ( dev_priv - > lvds_bl - > pol = = BLC_POLARITY_INVERSE )
blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle ;
blc_pwm_duty_cycle & = PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR ;
REG_WRITE ( BLC_PWM_CTL ,
( max_pwm_blc < < PSB_BACKLIGHT_PWM_CTL_SHIFT ) |
( blc_pwm_duty_cycle ) ) ;
2011-11-29 22:26:58 +00:00
dev_info ( dev - > dev , " Backlight lvds set brightness %08x \n " ,
( max_pwm_blc < < PSB_BACKLIGHT_PWM_CTL_SHIFT ) |
( blc_pwm_duty_cycle ) ) ;
2011-11-03 18:22:15 +00:00
return 0 ;
}
/*
* Set LVDS backlight level either by I2C or PWM
*/
void psb_intel_lvds_set_brightness ( struct drm_device * dev , int level )
{
2011-11-29 22:26:58 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2011-11-03 18:22:15 +00:00
dev_dbg ( dev - > dev , " backlight level is %d \n " , level ) ;
if ( ! dev_priv - > lvds_bl ) {
2011-11-29 22:26:58 +00:00
dev_err ( dev - > dev , " NO LVDS backlight info \n " ) ;
2011-11-03 18:22:15 +00:00
return ;
}
if ( dev_priv - > lvds_bl - > type = = BLC_I2C_TYPE )
psb_lvds_i2c_set_brightness ( dev , level ) ;
else
psb_lvds_pwm_set_brightness ( dev , level ) ;
}
/*
* Sets the backlight level .
*
* level : backlight level , from 0 to psb_intel_lvds_get_max_backlight ( ) .
*/
static void psb_intel_lvds_set_backlight ( struct drm_device * dev , int level )
{
struct drm_psb_private * dev_priv = dev - > dev_private ;
u32 blc_pwm_ctl ;
if ( gma_power_begin ( dev , false ) ) {
2011-11-29 22:26:58 +00:00
blc_pwm_ctl = REG_READ ( BLC_PWM_CTL ) ;
blc_pwm_ctl & = ~ BACKLIGHT_DUTY_CYCLE_MASK ;
2011-11-03 18:22:15 +00:00
REG_WRITE ( BLC_PWM_CTL ,
( blc_pwm_ctl |
( level < < BACKLIGHT_DUTY_CYCLE_SHIFT ) ) ) ;
2012-03-08 16:00:31 +00:00
dev_priv - > regs . saveBLC_PWM_CTL = ( blc_pwm_ctl |
2011-11-29 22:26:58 +00:00
( level < < BACKLIGHT_DUTY_CYCLE_SHIFT ) ) ;
2011-11-03 18:22:15 +00:00
gma_power_end ( dev ) ;
} else {
2012-03-08 16:00:31 +00:00
blc_pwm_ctl = dev_priv - > regs . saveBLC_PWM_CTL &
2011-11-03 18:22:15 +00:00
~ BACKLIGHT_DUTY_CYCLE_MASK ;
2012-03-08 16:00:31 +00:00
dev_priv - > regs . saveBLC_PWM_CTL = ( blc_pwm_ctl |
2011-11-03 18:22:15 +00:00
( level < < BACKLIGHT_DUTY_CYCLE_SHIFT ) ) ;
}
}
/*
* Sets the power state for the panel .
*/
2011-12-19 21:40:45 +00:00
static void psb_intel_lvds_set_power ( struct drm_device * dev , bool on )
2011-11-03 18:22:15 +00:00
{
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct psb_intel_mode_device * mode_dev = & dev_priv - > mode_dev ;
2011-11-03 18:22:15 +00:00
u32 pp_status ;
2011-11-29 22:26:58 +00:00
if ( ! gma_power_begin ( dev , true ) ) {
dev_err ( dev - > dev , " set power, chip off! \n " ) ;
2011-11-03 18:22:15 +00:00
return ;
2011-11-29 22:26:58 +00:00
}
2011-11-03 18:22:15 +00:00
if ( on ) {
REG_WRITE ( PP_CONTROL , REG_READ ( PP_CONTROL ) |
POWER_TARGET_ON ) ;
do {
pp_status = REG_READ ( PP_STATUS ) ;
} while ( ( pp_status & PP_ON ) = = 0 ) ;
psb_intel_lvds_set_backlight ( dev ,
2011-12-19 21:40:45 +00:00
mode_dev - > backlight_duty_cycle ) ;
2011-11-03 18:22:15 +00:00
} else {
psb_intel_lvds_set_backlight ( dev , 0 ) ;
REG_WRITE ( PP_CONTROL , REG_READ ( PP_CONTROL ) &
~ POWER_TARGET_ON ) ;
do {
pp_status = REG_READ ( PP_STATUS ) ;
} while ( pp_status & PP_ON ) ;
}
gma_power_end ( dev ) ;
}
static void psb_intel_lvds_encoder_dpms ( struct drm_encoder * encoder , int mode )
{
struct drm_device * dev = encoder - > dev ;
if ( mode = = DRM_MODE_DPMS_ON )
2011-12-19 21:40:45 +00:00
psb_intel_lvds_set_power ( dev , true ) ;
2011-11-03 18:22:15 +00:00
else
2011-12-19 21:40:45 +00:00
psb_intel_lvds_set_power ( dev , false ) ;
2011-11-03 18:22:15 +00:00
/* XXX: We never power down the LVDS pairs. */
}
static void psb_intel_lvds_save ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
struct drm_psb_private * dev_priv =
( struct drm_psb_private * ) dev - > dev_private ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
2011-11-03 18:22:15 +00:00
struct psb_intel_lvds_priv * lvds_priv =
2013-07-22 17:45:26 +02:00
( struct psb_intel_lvds_priv * ) gma_encoder - > dev_priv ;
2011-11-03 18:22:15 +00:00
lvds_priv - > savePP_ON = REG_READ ( LVDSPP_ON ) ;
lvds_priv - > savePP_OFF = REG_READ ( LVDSPP_OFF ) ;
lvds_priv - > saveLVDS = REG_READ ( LVDS ) ;
lvds_priv - > savePP_CONTROL = REG_READ ( PP_CONTROL ) ;
lvds_priv - > savePP_CYCLE = REG_READ ( PP_CYCLE ) ;
/*lvds_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);*/
lvds_priv - > saveBLC_PWM_CTL = REG_READ ( BLC_PWM_CTL ) ;
lvds_priv - > savePFIT_CONTROL = REG_READ ( PFIT_CONTROL ) ;
lvds_priv - > savePFIT_PGM_RATIOS = REG_READ ( PFIT_PGM_RATIOS ) ;
/*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/
2012-03-08 16:00:31 +00:00
dev_priv - > backlight_duty_cycle = ( dev_priv - > regs . saveBLC_PWM_CTL &
2011-11-03 18:22:15 +00:00
BACKLIGHT_DUTY_CYCLE_MASK ) ;
/*
* If the light is off at server startup ,
* just make it full brightness
*/
if ( dev_priv - > backlight_duty_cycle = = 0 )
dev_priv - > backlight_duty_cycle =
psb_intel_lvds_get_max_backlight ( dev ) ;
dev_dbg ( dev - > dev , " (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x) \n " ,
lvds_priv - > savePP_ON ,
lvds_priv - > savePP_OFF ,
lvds_priv - > saveLVDS ,
lvds_priv - > savePP_CONTROL ,
lvds_priv - > savePP_CYCLE ,
lvds_priv - > saveBLC_PWM_CTL ) ;
}
static void psb_intel_lvds_restore ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
u32 pp_status ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
2011-11-03 18:22:15 +00:00
struct psb_intel_lvds_priv * lvds_priv =
2013-07-22 17:45:26 +02:00
( struct psb_intel_lvds_priv * ) gma_encoder - > dev_priv ;
2011-11-03 18:22:15 +00:00
dev_dbg ( dev - > dev , " (0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x) \n " ,
lvds_priv - > savePP_ON ,
lvds_priv - > savePP_OFF ,
lvds_priv - > saveLVDS ,
lvds_priv - > savePP_CONTROL ,
lvds_priv - > savePP_CYCLE ,
lvds_priv - > saveBLC_PWM_CTL ) ;
REG_WRITE ( BLC_PWM_CTL , lvds_priv - > saveBLC_PWM_CTL ) ;
REG_WRITE ( PFIT_CONTROL , lvds_priv - > savePFIT_CONTROL ) ;
REG_WRITE ( PFIT_PGM_RATIOS , lvds_priv - > savePFIT_PGM_RATIOS ) ;
REG_WRITE ( LVDSPP_ON , lvds_priv - > savePP_ON ) ;
REG_WRITE ( LVDSPP_OFF , lvds_priv - > savePP_OFF ) ;
/*REG_WRITE(PP_DIVISOR, lvds_priv->savePP_DIVISOR);*/
REG_WRITE ( PP_CYCLE , lvds_priv - > savePP_CYCLE ) ;
REG_WRITE ( PP_CONTROL , lvds_priv - > savePP_CONTROL ) ;
REG_WRITE ( LVDS , lvds_priv - > saveLVDS ) ;
if ( lvds_priv - > savePP_CONTROL & POWER_TARGET_ON ) {
REG_WRITE ( PP_CONTROL , REG_READ ( PP_CONTROL ) |
POWER_TARGET_ON ) ;
do {
pp_status = REG_READ ( PP_STATUS ) ;
} while ( ( pp_status & PP_ON ) = = 0 ) ;
} else {
REG_WRITE ( PP_CONTROL , REG_READ ( PP_CONTROL ) &
~ POWER_TARGET_ON ) ;
do {
pp_status = REG_READ ( PP_STATUS ) ;
} while ( pp_status & PP_ON ) ;
}
}
int psb_intel_lvds_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = connector - > dev - > dev_private ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
2011-11-03 18:22:15 +00:00
struct drm_display_mode * fixed_mode =
2011-12-19 21:40:45 +00:00
dev_priv - > mode_dev . panel_fixed_mode ;
2011-11-03 18:22:15 +00:00
2013-07-22 17:45:26 +02:00
if ( gma_encoder - > type = = INTEL_OUTPUT_MIPI2 )
2011-12-19 21:40:45 +00:00
fixed_mode = dev_priv - > mode_dev . panel_fixed_mode2 ;
2011-11-03 18:22:15 +00:00
/* just in case */
if ( mode - > flags & DRM_MODE_FLAG_DBLSCAN )
return MODE_NO_DBLESCAN ;
/* just in case */
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
return MODE_NO_INTERLACE ;
if ( fixed_mode ) {
if ( mode - > hdisplay > fixed_mode - > hdisplay )
return MODE_PANEL ;
if ( mode - > vdisplay > fixed_mode - > vdisplay )
return MODE_PANEL ;
}
return MODE_OK ;
}
bool psb_intel_lvds_mode_fixup ( struct drm_encoder * encoder ,
2012-07-17 17:56:50 +02:00
const struct drm_display_mode * mode ,
2011-11-03 18:22:15 +00:00
struct drm_display_mode * adjusted_mode )
{
struct drm_device * dev = encoder - > dev ;
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct psb_intel_mode_device * mode_dev = & dev_priv - > mode_dev ;
2013-07-22 01:31:23 +02:00
struct gma_crtc * gma_crtc = to_gma_crtc ( encoder - > crtc ) ;
2011-11-03 18:22:15 +00:00
struct drm_encoder * tmp_encoder ;
struct drm_display_mode * panel_fixed_mode = mode_dev - > panel_fixed_mode ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = to_gma_encoder ( encoder ) ;
2011-11-03 18:22:15 +00:00
2013-07-22 17:45:26 +02:00
if ( gma_encoder - > type = = INTEL_OUTPUT_MIPI2 )
2011-11-03 18:22:15 +00:00
panel_fixed_mode = mode_dev - > panel_fixed_mode2 ;
/* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */
2013-07-22 01:31:23 +02:00
if ( ! IS_MRST ( dev ) & & gma_crtc - > pipe = = 0 ) {
2011-11-03 18:22:15 +00:00
printk ( KERN_ERR " Can't support LVDS on pipe A \n " ) ;
return false ;
}
2013-07-22 01:31:23 +02:00
if ( IS_MRST ( dev ) & & gma_crtc - > pipe ! = 0 ) {
2011-11-03 18:22:15 +00:00
printk ( KERN_ERR " Must use PIPE A \n " ) ;
return false ;
}
/* Should never happen!! */
list_for_each_entry ( tmp_encoder , & dev - > mode_config . encoder_list ,
head ) {
if ( tmp_encoder ! = encoder
& & tmp_encoder - > crtc = = encoder - > crtc ) {
printk ( KERN_ERR " Can't enable LVDS and another "
" encoder on the same pipe \n " ) ;
return false ;
}
}
/*
* If we have timings from the BIOS for the panel , put them in
* to the adjusted mode . The CRTC will be set up for this mode ,
* with the panel scaling set up to source from the H / VDisplay
* of the original mode .
*/
if ( panel_fixed_mode ! = NULL ) {
adjusted_mode - > hdisplay = panel_fixed_mode - > hdisplay ;
adjusted_mode - > hsync_start = panel_fixed_mode - > hsync_start ;
adjusted_mode - > hsync_end = panel_fixed_mode - > hsync_end ;
adjusted_mode - > htotal = panel_fixed_mode - > htotal ;
adjusted_mode - > vdisplay = panel_fixed_mode - > vdisplay ;
adjusted_mode - > vsync_start = panel_fixed_mode - > vsync_start ;
adjusted_mode - > vsync_end = panel_fixed_mode - > vsync_end ;
adjusted_mode - > vtotal = panel_fixed_mode - > vtotal ;
adjusted_mode - > clock = panel_fixed_mode - > clock ;
drm_mode_set_crtcinfo ( adjusted_mode ,
CRTC_INTERLACE_HALVE_V ) ;
}
/*
* XXX : It would be nice to support lower refresh rates on the
* panels to reduce power consumption , and perhaps match the
* user ' s requested refresh rate .
*/
return true ;
}
static void psb_intel_lvds_prepare ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct psb_intel_mode_device * mode_dev = & dev_priv - > mode_dev ;
2011-11-03 18:22:15 +00:00
if ( ! gma_power_begin ( dev , true ) )
return ;
mode_dev - > saveBLC_PWM_CTL = REG_READ ( BLC_PWM_CTL ) ;
mode_dev - > backlight_duty_cycle = ( mode_dev - > saveBLC_PWM_CTL &
BACKLIGHT_DUTY_CYCLE_MASK ) ;
2011-12-19 21:40:45 +00:00
psb_intel_lvds_set_power ( dev , false ) ;
2011-11-03 18:22:15 +00:00
gma_power_end ( dev ) ;
}
static void psb_intel_lvds_commit ( struct drm_encoder * encoder )
{
struct drm_device * dev = encoder - > dev ;
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct psb_intel_mode_device * mode_dev = & dev_priv - > mode_dev ;
2011-11-03 18:22:15 +00:00
if ( mode_dev - > backlight_duty_cycle = = 0 )
mode_dev - > backlight_duty_cycle =
psb_intel_lvds_get_max_backlight ( dev ) ;
2011-12-19 21:40:45 +00:00
psb_intel_lvds_set_power ( dev , true ) ;
2011-11-03 18:22:15 +00:00
}
static void psb_intel_lvds_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted_mode )
{
struct drm_device * dev = encoder - > dev ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
u32 pfit_control ;
/*
* The LVDS pin pair will already have been turned on in the
* psb_intel_crtc_mode_set since it has a large impact on the DPLL
* settings .
*/
/*
* Enable automatic panel scaling so that non - native modes fill the
* screen . Should be enabled before the pipe is enabled , according to
* register description and PRM .
*/
if ( mode - > hdisplay ! = adjusted_mode - > hdisplay | |
mode - > vdisplay ! = adjusted_mode - > vdisplay )
pfit_control = ( PFIT_ENABLE | VERT_AUTO_SCALE |
HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR ) ;
else
pfit_control = 0 ;
if ( dev_priv - > lvds_dither )
pfit_control | = PANEL_8TO6_DITHER_ENABLE ;
REG_WRITE ( PFIT_CONTROL , pfit_control ) ;
}
/*
* Detect the LVDS connection .
*
* This always returns CONNECTOR_STATUS_CONNECTED .
* This connector should only have
* been set up if the LVDS was actually connected anyway .
*/
static enum drm_connector_status psb_intel_lvds_detect ( struct drm_connector
* connector , bool force )
{
return connector_status_connected ;
}
/*
* Return the list of DDC modes if available , or the BIOS fixed mode otherwise .
*/
static int psb_intel_lvds_get_modes ( struct drm_connector * connector )
{
struct drm_device * dev = connector - > dev ;
2011-12-19 21:40:45 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct psb_intel_mode_device * mode_dev = & dev_priv - > mode_dev ;
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
struct psb_intel_lvds_priv * lvds_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:15 +00:00
int ret = 0 ;
if ( ! IS_MRST ( dev ) )
2011-12-19 21:40:45 +00:00
ret = psb_intel_ddc_get_modes ( connector , & lvds_priv - > i2c_bus - > adapter ) ;
2011-11-03 18:22:15 +00:00
if ( ret )
return ret ;
/* Didn't get an EDID, so
* Set wide sync ranges so we get all modes
* handed to valid_mode for checking
*/
connector - > display_info . min_vfreq = 0 ;
connector - > display_info . max_vfreq = 200 ;
connector - > display_info . min_hfreq = 0 ;
connector - > display_info . max_hfreq = 200 ;
if ( mode_dev - > panel_fixed_mode ! = NULL ) {
struct drm_display_mode * mode =
drm_mode_duplicate ( dev , mode_dev - > panel_fixed_mode ) ;
drm_mode_probed_add ( connector , mode ) ;
return 1 ;
}
return 0 ;
}
/**
* psb_intel_lvds_destroy - unregister and free LVDS structures
* @ connector : connector to free
*
* Unregister the DDC bus for this connector then free the driver private
* structure .
*/
void psb_intel_lvds_destroy ( struct drm_connector * connector )
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder = gma_attached_encoder ( connector ) ;
struct psb_intel_lvds_priv * lvds_priv = gma_encoder - > dev_priv ;
2011-11-03 18:22:15 +00:00
2011-12-19 21:40:45 +00:00
if ( lvds_priv - > ddc_bus )
psb_intel_i2c_destroy ( lvds_priv - > ddc_bus ) ;
2011-11-03 18:22:15 +00:00
drm_sysfs_connector_remove ( connector ) ;
drm_connector_cleanup ( connector ) ;
kfree ( connector ) ;
}
int psb_intel_lvds_set_property ( struct drm_connector * connector ,
struct drm_property * property ,
uint64_t value )
{
struct drm_encoder * encoder = connector - > encoder ;
if ( ! encoder )
return - 1 ;
if ( ! strcmp ( property - > name , " scaling mode " ) ) {
2013-07-22 01:31:23 +02:00
struct gma_crtc * crtc = to_gma_crtc ( encoder - > crtc ) ;
2011-11-03 18:22:15 +00:00
uint64_t curval ;
if ( ! crtc )
goto set_prop_error ;
switch ( value ) {
case DRM_MODE_SCALE_FULLSCREEN :
break ;
case DRM_MODE_SCALE_NO_SCALE :
break ;
case DRM_MODE_SCALE_ASPECT :
break ;
default :
goto set_prop_error ;
}
2012-10-11 20:38:23 -05:00
if ( drm_object_property_get_value ( & connector - > base ,
2011-11-03 18:22:15 +00:00
property ,
& curval ) )
goto set_prop_error ;
if ( curval = = value )
goto set_prop_done ;
2012-10-11 20:38:23 -05:00
if ( drm_object_property_set_value ( & connector - > base ,
2011-11-03 18:22:15 +00:00
property ,
value ) )
goto set_prop_error ;
if ( crtc - > saved_mode . hdisplay ! = 0 & &
crtc - > saved_mode . vdisplay ! = 0 ) {
if ( ! drm_crtc_helper_set_mode ( encoder - > crtc ,
& crtc - > saved_mode ,
encoder - > crtc - > x ,
encoder - > crtc - > y ,
encoder - > crtc - > fb ) )
goto set_prop_error ;
}
} else if ( ! strcmp ( property - > name , " backlight " ) ) {
2012-10-11 20:38:23 -05:00
if ( drm_object_property_set_value ( & connector - > base ,
2011-11-03 18:22:15 +00:00
property ,
value ) )
goto set_prop_error ;
2012-08-08 13:55:55 +00:00
else
gma_backlight_set ( encoder - > dev , value ) ;
2011-11-03 18:22:15 +00:00
} else if ( ! strcmp ( property - > name , " DPMS " ) ) {
struct drm_encoder_helper_funcs * hfuncs
= encoder - > helper_private ;
hfuncs - > dpms ( encoder , value ) ;
}
set_prop_done :
return 0 ;
set_prop_error :
return - 1 ;
}
static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = {
. dpms = psb_intel_lvds_encoder_dpms ,
. mode_fixup = psb_intel_lvds_mode_fixup ,
. prepare = psb_intel_lvds_prepare ,
. mode_set = psb_intel_lvds_mode_set ,
. commit = psb_intel_lvds_commit ,
} ;
const struct drm_connector_helper_funcs
psb_intel_lvds_connector_helper_funcs = {
. get_modes = psb_intel_lvds_get_modes ,
. mode_valid = psb_intel_lvds_mode_valid ,
2013-07-11 01:02:01 +02:00
. best_encoder = gma_best_encoder ,
2011-11-03 18:22:15 +00:00
} ;
const struct drm_connector_funcs psb_intel_lvds_connector_funcs = {
. dpms = drm_helper_connector_dpms ,
. save = psb_intel_lvds_save ,
. restore = psb_intel_lvds_restore ,
. detect = psb_intel_lvds_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = psb_intel_lvds_set_property ,
. destroy = psb_intel_lvds_destroy ,
} ;
static void psb_intel_lvds_enc_destroy ( struct drm_encoder * encoder )
{
drm_encoder_cleanup ( encoder ) ;
}
const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = {
. destroy = psb_intel_lvds_enc_destroy ,
} ;
/**
* psb_intel_lvds_init - setup LVDS connectors on this device
* @ dev : drm device
*
* Create the connector , register the LVDS DDC bus , and try to figure out what
* modes we can display on the LVDS panel ( if present ) .
*/
void psb_intel_lvds_init ( struct drm_device * dev ,
2011-12-19 21:40:45 +00:00
struct psb_intel_mode_device * mode_dev )
2011-11-03 18:22:15 +00:00
{
2013-07-22 17:45:26 +02:00
struct gma_encoder * gma_encoder ;
2013-07-22 17:05:25 +02:00
struct gma_connector * gma_connector ;
2011-11-03 18:22:15 +00:00
struct psb_intel_lvds_priv * lvds_priv ;
struct drm_connector * connector ;
struct drm_encoder * encoder ;
struct drm_display_mode * scan ; /* *modes, *bios_mode; */
struct drm_crtc * crtc ;
2011-11-29 22:26:58 +00:00
struct drm_psb_private * dev_priv = dev - > dev_private ;
2011-11-03 18:22:15 +00:00
u32 lvds ;
int pipe ;
2013-07-22 17:45:26 +02:00
gma_encoder = kzalloc ( sizeof ( struct gma_encoder ) , GFP_KERNEL ) ;
if ( ! gma_encoder ) {
dev_err ( dev - > dev , " gma_encoder allocation error \n " ) ;
2011-11-03 18:22:15 +00:00
return ;
2011-12-19 21:40:45 +00:00
}
2013-07-22 17:05:25 +02:00
gma_connector = kzalloc ( sizeof ( struct gma_connector ) , GFP_KERNEL ) ;
if ( ! gma_connector ) {
dev_err ( dev - > dev , " gma_connector allocation error \n " ) ;
2012-03-08 16:00:58 +00:00
goto failed_encoder ;
2011-12-19 21:40:45 +00:00
}
2011-11-03 18:22:15 +00:00
lvds_priv = kzalloc ( sizeof ( struct psb_intel_lvds_priv ) , GFP_KERNEL ) ;
if ( ! lvds_priv ) {
dev_err ( dev - > dev , " LVDS private allocation error \n " ) ;
2011-12-19 21:40:45 +00:00
goto failed_connector ;
2011-11-03 18:22:15 +00:00
}
2013-07-22 17:45:26 +02:00
gma_encoder - > dev_priv = lvds_priv ;
2011-11-29 22:26:58 +00:00
2013-07-22 17:05:25 +02:00
connector = & gma_connector - > base ;
2013-07-22 17:45:26 +02:00
encoder = & gma_encoder - > base ;
2011-12-19 21:40:45 +00:00
drm_connector_init ( dev , connector ,
2011-11-03 18:22:15 +00:00
& psb_intel_lvds_connector_funcs ,
DRM_MODE_CONNECTOR_LVDS ) ;
2011-12-19 21:40:45 +00:00
drm_encoder_init ( dev , encoder ,
2011-11-03 18:22:15 +00:00
& psb_intel_lvds_enc_funcs ,
DRM_MODE_ENCODER_LVDS ) ;
2013-07-22 17:45:26 +02:00
gma_connector_attach_encoder ( gma_connector , gma_encoder ) ;
gma_encoder - > type = INTEL_OUTPUT_LVDS ;
2011-11-03 18:22:15 +00:00
drm_encoder_helper_add ( encoder , & psb_intel_lvds_helper_funcs ) ;
drm_connector_helper_add ( connector ,
& psb_intel_lvds_connector_helper_funcs ) ;
connector - > display_info . subpixel_order = SubPixelHorizontalRGB ;
connector - > interlace_allowed = false ;
connector - > doublescan_allowed = false ;
/*Attach connector properties*/
2012-10-11 20:38:23 -05:00
drm_object_attach_property ( & connector - > base ,
2011-11-03 18:22:15 +00:00
dev - > mode_config . scaling_mode_property ,
DRM_MODE_SCALE_FULLSCREEN ) ;
2012-10-11 20:38:23 -05:00
drm_object_attach_property ( & connector - > base ,
2011-11-03 18:22:15 +00:00
dev_priv - > backlight_property ,
BRIGHTNESS_MAX_LEVEL ) ;
/*
* Set up I2C bus
* FIXME : distroy i2c_bus when exit
*/
2011-12-19 21:40:45 +00:00
lvds_priv - > i2c_bus = psb_intel_i2c_create ( dev , GPIOB , " LVDSBLC_B " ) ;
if ( ! lvds_priv - > i2c_bus ) {
2011-11-03 18:22:15 +00:00
dev_printk ( KERN_ERR ,
& dev - > pdev - > dev , " I2C bus registration failed. \n " ) ;
goto failed_blc_i2c ;
}
2011-12-19 21:40:45 +00:00
lvds_priv - > i2c_bus - > slave_addr = 0x2C ;
dev_priv - > lvds_i2c_bus = lvds_priv - > i2c_bus ;
2011-11-03 18:22:15 +00:00
/*
* LVDS discovery :
* 1 ) check for EDID on DDC
* 2 ) check for VBT data
* 3 ) check to see if LVDS is already on
* if none of the above , no panel
* 4 ) make sure lid is open
* if closed , act like it ' s not there for now
*/
/* Set up the DDC bus. */
2011-12-19 21:40:45 +00:00
lvds_priv - > ddc_bus = psb_intel_i2c_create ( dev , GPIOC , " LVDSDDC_C " ) ;
if ( ! lvds_priv - > ddc_bus ) {
2011-11-03 18:22:15 +00:00
dev_printk ( KERN_ERR , & dev - > pdev - > dev ,
" DDC bus registration " " failed. \n " ) ;
goto failed_ddc ;
}
/*
* Attempt to get the fixed panel mode from DDC . Assume that the
* preferred mode is the right one .
*/
2014-03-21 23:22:36 +01:00
mutex_lock ( & dev - > mode_config . mutex ) ;
2011-12-19 21:40:45 +00:00
psb_intel_ddc_get_modes ( connector , & lvds_priv - > ddc_bus - > adapter ) ;
2011-11-03 18:22:15 +00:00
list_for_each_entry ( scan , & connector - > probed_modes , head ) {
if ( scan - > type & DRM_MODE_TYPE_PREFERRED ) {
mode_dev - > panel_fixed_mode =
drm_mode_duplicate ( dev , scan ) ;
goto out ; /* FIXME: check for quirks */
}
}
/* Failed to get EDID, what about VBT? do we need this? */
if ( mode_dev - > vbt_mode )
mode_dev - > panel_fixed_mode =
drm_mode_duplicate ( dev , mode_dev - > vbt_mode ) ;
if ( ! mode_dev - > panel_fixed_mode )
if ( dev_priv - > lfp_lvds_vbt_mode )
mode_dev - > panel_fixed_mode =
drm_mode_duplicate ( dev ,
dev_priv - > lfp_lvds_vbt_mode ) ;
/*
* If we didn ' t get EDID , try checking if the panel is already turned
* on . If so , assume that whatever is currently programmed is the
* correct mode .
*/
lvds = REG_READ ( LVDS ) ;
pipe = ( lvds & LVDS_PIPEB_SELECT ) ? 1 : 0 ;
crtc = psb_intel_get_crtc_from_pipe ( dev , pipe ) ;
if ( crtc & & ( lvds & LVDS_PORT_EN ) ) {
mode_dev - > panel_fixed_mode =
psb_intel_crtc_mode_get ( dev , crtc ) ;
if ( mode_dev - > panel_fixed_mode ) {
mode_dev - > panel_fixed_mode - > type | =
DRM_MODE_TYPE_PREFERRED ;
goto out ; /* FIXME: check for quirks */
}
}
/* If we still don't have a mode after all that, give up. */
if ( ! mode_dev - > panel_fixed_mode ) {
dev_err ( dev - > dev , " Found no modes on the lvds, ignoring the LVDS \n " ) ;
goto failed_find ;
}
/*
* Blacklist machines with BIOSes that list an LVDS panel without
* actually having one .
*/
out :
2014-03-21 23:22:36 +01:00
mutex_unlock ( & dev - > mode_config . mutex ) ;
2011-11-03 18:22:15 +00:00
drm_sysfs_connector_add ( connector ) ;
return ;
failed_find :
2014-03-21 23:22:36 +01:00
mutex_unlock ( & dev - > mode_config . mutex ) ;
2011-12-19 21:40:45 +00:00
if ( lvds_priv - > ddc_bus )
psb_intel_i2c_destroy ( lvds_priv - > ddc_bus ) ;
2011-11-03 18:22:15 +00:00
failed_ddc :
2011-12-19 21:40:45 +00:00
if ( lvds_priv - > i2c_bus )
psb_intel_i2c_destroy ( lvds_priv - > i2c_bus ) ;
2011-11-03 18:22:15 +00:00
failed_blc_i2c :
drm_encoder_cleanup ( encoder ) ;
drm_connector_cleanup ( connector ) ;
2011-12-19 21:40:45 +00:00
failed_connector :
2013-07-22 17:05:25 +02:00
kfree ( gma_connector ) ;
2012-03-08 16:00:58 +00:00
failed_encoder :
2013-07-22 17:45:26 +02:00
kfree ( gma_encoder ) ;
2011-11-03 18:22:15 +00:00
}