2012-03-08 16:02:20 +00:00
/*
* Copyright © 2010 Intel Corporation
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE .
*
* Authors :
* jim liu < jim . liu @ intel . com >
* Jackie Li < yaodong . li @ intel . com >
*/
# include <linux/module.h>
# include "mdfld_dsi_output.h"
# include "mdfld_dsi_dpi.h"
# include "mdfld_output.h"
# include "mdfld_dsi_pkg_sender.h"
# include "tc35876x-dsi-lvds.h"
# include <linux/pm_runtime.h>
# include <asm/intel_scu_ipc.h>
/* get the LABC from command line. */
static int LABC_control = 1 ;
# ifdef MODULE
module_param ( LABC_control , int , 0644 ) ;
# else
static int __init parse_LABC_control ( char * arg )
{
/* LABC control can be passed in as a cmdline parameter */
/* to enable this feature add LABC=1 to cmdline */
/* to disable this feature add LABC=0 to cmdline */
if ( ! arg )
return - EINVAL ;
if ( ! strcasecmp ( arg , " 0 " ) )
LABC_control = 0 ;
else if ( ! strcasecmp ( arg , " 1 " ) )
LABC_control = 1 ;
return 0 ;
}
early_param ( " LABC " , parse_LABC_control ) ;
# endif
/**
* Check and see if the generic control or data buffer is empty and ready .
*/
void mdfld_dsi_gen_fifo_ready ( struct drm_device * dev , u32 gen_fifo_stat_reg ,
u32 fifo_stat )
{
u32 GEN_BF_time_out_count ;
/* Check MIPI Adatper command registers */
for ( GEN_BF_time_out_count = 0 ;
GEN_BF_time_out_count < GEN_FB_TIME_OUT ;
GEN_BF_time_out_count + + ) {
if ( ( REG_READ ( gen_fifo_stat_reg ) & fifo_stat ) = = fifo_stat )
break ;
udelay ( 100 ) ;
}
if ( GEN_BF_time_out_count = = GEN_FB_TIME_OUT )
DRM_ERROR ( " mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x. \n " ,
gen_fifo_stat_reg ) ;
}
/**
* Manage the DSI MIPI keyboard and display brightness .
* FIXME : this is exported to OSPM code . should work out an specific
* display interface to OSPM .
*/
void mdfld_dsi_brightness_init ( struct mdfld_dsi_config * dsi_config , int pipe )
{
struct mdfld_dsi_pkg_sender * sender =
mdfld_dsi_get_pkg_sender ( dsi_config ) ;
2013-02-25 04:01:48 +05:30
struct drm_device * dev ;
struct drm_psb_private * dev_priv ;
2012-03-08 16:02:20 +00:00
u32 gen_ctrl_val ;
if ( ! sender ) {
DRM_ERROR ( " No sender found \n " ) ;
return ;
}
2013-02-25 04:01:48 +05:30
dev = sender - > dev ;
dev_priv = dev - > dev_private ;
2012-03-08 16:02:20 +00:00
/* Set default display backlight value to 85% (0xd8)*/
mdfld_dsi_send_mcs_short ( sender , write_display_brightness , 0xd8 , 1 ,
true ) ;
/* Set minimum brightness setting of CABC function to 20% (0x33)*/
mdfld_dsi_send_mcs_short ( sender , write_cabc_min_bright , 0x33 , 1 , true ) ;
/* Enable backlight or/and LABC */
gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
BACKLIGHT_ON ;
if ( LABC_control = = 1 )
gen_ctrl_val | = DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
| GAMMA_AUTO ;
if ( LABC_control = = 1 )
gen_ctrl_val | = AMBIENT_LIGHT_SENSE_ON ;
dev_priv - > mipi_ctrl_display = gen_ctrl_val ;
mdfld_dsi_send_mcs_short ( sender , write_ctrl_display , ( u8 ) gen_ctrl_val ,
1 , true ) ;
mdfld_dsi_send_mcs_short ( sender , write_ctrl_cabc , UI_IMAGE , 1 , true ) ;
}
void mdfld_dsi_brightness_control ( struct drm_device * dev , int pipe , int level )
{
struct mdfld_dsi_pkg_sender * sender ;
struct drm_psb_private * dev_priv ;
struct mdfld_dsi_config * dsi_config ;
u32 gen_ctrl_val = 0 ;
int p_type = TMD_VID ;
if ( ! dev | | ( pipe ! = 0 & & pipe ! = 2 ) ) {
DRM_ERROR ( " Invalid parameter \n " ) ;
return ;
}
p_type = mdfld_get_panel_type ( dev , 0 ) ;
dev_priv = dev - > dev_private ;
if ( pipe )
dsi_config = dev_priv - > dsi_configs [ 1 ] ;
else
dsi_config = dev_priv - > dsi_configs [ 0 ] ;
sender = mdfld_dsi_get_pkg_sender ( dsi_config ) ;
if ( ! sender ) {
DRM_ERROR ( " No sender found \n " ) ;
return ;
}
gen_ctrl_val = ( level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL ) & 0xff ;
dev_dbg ( sender - > dev - > dev , " pipe = %d, gen_ctrl_val = %d. \n " ,
pipe , gen_ctrl_val ) ;
if ( p_type = = TMD_VID ) {
/* Set display backlight value */
mdfld_dsi_send_mcs_short ( sender , tmd_write_display_brightness ,
( u8 ) gen_ctrl_val , 1 , true ) ;
} else {
/* Set display backlight value */
mdfld_dsi_send_mcs_short ( sender , write_display_brightness ,
( u8 ) gen_ctrl_val , 1 , true ) ;
/* Enable backlight control */
if ( level = = 0 )
gen_ctrl_val = 0 ;
else
gen_ctrl_val = dev_priv - > mipi_ctrl_display ;
mdfld_dsi_send_mcs_short ( sender , write_ctrl_display ,
( u8 ) gen_ctrl_val , 1 , true ) ;
}
}
static int mdfld_dsi_get_panel_status ( struct mdfld_dsi_config * dsi_config ,
u8 dcs , u32 * data , bool hs )
{
struct mdfld_dsi_pkg_sender * sender
= mdfld_dsi_get_pkg_sender ( dsi_config ) ;
if ( ! sender | | ! data ) {
DRM_ERROR ( " Invalid parameter \n " ) ;
return - EINVAL ;
}
return mdfld_dsi_read_mcs ( sender , dcs , data , 1 , hs ) ;
}
int mdfld_dsi_get_power_mode ( struct mdfld_dsi_config * dsi_config , u32 * mode ,
bool hs )
{
if ( ! dsi_config | | ! mode ) {
DRM_ERROR ( " Invalid parameter \n " ) ;
return - EINVAL ;
}
return mdfld_dsi_get_panel_status ( dsi_config , 0x0a , mode , hs ) ;
}
/*
* NOTE : this function was used by OSPM .
* TODO : will be removed later , should work out display interfaces for OSPM
*/
void mdfld_dsi_controller_init ( struct mdfld_dsi_config * dsi_config , int pipe )
{
if ( ! dsi_config | | ( ( pipe ! = 0 ) & & ( pipe ! = 2 ) ) ) {
DRM_ERROR ( " Invalid parameters \n " ) ;
return ;
}
mdfld_dsi_dpi_controller_init ( dsi_config , pipe ) ;
}
static void mdfld_dsi_connector_save ( struct drm_connector * connector )
{
}
static void mdfld_dsi_connector_restore ( struct drm_connector * connector )
{
}
/* FIXME: start using the force parameter */
static enum drm_connector_status
mdfld_dsi_connector_detect ( struct drm_connector * connector , bool force )
{
struct mdfld_dsi_connector * dsi_connector
= mdfld_dsi_connector ( connector ) ;
dsi_connector - > status = connector_status_connected ;
return dsi_connector - > status ;
}
static int mdfld_dsi_connector_set_property ( struct drm_connector * connector ,
struct drm_property * property ,
uint64_t value )
{
struct drm_encoder * encoder = connector - > encoder ;
if ( ! strcmp ( property - > name , " scaling mode " ) & & encoder ) {
struct psb_intel_crtc * psb_crtc =
to_psb_intel_crtc ( encoder - > crtc ) ;
bool centerechange ;
uint64_t val ;
if ( ! psb_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 , property , & val ) )
2012-03-08 16:02:20 +00:00
goto set_prop_error ;
if ( val = = value )
goto set_prop_done ;
2012-10-11 20:38:23 -05:00
if ( drm_object_property_set_value ( & connector - > base ,
2012-03-08 16:02:20 +00:00
property , value ) )
goto set_prop_error ;
centerechange = ( val = = DRM_MODE_SCALE_NO_SCALE ) | |
( value = = DRM_MODE_SCALE_NO_SCALE ) ;
if ( psb_crtc - > saved_mode . hdisplay ! = 0 & &
psb_crtc - > saved_mode . vdisplay ! = 0 ) {
if ( centerechange ) {
if ( ! drm_crtc_helper_set_mode ( encoder - > crtc ,
& psb_crtc - > saved_mode ,
encoder - > crtc - > x ,
encoder - > crtc - > y ,
encoder - > crtc - > fb ) )
goto set_prop_error ;
} else {
struct drm_encoder_helper_funcs * funcs =
encoder - > helper_private ;
funcs - > mode_set ( encoder ,
& psb_crtc - > saved_mode ,
& psb_crtc - > saved_adjusted_mode ) ;
}
}
} else if ( ! strcmp ( property - > name , " backlight " ) & & encoder ) {
2012-10-11 20:38:23 -05:00
if ( drm_object_property_set_value ( & connector - > base , property ,
2012-03-08 16:02:20 +00:00
value ) )
goto set_prop_error ;
2012-08-08 13:55:55 +00:00
else
gma_backlight_set ( encoder - > dev , value ) ;
2012-03-08 16:02:20 +00:00
}
set_prop_done :
return 0 ;
set_prop_error :
return - 1 ;
}
static void mdfld_dsi_connector_destroy ( struct drm_connector * connector )
{
struct mdfld_dsi_connector * dsi_connector =
mdfld_dsi_connector ( connector ) ;
struct mdfld_dsi_pkg_sender * sender ;
if ( ! dsi_connector )
return ;
drm_sysfs_connector_remove ( connector ) ;
drm_connector_cleanup ( connector ) ;
sender = dsi_connector - > pkg_sender ;
mdfld_dsi_pkg_sender_destroy ( sender ) ;
kfree ( dsi_connector ) ;
}
static int mdfld_dsi_connector_get_modes ( struct drm_connector * connector )
{
struct mdfld_dsi_connector * dsi_connector =
mdfld_dsi_connector ( connector ) ;
struct mdfld_dsi_config * dsi_config =
mdfld_dsi_get_config ( dsi_connector ) ;
struct drm_display_mode * fixed_mode = dsi_config - > fixed_mode ;
struct drm_display_mode * dup_mode = NULL ;
struct drm_device * dev = connector - > dev ;
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 ( fixed_mode ) {
dev_dbg ( dev - > dev , " fixed_mode %dx%d \n " ,
fixed_mode - > hdisplay , fixed_mode - > vdisplay ) ;
dup_mode = drm_mode_duplicate ( dev , fixed_mode ) ;
drm_mode_probed_add ( connector , dup_mode ) ;
return 1 ;
}
DRM_ERROR ( " Didn't get any modes! \n " ) ;
return 0 ;
}
static int mdfld_dsi_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
struct mdfld_dsi_connector * dsi_connector =
mdfld_dsi_connector ( connector ) ;
struct mdfld_dsi_config * dsi_config =
mdfld_dsi_get_config ( dsi_connector ) ;
struct drm_display_mode * fixed_mode = dsi_config - > fixed_mode ;
if ( mode - > flags & DRM_MODE_FLAG_DBLSCAN )
return MODE_NO_DBLESCAN ;
if ( mode - > flags & DRM_MODE_FLAG_INTERLACE )
return MODE_NO_INTERLACE ;
/**
* FIXME : current DC has no fitting unit , reject any mode setting
* request
* Will figure out a way to do up - scaling ( pannel fitting ) later .
* */
if ( fixed_mode ) {
if ( mode - > hdisplay ! = fixed_mode - > hdisplay )
return MODE_PANEL ;
if ( mode - > vdisplay ! = fixed_mode - > vdisplay )
return MODE_PANEL ;
}
return MODE_OK ;
}
static void mdfld_dsi_connector_dpms ( struct drm_connector * connector , int mode )
{
if ( mode = = connector - > dpms )
return ;
/*first, execute dpms*/
drm_helper_connector_dpms ( connector , mode ) ;
}
static struct drm_encoder * mdfld_dsi_connector_best_encoder (
struct drm_connector * connector )
{
struct mdfld_dsi_connector * dsi_connector =
mdfld_dsi_connector ( connector ) ;
struct mdfld_dsi_config * dsi_config =
mdfld_dsi_get_config ( dsi_connector ) ;
return & dsi_config - > encoder - > base . base ;
}
/*DSI connector funcs*/
static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
. dpms = /*drm_helper_connector_dpms*/ mdfld_dsi_connector_dpms ,
. save = mdfld_dsi_connector_save ,
. restore = mdfld_dsi_connector_restore ,
. detect = mdfld_dsi_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. set_property = mdfld_dsi_connector_set_property ,
. destroy = mdfld_dsi_connector_destroy ,
} ;
/*DSI connector helper funcs*/
static const struct drm_connector_helper_funcs
mdfld_dsi_connector_helper_funcs = {
. get_modes = mdfld_dsi_connector_get_modes ,
. mode_valid = mdfld_dsi_connector_mode_valid ,
. best_encoder = mdfld_dsi_connector_best_encoder ,
} ;
static int mdfld_dsi_get_default_config ( struct drm_device * dev ,
struct mdfld_dsi_config * config , int pipe )
{
if ( ! dev | | ! config ) {
DRM_ERROR ( " Invalid parameters " ) ;
return - EINVAL ;
}
config - > bpp = 24 ;
if ( mdfld_get_panel_type ( dev , pipe ) = = TC35876X )
config - > lane_count = 4 ;
else
config - > lane_count = 2 ;
config - > channel_num = 0 ;
if ( mdfld_get_panel_type ( dev , pipe ) = = TMD_VID )
config - > video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE ;
else if ( mdfld_get_panel_type ( dev , pipe ) = = TC35876X )
config - > video_mode =
MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS ;
else
config - > video_mode = MDFLD_DSI_VIDEO_BURST_MODE ;
return 0 ;
}
int mdfld_dsi_panel_reset ( int pipe )
{
unsigned gpio ;
int ret = 0 ;
switch ( pipe ) {
case 0 :
gpio = 128 ;
break ;
case 2 :
gpio = 34 ;
break ;
default :
DRM_ERROR ( " Invalid output \n " ) ;
return - EINVAL ;
}
ret = gpio_request ( gpio , " gfx " ) ;
if ( ret ) {
DRM_ERROR ( " gpio_rqueset failed \n " ) ;
return ret ;
}
ret = gpio_direction_output ( gpio , 1 ) ;
if ( ret ) {
DRM_ERROR ( " gpio_direction_output failed \n " ) ;
goto gpio_error ;
}
gpio_get_value ( 128 ) ;
gpio_error :
if ( gpio_is_valid ( gpio ) )
gpio_free ( gpio ) ;
return ret ;
}
/*
* MIPI output init
* @ dev drm device
* @ pipe pipe number . 0 or 2
* @ config
*
* Do the initialization of a MIPI output , including create DRM mode objects
* initialization of DSI output on @ pipe
*/
void mdfld_dsi_output_init ( struct drm_device * dev ,
int pipe ,
const struct panel_funcs * p_vid_funcs )
{
struct mdfld_dsi_config * dsi_config ;
struct mdfld_dsi_connector * dsi_connector ;
struct drm_connector * connector ;
struct mdfld_dsi_encoder * encoder ;
struct drm_psb_private * dev_priv = dev - > dev_private ;
struct panel_info dsi_panel_info ;
u32 width_mm , height_mm ;
dev_dbg ( dev - > dev , " init DSI output on pipe %d \n " , pipe ) ;
2012-11-06 14:34:25 +00:00
if ( pipe ! = 0 & & pipe ! = 2 ) {
2012-03-08 16:02:20 +00:00
DRM_ERROR ( " Invalid parameter \n " ) ;
return ;
}
/*create a new connetor*/
dsi_connector = kzalloc ( sizeof ( struct mdfld_dsi_connector ) , GFP_KERNEL ) ;
if ( ! dsi_connector ) {
DRM_ERROR ( " No memory " ) ;
return ;
}
dsi_connector - > pipe = pipe ;
2012-03-08 16:02:50 +00:00
dsi_config = kzalloc ( sizeof ( struct mdfld_dsi_config ) ,
GFP_KERNEL ) ;
if ( ! dsi_config ) {
DRM_ERROR ( " cannot allocate memory for DSI config \n " ) ;
goto dsi_init_err0 ;
2012-03-08 16:02:20 +00:00
}
2012-03-08 16:02:50 +00:00
mdfld_dsi_get_default_config ( dev , dsi_config , pipe ) ;
2012-03-08 16:02:20 +00:00
dsi_connector - > private = dsi_config ;
dsi_config - > changed = 1 ;
dsi_config - > dev = dev ;
dsi_config - > fixed_mode = p_vid_funcs - > get_config_mode ( dev ) ;
if ( p_vid_funcs - > get_panel_info ( dev , pipe , & dsi_panel_info ) )
goto dsi_init_err0 ;
width_mm = dsi_panel_info . width_mm ;
height_mm = dsi_panel_info . height_mm ;
dsi_config - > mode = dsi_config - > fixed_mode ;
dsi_config - > connector = dsi_connector ;
if ( ! dsi_config - > fixed_mode ) {
DRM_ERROR ( " No pannel fixed mode was found \n " ) ;
goto dsi_init_err0 ;
}
if ( pipe & & dev_priv - > dsi_configs [ 0 ] ) {
dsi_config - > dvr_ic_inited = 0 ;
dev_priv - > dsi_configs [ 1 ] = dsi_config ;
} else if ( pipe = = 0 ) {
dsi_config - > dvr_ic_inited = 1 ;
dev_priv - > dsi_configs [ 0 ] = dsi_config ;
} else {
DRM_ERROR ( " Trying to init MIPI1 before MIPI0 \n " ) ;
goto dsi_init_err0 ;
}
connector = & dsi_connector - > base . base ;
drm_connector_init ( dev , connector , & mdfld_dsi_connector_funcs ,
DRM_MODE_CONNECTOR_LVDS ) ;
drm_connector_helper_add ( connector , & mdfld_dsi_connector_helper_funcs ) ;
connector - > display_info . subpixel_order = SubPixelHorizontalRGB ;
connector - > display_info . width_mm = width_mm ;
connector - > display_info . height_mm = height_mm ;
connector - > interlace_allowed = false ;
connector - > doublescan_allowed = false ;
/*attach properties*/
2012-10-11 20:38:23 -05:00
drm_object_attach_property ( & connector - > base ,
2012-03-08 16:02:20 +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 ,
2012-03-08 16:02:20 +00:00
dev_priv - > backlight_property ,
MDFLD_DSI_BRIGHTNESS_MAX_LEVEL ) ;
/*init DSI package sender on this output*/
if ( mdfld_dsi_pkg_sender_init ( dsi_connector , pipe ) ) {
DRM_ERROR ( " Package Sender initialization failed on pipe %d \n " ,
pipe ) ;
goto dsi_init_err0 ;
}
encoder = mdfld_dsi_dpi_init ( dev , dsi_connector , p_vid_funcs ) ;
if ( ! encoder ) {
DRM_ERROR ( " Create DPI encoder failed \n " ) ;
goto dsi_init_err1 ;
}
encoder - > private = dsi_config ;
dsi_config - > encoder = encoder ;
encoder - > base . type = ( pipe = = 0 ) ? INTEL_OUTPUT_MIPI :
INTEL_OUTPUT_MIPI2 ;
drm_sysfs_connector_add ( connector ) ;
return ;
/*TODO: add code to destroy outputs on error*/
dsi_init_err1 :
/*destroy sender*/
mdfld_dsi_pkg_sender_destroy ( dsi_connector - > pkg_sender ) ;
drm_connector_cleanup ( connector ) ;
kfree ( dsi_config - > fixed_mode ) ;
kfree ( dsi_config ) ;
dsi_init_err0 :
kfree ( dsi_connector ) ;
}