2013-11-15 16:06:05 +01:00
/*
* Copyright ( C ) 2013 NVIDIA Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/clk.h>
2014-01-31 10:02:15 +01:00
# include <linux/debugfs.h>
2014-11-28 15:41:34 +01:00
# include <linux/gpio.h>
2013-11-15 16:06:05 +01:00
# include <linux/io.h>
# include <linux/platform_device.h>
# include <linux/reset.h>
2014-07-17 13:17:24 +02:00
2014-07-11 13:19:06 +02:00
# include <soc/tegra/pmc.h>
2013-11-15 16:06:05 +01:00
2014-11-24 16:27:13 +01:00
# include <drm/drm_atomic_helper.h>
2013-11-15 16:06:05 +01:00
# include <drm/drm_dp_helper.h>
2014-11-28 15:41:34 +01:00
# include <drm/drm_panel.h>
2013-11-15 16:06:05 +01:00
# include "dc.h"
# include "drm.h"
# include "sor.h"
struct tegra_sor {
struct host1x_client client ;
struct tegra_output output ;
struct device * dev ;
void __iomem * regs ;
struct reset_control * rst ;
struct clk * clk_parent ;
struct clk * clk_safe ;
struct clk * clk_dp ;
struct clk * clk ;
struct tegra_dpaux * dpaux ;
2014-03-26 11:13:16 +01:00
struct mutex lock ;
2013-11-15 16:06:05 +01:00
bool enabled ;
2014-01-31 10:02:15 +01:00
struct dentry * debugfs ;
2013-11-15 16:06:05 +01:00
} ;
2014-06-05 16:31:10 +02:00
struct tegra_sor_config {
u32 bits_per_pixel ;
u32 active_polarity ;
u32 active_count ;
u32 tu_size ;
u32 active_frac ;
u32 watermark ;
2014-06-05 16:12:46 +02:00
u32 hblank_symbols ;
u32 vblank_symbols ;
2014-06-05 16:31:10 +02:00
} ;
2013-11-15 16:06:05 +01:00
static inline struct tegra_sor *
host1x_client_to_sor ( struct host1x_client * client )
{
return container_of ( client , struct tegra_sor , client ) ;
}
static inline struct tegra_sor * to_sor ( struct tegra_output * output )
{
return container_of ( output , struct tegra_sor , output ) ;
}
static inline unsigned long tegra_sor_readl ( struct tegra_sor * sor ,
unsigned long offset )
{
return readl ( sor - > regs + ( offset < < 2 ) ) ;
}
static inline void tegra_sor_writel ( struct tegra_sor * sor , unsigned long value ,
unsigned long offset )
{
writel ( value , sor - > regs + ( offset < < 2 ) ) ;
}
static int tegra_sor_dp_train_fast ( struct tegra_sor * sor ,
struct drm_dp_link * link )
{
unsigned long value ;
unsigned int i ;
u8 pattern ;
int err ;
/* setup lane parameters */
value = SOR_LANE_DRIVE_CURRENT_LANE3 ( 0x40 ) |
SOR_LANE_DRIVE_CURRENT_LANE2 ( 0x40 ) |
SOR_LANE_DRIVE_CURRENT_LANE1 ( 0x40 ) |
SOR_LANE_DRIVE_CURRENT_LANE0 ( 0x40 ) ;
tegra_sor_writel ( sor , value , SOR_LANE_DRIVE_CURRENT_0 ) ;
value = SOR_LANE_PREEMPHASIS_LANE3 ( 0x0f ) |
SOR_LANE_PREEMPHASIS_LANE2 ( 0x0f ) |
SOR_LANE_PREEMPHASIS_LANE1 ( 0x0f ) |
SOR_LANE_PREEMPHASIS_LANE0 ( 0x0f ) ;
tegra_sor_writel ( sor , value , SOR_LANE_PREEMPHASIS_0 ) ;
value = SOR_LANE_POST_CURSOR_LANE3 ( 0x00 ) |
SOR_LANE_POST_CURSOR_LANE2 ( 0x00 ) |
SOR_LANE_POST_CURSOR_LANE1 ( 0x00 ) |
SOR_LANE_POST_CURSOR_LANE0 ( 0x00 ) ;
tegra_sor_writel ( sor , value , SOR_LANE_POST_CURSOR_0 ) ;
/* disable LVDS mode */
tegra_sor_writel ( sor , 0 , SOR_LVDS ) ;
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value | = SOR_DP_PADCTL_TX_PU_ENABLE ;
value & = ~ SOR_DP_PADCTL_TX_PU_MASK ;
value | = SOR_DP_PADCTL_TX_PU ( 2 ) ; /* XXX: don't hardcode? */
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value | = SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0 ;
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
usleep_range ( 10 , 100 ) ;
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value & = ~ ( SOR_DP_PADCTL_CM_TXD_3 | SOR_DP_PADCTL_CM_TXD_2 |
SOR_DP_PADCTL_CM_TXD_1 | SOR_DP_PADCTL_CM_TXD_0 ) ;
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
err = tegra_dpaux_prepare ( sor - > dpaux , DP_SET_ANSI_8B10B ) ;
if ( err < 0 )
return err ;
for ( i = 0 , value = 0 ; i < link - > num_lanes ; i + + ) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_NONE |
SOR_DP_TPG_PATTERN_TRAIN1 ;
value = ( value < < 8 ) | lane ;
}
tegra_sor_writel ( sor , value , SOR_DP_TPG ) ;
pattern = DP_TRAINING_PATTERN_1 ;
err = tegra_dpaux_train ( sor - > dpaux , link , pattern ) ;
if ( err < 0 )
return err ;
value = tegra_sor_readl ( sor , SOR_DP_SPARE_0 ) ;
value | = SOR_DP_SPARE_SEQ_ENABLE ;
value & = ~ SOR_DP_SPARE_PANEL_INTERNAL ;
value | = SOR_DP_SPARE_MACRO_SOR_CLK ;
tegra_sor_writel ( sor , value , SOR_DP_SPARE_0 ) ;
for ( i = 0 , value = 0 ; i < link - > num_lanes ; i + + ) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_NONE |
SOR_DP_TPG_PATTERN_TRAIN2 ;
value = ( value < < 8 ) | lane ;
}
tegra_sor_writel ( sor , value , SOR_DP_TPG ) ;
pattern = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_2 ;
err = tegra_dpaux_train ( sor - > dpaux , link , pattern ) ;
if ( err < 0 )
return err ;
for ( i = 0 , value = 0 ; i < link - > num_lanes ; i + + ) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_GALIOS |
SOR_DP_TPG_PATTERN_NONE ;
value = ( value < < 8 ) | lane ;
}
tegra_sor_writel ( sor , value , SOR_DP_TPG ) ;
pattern = DP_TRAINING_PATTERN_DISABLE ;
err = tegra_dpaux_train ( sor - > dpaux , link , pattern ) ;
if ( err < 0 )
return err ;
return 0 ;
}
static void tegra_sor_super_update ( struct tegra_sor * sor )
{
tegra_sor_writel ( sor , 0 , SOR_SUPER_STATE_0 ) ;
tegra_sor_writel ( sor , 1 , SOR_SUPER_STATE_0 ) ;
tegra_sor_writel ( sor , 0 , SOR_SUPER_STATE_0 ) ;
}
static void tegra_sor_update ( struct tegra_sor * sor )
{
tegra_sor_writel ( sor , 0 , SOR_STATE_0 ) ;
tegra_sor_writel ( sor , 1 , SOR_STATE_0 ) ;
tegra_sor_writel ( sor , 0 , SOR_STATE_0 ) ;
}
static int tegra_sor_setup_pwm ( struct tegra_sor * sor , unsigned long timeout )
{
unsigned long value ;
value = tegra_sor_readl ( sor , SOR_PWM_DIV ) ;
value & = ~ SOR_PWM_DIV_MASK ;
value | = 0x400 ; /* period */
tegra_sor_writel ( sor , value , SOR_PWM_DIV ) ;
value = tegra_sor_readl ( sor , SOR_PWM_CTL ) ;
value & = ~ SOR_PWM_CTL_DUTY_CYCLE_MASK ;
value | = 0x400 ; /* duty cycle */
value & = ~ SOR_PWM_CTL_CLK_SEL ; /* clock source: PCLK */
value | = SOR_PWM_CTL_TRIGGER ;
tegra_sor_writel ( sor , value , SOR_PWM_CTL ) ;
timeout = jiffies + msecs_to_jiffies ( timeout ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_PWM_CTL ) ;
if ( ( value & SOR_PWM_CTL_TRIGGER ) = = 0 )
return 0 ;
usleep_range ( 25 , 100 ) ;
}
return - ETIMEDOUT ;
}
static int tegra_sor_attach ( struct tegra_sor * sor )
{
unsigned long value , timeout ;
/* wake up in normal mode */
value = tegra_sor_readl ( sor , SOR_SUPER_STATE_1 ) ;
value | = SOR_SUPER_STATE_HEAD_MODE_AWAKE ;
value | = SOR_SUPER_STATE_MODE_NORMAL ;
tegra_sor_writel ( sor , value , SOR_SUPER_STATE_1 ) ;
tegra_sor_super_update ( sor ) ;
/* attach */
value = tegra_sor_readl ( sor , SOR_SUPER_STATE_1 ) ;
value | = SOR_SUPER_STATE_ATTACHED ;
tegra_sor_writel ( sor , value , SOR_SUPER_STATE_1 ) ;
tegra_sor_super_update ( sor ) ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_TEST ) ;
if ( ( value & SOR_TEST_ATTACHED ) ! = 0 )
return 0 ;
usleep_range ( 25 , 100 ) ;
}
return - ETIMEDOUT ;
}
static int tegra_sor_wakeup ( struct tegra_sor * sor )
{
unsigned long value , timeout ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
/* wait for head to wake up */
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_TEST ) ;
value & = SOR_TEST_HEAD_MODE_MASK ;
if ( value = = SOR_TEST_HEAD_MODE_AWAKE )
return 0 ;
usleep_range ( 25 , 100 ) ;
}
return - ETIMEDOUT ;
}
static int tegra_sor_power_up ( struct tegra_sor * sor , unsigned long timeout )
{
unsigned long value ;
value = tegra_sor_readl ( sor , SOR_PWR ) ;
value | = SOR_PWR_TRIGGER | SOR_PWR_NORMAL_STATE_PU ;
tegra_sor_writel ( sor , value , SOR_PWR ) ;
timeout = jiffies + msecs_to_jiffies ( timeout ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_PWR ) ;
if ( ( value & SOR_PWR_TRIGGER ) = = 0 )
return 0 ;
usleep_range ( 25 , 100 ) ;
}
return - ETIMEDOUT ;
}
2014-06-05 16:31:10 +02:00
struct tegra_sor_params {
/* number of link clocks per line */
unsigned int num_clocks ;
/* ratio between input and output */
u64 ratio ;
/* precision factor */
u64 precision ;
unsigned int active_polarity ;
unsigned int active_count ;
unsigned int active_frac ;
unsigned int tu_size ;
unsigned int error ;
} ;
static int tegra_sor_compute_params ( struct tegra_sor * sor ,
struct tegra_sor_params * params ,
unsigned int tu_size )
{
u64 active_sym , active_count , frac , approx ;
u32 active_polarity , active_frac = 0 ;
const u64 f = params - > precision ;
s64 error ;
active_sym = params - > ratio * tu_size ;
active_count = div_u64 ( active_sym , f ) * f ;
frac = active_sym - active_count ;
/* fraction < 0.5 */
if ( frac > = ( f / 2 ) ) {
active_polarity = 1 ;
frac = f - frac ;
} else {
active_polarity = 0 ;
}
if ( frac ! = 0 ) {
frac = div_u64 ( f * f , frac ) ; /* 1/fraction */
if ( frac < = ( 15 * f ) ) {
active_frac = div_u64 ( frac , f ) ;
/* round up */
if ( active_polarity )
active_frac + + ;
} else {
active_frac = active_polarity ? 1 : 15 ;
}
}
if ( active_frac = = 1 )
active_polarity = 0 ;
if ( active_polarity = = 1 ) {
if ( active_frac ) {
approx = active_count + ( active_frac * ( f - 1 ) ) * f ;
approx = div_u64 ( approx , active_frac * f ) ;
} else {
approx = active_count + f ;
}
} else {
if ( active_frac )
approx = active_count + div_u64 ( f , active_frac ) ;
else
approx = active_count ;
}
error = div_s64 ( active_sym - approx , tu_size ) ;
error * = params - > num_clocks ;
if ( error < = 0 & & abs64 ( error ) < params - > error ) {
params - > active_count = div_u64 ( active_count , f ) ;
params - > active_polarity = active_polarity ;
params - > active_frac = active_frac ;
params - > error = abs64 ( error ) ;
params - > tu_size = tu_size ;
if ( error = = 0 )
return true ;
}
return false ;
}
static int tegra_sor_calc_config ( struct tegra_sor * sor ,
struct drm_display_mode * mode ,
struct tegra_sor_config * config ,
struct drm_dp_link * link )
{
const u64 f = 100000 , link_rate = link - > rate * 1000 ;
const u64 pclk = mode - > clock * 1000 ;
2014-06-05 16:12:46 +02:00
u64 input , output , watermark , num ;
2014-06-05 16:31:10 +02:00
struct tegra_sor_params params ;
u32 num_syms_per_line ;
unsigned int i ;
if ( ! link_rate | | ! link - > num_lanes | | ! pclk | | ! config - > bits_per_pixel )
return - EINVAL ;
output = link_rate * 8 * link - > num_lanes ;
input = pclk * config - > bits_per_pixel ;
if ( input > = output )
return - ERANGE ;
memset ( & params , 0 , sizeof ( params ) ) ;
params . ratio = div64_u64 ( input * f , output ) ;
params . num_clocks = div_u64 ( link_rate * mode - > hdisplay , pclk ) ;
params . precision = f ;
params . error = 64 * f ;
params . tu_size = 64 ;
for ( i = params . tu_size ; i > = 32 ; i - - )
if ( tegra_sor_compute_params ( sor , & params , i ) )
break ;
if ( params . active_frac = = 0 ) {
config - > active_polarity = 0 ;
config - > active_count = params . active_count ;
if ( ! params . active_polarity )
config - > active_count - - ;
config - > tu_size = params . tu_size ;
config - > active_frac = 1 ;
} else {
config - > active_polarity = params . active_polarity ;
config - > active_count = params . active_count ;
config - > active_frac = params . active_frac ;
config - > tu_size = params . tu_size ;
}
dev_dbg ( sor - > dev ,
" polarity: %d active count: %d tu size: %d active frac: %d \n " ,
config - > active_polarity , config - > active_count ,
config - > tu_size , config - > active_frac ) ;
watermark = params . ratio * config - > tu_size * ( f - params . ratio ) ;
watermark = div_u64 ( watermark , f ) ;
watermark = div_u64 ( watermark + params . error , f ) ;
config - > watermark = watermark + ( config - > bits_per_pixel / 8 ) + 2 ;
num_syms_per_line = ( mode - > hdisplay * config - > bits_per_pixel ) *
( link - > num_lanes * 8 ) ;
if ( config - > watermark > 30 ) {
config - > watermark = 30 ;
dev_err ( sor - > dev ,
" unable to compute TU size, forcing watermark to %u \n " ,
config - > watermark ) ;
} else if ( config - > watermark > num_syms_per_line ) {
config - > watermark = num_syms_per_line ;
dev_err ( sor - > dev , " watermark too high, forcing to %u \n " ,
config - > watermark ) ;
}
2014-06-05 16:12:46 +02:00
/* compute the number of symbols per horizontal blanking interval */
num = ( ( mode - > htotal - mode - > hdisplay ) - 7 ) * link_rate ;
config - > hblank_symbols = div_u64 ( num , pclk ) ;
if ( link - > capabilities & DP_LINK_CAP_ENHANCED_FRAMING )
config - > hblank_symbols - = 3 ;
config - > hblank_symbols - = 12 / link - > num_lanes ;
/* compute the number of symbols per vertical blanking interval */
num = ( mode - > hdisplay - 25 ) * link_rate ;
config - > vblank_symbols = div_u64 ( num , pclk ) ;
config - > vblank_symbols - = 36 / link - > num_lanes + 4 ;
dev_dbg ( sor - > dev , " blank symbols: H:%u V:%u \n " , config - > hblank_symbols ,
config - > vblank_symbols ) ;
2014-06-05 16:31:10 +02:00
return 0 ;
}
2014-11-28 15:41:34 +01:00
static int tegra_sor_detach ( struct tegra_sor * sor )
{
unsigned long value , timeout ;
/* switch to safe mode */
value = tegra_sor_readl ( sor , SOR_SUPER_STATE_1 ) ;
value & = ~ SOR_SUPER_STATE_MODE_NORMAL ;
tegra_sor_writel ( sor , value , SOR_SUPER_STATE_1 ) ;
tegra_sor_super_update ( sor ) ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_PWR ) ;
if ( value & SOR_PWR_MODE_SAFE )
break ;
}
if ( ( value & SOR_PWR_MODE_SAFE ) = = 0 )
return - ETIMEDOUT ;
/* go to sleep */
value = tegra_sor_readl ( sor , SOR_SUPER_STATE_1 ) ;
value & = ~ SOR_SUPER_STATE_HEAD_MODE_MASK ;
tegra_sor_writel ( sor , value , SOR_SUPER_STATE_1 ) ;
tegra_sor_super_update ( sor ) ;
/* detach */
value = tegra_sor_readl ( sor , SOR_SUPER_STATE_1 ) ;
value & = ~ SOR_SUPER_STATE_ATTACHED ;
tegra_sor_writel ( sor , value , SOR_SUPER_STATE_1 ) ;
tegra_sor_super_update ( sor ) ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_TEST ) ;
if ( ( value & SOR_TEST_ATTACHED ) = = 0 )
break ;
usleep_range ( 25 , 100 ) ;
}
if ( ( value & SOR_TEST_ATTACHED ) ! = 0 )
return - ETIMEDOUT ;
return 0 ;
}
static int tegra_sor_power_down ( struct tegra_sor * sor )
{
unsigned long value , timeout ;
int err ;
value = tegra_sor_readl ( sor , SOR_PWR ) ;
value & = ~ SOR_PWR_NORMAL_STATE_PU ;
value | = SOR_PWR_TRIGGER ;
tegra_sor_writel ( sor , value , SOR_PWR ) ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_PWR ) ;
if ( ( value & SOR_PWR_TRIGGER ) = = 0 )
return 0 ;
usleep_range ( 25 , 100 ) ;
}
if ( ( value & SOR_PWR_TRIGGER ) ! = 0 )
return - ETIMEDOUT ;
err = clk_set_parent ( sor - > clk , sor - > clk_safe ) ;
if ( err < 0 )
dev_err ( sor - > dev , " failed to set safe parent clock: %d \n " , err ) ;
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value & = ~ ( SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 |
SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2 ) ;
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
/* stop lane sequencer */
value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP |
SOR_LANE_SEQ_CTL_POWER_STATE_DOWN ;
tegra_sor_writel ( sor , value , SOR_LANE_SEQ_CTL ) ;
timeout = jiffies + msecs_to_jiffies ( 250 ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_LANE_SEQ_CTL ) ;
if ( ( value & SOR_LANE_SEQ_CTL_TRIGGER ) = = 0 )
break ;
usleep_range ( 25 , 100 ) ;
}
if ( ( value & SOR_LANE_SEQ_CTL_TRIGGER ) ! = 0 )
return - ETIMEDOUT ;
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value | = SOR_PLL_2_PORT_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
usleep_range ( 20 , 100 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_0 ) ;
value | = SOR_PLL_0_POWER_OFF ;
value | = SOR_PLL_0_VCOPD ;
tegra_sor_writel ( sor , value , SOR_PLL_0 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value | = SOR_PLL_2_SEQ_PLLCAPPD ;
value | = SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
usleep_range ( 20 , 100 ) ;
return 0 ;
}
static int tegra_sor_crc_open ( struct inode * inode , struct file * file )
{
file - > private_data = inode - > i_private ;
return 0 ;
}
static int tegra_sor_crc_release ( struct inode * inode , struct file * file )
{
return 0 ;
}
static int tegra_sor_crc_wait ( struct tegra_sor * sor , unsigned long timeout )
{
u32 value ;
timeout = jiffies + msecs_to_jiffies ( timeout ) ;
while ( time_before ( jiffies , timeout ) ) {
value = tegra_sor_readl ( sor , SOR_CRC_A ) ;
if ( value & SOR_CRC_A_VALID )
return 0 ;
usleep_range ( 100 , 200 ) ;
}
return - ETIMEDOUT ;
}
static ssize_t tegra_sor_crc_read ( struct file * file , char __user * buffer ,
size_t size , loff_t * ppos )
{
struct tegra_sor * sor = file - > private_data ;
ssize_t num , err ;
char buf [ 10 ] ;
u32 value ;
mutex_lock ( & sor - > lock ) ;
if ( ! sor - > enabled ) {
err = - EAGAIN ;
goto unlock ;
}
value = tegra_sor_readl ( sor , SOR_STATE_1 ) ;
value & = ~ SOR_STATE_ASY_CRC_MODE_MASK ;
tegra_sor_writel ( sor , value , SOR_STATE_1 ) ;
value = tegra_sor_readl ( sor , SOR_CRC_CNTRL ) ;
value | = SOR_CRC_CNTRL_ENABLE ;
tegra_sor_writel ( sor , value , SOR_CRC_CNTRL ) ;
value = tegra_sor_readl ( sor , SOR_TEST ) ;
value & = ~ SOR_TEST_CRC_POST_SERIALIZE ;
tegra_sor_writel ( sor , value , SOR_TEST ) ;
err = tegra_sor_crc_wait ( sor , 100 ) ;
if ( err < 0 )
goto unlock ;
tegra_sor_writel ( sor , SOR_CRC_A_RESET , SOR_CRC_A ) ;
value = tegra_sor_readl ( sor , SOR_CRC_B ) ;
num = scnprintf ( buf , sizeof ( buf ) , " %08x \n " , value ) ;
err = simple_read_from_buffer ( buffer , size , ppos , buf , num ) ;
unlock :
mutex_unlock ( & sor - > lock ) ;
return err ;
}
static const struct file_operations tegra_sor_crc_fops = {
. owner = THIS_MODULE ,
. open = tegra_sor_crc_open ,
. read = tegra_sor_crc_read ,
. release = tegra_sor_crc_release ,
} ;
static int tegra_sor_debugfs_init ( struct tegra_sor * sor ,
struct drm_minor * minor )
2013-11-15 16:06:05 +01:00
{
2014-11-28 15:41:34 +01:00
struct dentry * entry ;
int err = 0 ;
sor - > debugfs = debugfs_create_dir ( " sor " , minor - > debugfs_root ) ;
if ( ! sor - > debugfs )
return - ENOMEM ;
entry = debugfs_create_file ( " crc " , 0644 , sor - > debugfs , sor ,
& tegra_sor_crc_fops ) ;
if ( ! entry ) {
dev_err ( sor - > dev ,
" cannot create /sys/kernel/debug/dri/%s/sor/crc \n " ,
minor - > debugfs_root - > d_name . name ) ;
err = - ENOMEM ;
goto remove ;
}
return err ;
remove :
debugfs_remove ( sor - > debugfs ) ;
sor - > debugfs = NULL ;
return err ;
}
2014-12-19 15:47:30 +01:00
static void tegra_sor_debugfs_exit ( struct tegra_sor * sor )
2014-11-28 15:41:34 +01:00
{
debugfs_remove_recursive ( sor - > debugfs ) ;
sor - > debugfs = NULL ;
}
static void tegra_sor_connector_dpms ( struct drm_connector * connector , int mode )
{
}
static enum drm_connector_status
tegra_sor_connector_detect ( struct drm_connector * connector , bool force )
{
struct tegra_output * output = connector_to_output ( connector ) ;
struct tegra_sor * sor = to_sor ( output ) ;
if ( sor - > dpaux )
return tegra_dpaux_detect ( sor - > dpaux ) ;
return connector_status_unknown ;
}
static const struct drm_connector_funcs tegra_sor_connector_funcs = {
. dpms = tegra_sor_connector_dpms ,
2014-11-24 17:02:53 +01:00
. reset = drm_atomic_helper_connector_reset ,
2014-11-28 15:41:34 +01:00
. detect = tegra_sor_connector_detect ,
. fill_modes = drm_helper_probe_single_connector_modes ,
. destroy = tegra_output_connector_destroy ,
2014-11-24 17:02:53 +01:00
. atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state ,
2014-11-24 16:27:13 +01:00
. atomic_destroy_state = drm_atomic_helper_connector_destroy_state ,
2014-11-28 15:41:34 +01:00
} ;
static int tegra_sor_connector_get_modes ( struct drm_connector * connector )
{
struct tegra_output * output = connector_to_output ( connector ) ;
struct tegra_sor * sor = to_sor ( output ) ;
int err ;
if ( sor - > dpaux )
tegra_dpaux_enable ( sor - > dpaux ) ;
err = tegra_output_connector_get_modes ( connector ) ;
if ( sor - > dpaux )
tegra_dpaux_disable ( sor - > dpaux ) ;
return err ;
}
static enum drm_mode_status
tegra_sor_connector_mode_valid ( struct drm_connector * connector ,
struct drm_display_mode * mode )
{
return MODE_OK ;
}
static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
. get_modes = tegra_sor_connector_get_modes ,
. mode_valid = tegra_sor_connector_mode_valid ,
. best_encoder = tegra_output_connector_best_encoder ,
} ;
static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
. destroy = tegra_output_encoder_destroy ,
} ;
static void tegra_sor_encoder_dpms ( struct drm_encoder * encoder , int mode )
{
}
static void tegra_sor_encoder_prepare ( struct drm_encoder * encoder )
{
}
static void tegra_sor_encoder_commit ( struct drm_encoder * encoder )
{
}
static void tegra_sor_encoder_mode_set ( struct drm_encoder * encoder ,
struct drm_display_mode * mode ,
struct drm_display_mode * adjusted )
{
struct tegra_output * output = encoder_to_output ( encoder ) ;
struct tegra_dc * dc = to_tegra_dc ( encoder - > crtc ) ;
2013-11-15 16:06:05 +01:00
unsigned int vbe , vse , hbe , hse , vbs , hbs , i ;
struct tegra_sor * sor = to_sor ( output ) ;
2014-06-05 16:31:10 +02:00
struct tegra_sor_config config ;
struct drm_dp_link link ;
struct drm_dp_aux * aux ;
2013-11-15 16:06:05 +01:00
unsigned long value ;
2014-03-26 11:13:16 +01:00
int err = 0 ;
mutex_lock ( & sor - > lock ) ;
2013-11-15 16:06:05 +01:00
if ( sor - > enabled )
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
err = clk_prepare_enable ( sor - > clk ) ;
if ( err < 0 )
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
reset_control_deassert ( sor - > rst ) ;
2014-11-28 15:41:34 +01:00
if ( output - > panel )
drm_panel_prepare ( output - > panel ) ;
2014-06-05 16:31:10 +02:00
/* FIXME: properly convert to struct drm_dp_aux */
aux = ( struct drm_dp_aux * ) sor - > dpaux ;
2013-11-15 16:06:05 +01:00
if ( sor - > dpaux ) {
err = tegra_dpaux_enable ( sor - > dpaux ) ;
if ( err < 0 )
dev_err ( sor - > dev , " failed to enable DP: %d \n " , err ) ;
2014-06-05 16:31:10 +02:00
err = drm_dp_link_probe ( aux , & link ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to probe eDP link: %d \n " ,
err ) ;
2014-06-11 10:06:09 +03:00
goto unlock ;
2014-06-05 16:31:10 +02:00
}
2013-11-15 16:06:05 +01:00
}
err = clk_set_parent ( sor - > clk , sor - > clk_safe ) ;
if ( err < 0 )
dev_err ( sor - > dev , " failed to set safe parent clock: %d \n " , err ) ;
2014-06-05 16:31:10 +02:00
memset ( & config , 0 , sizeof ( config ) ) ;
2014-06-19 18:18:29 -07:00
config . bits_per_pixel = output - > connector . display_info . bpc * 3 ;
2014-06-05 16:31:10 +02:00
err = tegra_sor_calc_config ( sor , mode , & config , & link ) ;
if ( err < 0 )
dev_err ( sor - > dev , " failed to compute link configuration: %d \n " ,
err ) ;
2013-11-15 16:06:05 +01:00
value = tegra_sor_readl ( sor , SOR_CLK_CNTRL ) ;
value & = ~ SOR_CLK_CNTRL_DP_CLK_SEL_MASK ;
value | = SOR_CLK_CNTRL_DP_CLK_SEL_SINGLE_DPCLK ;
tegra_sor_writel ( sor , value , SOR_CLK_CNTRL ) ;
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value & = ~ SOR_PLL_2_BANDGAP_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
usleep_range ( 20 , 100 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_3 ) ;
value | = SOR_PLL_3_PLL_VDD_MODE_V3_3 ;
tegra_sor_writel ( sor , value , SOR_PLL_3 ) ;
value = SOR_PLL_0_ICHPMP ( 0xf ) | SOR_PLL_0_VCOCAP_RST |
SOR_PLL_0_PLLREG_LEVEL_V45 | SOR_PLL_0_RESISTOR_EXT ;
tegra_sor_writel ( sor , value , SOR_PLL_0 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value | = SOR_PLL_2_SEQ_PLLCAPPD ;
value & = ~ SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE ;
value | = SOR_PLL_2_LVDS_ENABLE ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
value = SOR_PLL_1_TERM_COMPOUT | SOR_PLL_1_TMDS_TERM ;
tegra_sor_writel ( sor , value , SOR_PLL_1 ) ;
while ( true ) {
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
if ( ( value & SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE ) = = 0 )
break ;
usleep_range ( 250 , 1000 ) ;
}
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value & = ~ SOR_PLL_2_POWERDOWN_OVERRIDE ;
value & = ~ SOR_PLL_2_PORT_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
/*
* power up
*/
/* set safe link bandwidth (1.62 Gbps) */
value = tegra_sor_readl ( sor , SOR_CLK_CNTRL ) ;
value & = ~ SOR_CLK_CNTRL_DP_LINK_SPEED_MASK ;
value | = SOR_CLK_CNTRL_DP_LINK_SPEED_G1_62 ;
tegra_sor_writel ( sor , value , SOR_CLK_CNTRL ) ;
/* step 1 */
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value | = SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE | SOR_PLL_2_PORT_POWERDOWN |
SOR_PLL_2_BANDGAP_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_0 ) ;
value | = SOR_PLL_0_VCOPD | SOR_PLL_0_POWER_OFF ;
tegra_sor_writel ( sor , value , SOR_PLL_0 ) ;
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value & = ~ SOR_DP_PADCTL_PAD_CAL_PD ;
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
/* step 2 */
err = tegra_io_rail_power_on ( TEGRA_IO_RAIL_LVDS ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to power on I/O rail: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
usleep_range ( 5 , 100 ) ;
/* step 3 */
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value & = ~ SOR_PLL_2_BANDGAP_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
usleep_range ( 20 , 100 ) ;
/* step 4 */
value = tegra_sor_readl ( sor , SOR_PLL_0 ) ;
value & = ~ SOR_PLL_0_POWER_OFF ;
value & = ~ SOR_PLL_0_VCOPD ;
tegra_sor_writel ( sor , value , SOR_PLL_0 ) ;
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value & = ~ SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
usleep_range ( 200 , 1000 ) ;
/* step 5 */
value = tegra_sor_readl ( sor , SOR_PLL_2 ) ;
value & = ~ SOR_PLL_2_PORT_POWERDOWN ;
tegra_sor_writel ( sor , value , SOR_PLL_2 ) ;
/* switch to DP clock */
err = clk_set_parent ( sor - > clk , sor - > clk_dp ) ;
if ( err < 0 )
dev_err ( sor - > dev , " failed to set DP parent clock: %d \n " , err ) ;
2014-06-05 16:19:48 +02:00
/* power DP lanes */
2013-11-15 16:06:05 +01:00
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
2014-06-05 16:19:48 +02:00
if ( link . num_lanes < = 2 )
value & = ~ ( SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2 ) ;
else
value | = SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_2 ;
if ( link . num_lanes < = 1 )
value & = ~ SOR_DP_PADCTL_PD_TXD_1 ;
else
value | = SOR_DP_PADCTL_PD_TXD_1 ;
if ( link . num_lanes = = 0 )
value & = ~ SOR_DP_PADCTL_PD_TXD_0 ;
else
value | = SOR_DP_PADCTL_PD_TXD_0 ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
value = tegra_sor_readl ( sor , SOR_DP_LINKCTL_0 ) ;
value & = ~ SOR_DP_LINKCTL_LANE_COUNT_MASK ;
2014-06-05 16:29:46 +02:00
value | = SOR_DP_LINKCTL_LANE_COUNT ( link . num_lanes ) ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_DP_LINKCTL_0 ) ;
/* start lane sequencer */
value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_DOWN |
SOR_LANE_SEQ_CTL_POWER_STATE_UP ;
tegra_sor_writel ( sor , value , SOR_LANE_SEQ_CTL ) ;
while ( true ) {
value = tegra_sor_readl ( sor , SOR_LANE_SEQ_CTL ) ;
if ( ( value & SOR_LANE_SEQ_CTL_TRIGGER ) = = 0 )
break ;
usleep_range ( 250 , 1000 ) ;
}
2014-06-05 16:16:23 +02:00
/* set link bandwidth */
2013-11-15 16:06:05 +01:00
value = tegra_sor_readl ( sor , SOR_CLK_CNTRL ) ;
value & = ~ SOR_CLK_CNTRL_DP_LINK_SPEED_MASK ;
2014-06-05 16:16:23 +02:00
value | = drm_dp_link_rate_to_bw_code ( link . rate ) < < 2 ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_CLK_CNTRL ) ;
/* set linkctl */
value = tegra_sor_readl ( sor , SOR_DP_LINKCTL_0 ) ;
value | = SOR_DP_LINKCTL_ENABLE ;
value & = ~ SOR_DP_LINKCTL_TU_SIZE_MASK ;
2014-06-05 16:31:10 +02:00
value | = SOR_DP_LINKCTL_TU_SIZE ( config . tu_size ) ;
2013-11-15 16:06:05 +01:00
value | = SOR_DP_LINKCTL_ENHANCED_FRAME ;
tegra_sor_writel ( sor , value , SOR_DP_LINKCTL_0 ) ;
for ( i = 0 , value = 0 ; i < 4 ; i + + ) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_GALIOS |
SOR_DP_TPG_PATTERN_NONE ;
value = ( value < < 8 ) | lane ;
}
tegra_sor_writel ( sor , value , SOR_DP_TPG ) ;
value = tegra_sor_readl ( sor , SOR_DP_CONFIG_0 ) ;
value & = ~ SOR_DP_CONFIG_WATERMARK_MASK ;
2014-06-05 16:31:10 +02:00
value | = SOR_DP_CONFIG_WATERMARK ( config . watermark ) ;
2013-11-15 16:06:05 +01:00
value & = ~ SOR_DP_CONFIG_ACTIVE_SYM_COUNT_MASK ;
2014-06-05 16:31:10 +02:00
value | = SOR_DP_CONFIG_ACTIVE_SYM_COUNT ( config . active_count ) ;
2013-11-15 16:06:05 +01:00
value & = ~ SOR_DP_CONFIG_ACTIVE_SYM_FRAC_MASK ;
2014-06-05 16:31:10 +02:00
value | = SOR_DP_CONFIG_ACTIVE_SYM_FRAC ( config . active_frac ) ;
2013-11-15 16:06:05 +01:00
2014-06-05 16:31:10 +02:00
if ( config . active_polarity )
value | = SOR_DP_CONFIG_ACTIVE_SYM_POLARITY ;
else
value & = ~ SOR_DP_CONFIG_ACTIVE_SYM_POLARITY ;
2013-11-15 16:06:05 +01:00
value | = SOR_DP_CONFIG_ACTIVE_SYM_ENABLE ;
2014-06-05 16:20:27 +02:00
value | = SOR_DP_CONFIG_DISPARITY_NEGATIVE ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_DP_CONFIG_0 ) ;
value = tegra_sor_readl ( sor , SOR_DP_AUDIO_HBLANK_SYMBOLS ) ;
value & = ~ SOR_DP_AUDIO_HBLANK_SYMBOLS_MASK ;
2014-06-05 16:12:46 +02:00
value | = config . hblank_symbols & 0xffff ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_DP_AUDIO_HBLANK_SYMBOLS ) ;
value = tegra_sor_readl ( sor , SOR_DP_AUDIO_VBLANK_SYMBOLS ) ;
value & = ~ SOR_DP_AUDIO_VBLANK_SYMBOLS_MASK ;
2014-06-05 16:12:46 +02:00
value | = config . vblank_symbols & 0xffff ;
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_DP_AUDIO_VBLANK_SYMBOLS ) ;
/* enable pad calibration logic */
value = tegra_sor_readl ( sor , SOR_DP_PADCTL_0 ) ;
value | = SOR_DP_PADCTL_PAD_CAL_PD ;
tegra_sor_writel ( sor , value , SOR_DP_PADCTL_0 ) ;
if ( sor - > dpaux ) {
u8 rate , lanes ;
err = drm_dp_link_probe ( aux , & link ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to probe eDP link: %d \n " ,
err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
err = drm_dp_link_power_up ( aux , & link ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to power up eDP link: %d \n " ,
err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
err = drm_dp_link_configure ( aux , & link ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to configure eDP link: %d \n " ,
err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
rate = drm_dp_link_rate_to_bw_code ( link . rate ) ;
lanes = link . num_lanes ;
value = tegra_sor_readl ( sor , SOR_CLK_CNTRL ) ;
value & = ~ SOR_CLK_CNTRL_DP_LINK_SPEED_MASK ;
value | = SOR_CLK_CNTRL_DP_LINK_SPEED ( rate ) ;
tegra_sor_writel ( sor , value , SOR_CLK_CNTRL ) ;
value = tegra_sor_readl ( sor , SOR_DP_LINKCTL_0 ) ;
value & = ~ SOR_DP_LINKCTL_LANE_COUNT_MASK ;
value | = SOR_DP_LINKCTL_LANE_COUNT ( lanes ) ;
if ( link . capabilities & DP_LINK_CAP_ENHANCED_FRAMING )
value | = SOR_DP_LINKCTL_ENHANCED_FRAME ;
tegra_sor_writel ( sor , value , SOR_DP_LINKCTL_0 ) ;
/* disable training pattern generator */
for ( i = 0 ; i < link . num_lanes ; i + + ) {
unsigned long lane = SOR_DP_TPG_CHANNEL_CODING |
SOR_DP_TPG_SCRAMBLER_GALIOS |
SOR_DP_TPG_PATTERN_NONE ;
value = ( value < < 8 ) | lane ;
}
tegra_sor_writel ( sor , value , SOR_DP_TPG ) ;
err = tegra_sor_dp_train_fast ( sor , & link ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " DP fast link training failed: %d \n " ,
err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
dev_dbg ( sor - > dev , " fast link training succeeded \n " ) ;
}
err = tegra_sor_power_up ( sor , 250 ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to power up SOR: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
/*
* configure panel ( 24 bpp , vsync - , hsync - , DP - A protocol , complete
* raster , associate with display controller )
*/
2014-07-07 15:35:06 +02:00
value = SOR_STATE_ASY_PROTOCOL_DP_A |
2013-11-15 16:06:05 +01:00
SOR_STATE_ASY_CRC_MODE_COMPLETE |
SOR_STATE_ASY_OWNER ( dc - > pipe + 1 ) ;
2014-06-05 16:31:10 +02:00
2014-07-07 15:35:06 +02:00
if ( mode - > flags & DRM_MODE_FLAG_PHSYNC )
value & = ~ SOR_STATE_ASY_HSYNCPOL ;
if ( mode - > flags & DRM_MODE_FLAG_NHSYNC )
value | = SOR_STATE_ASY_HSYNCPOL ;
if ( mode - > flags & DRM_MODE_FLAG_PVSYNC )
value & = ~ SOR_STATE_ASY_VSYNCPOL ;
if ( mode - > flags & DRM_MODE_FLAG_NVSYNC )
value | = SOR_STATE_ASY_VSYNCPOL ;
2014-06-05 16:31:10 +02:00
switch ( config . bits_per_pixel ) {
case 24 :
value | = SOR_STATE_ASY_PIXELDEPTH_BPP_24_444 ;
break ;
case 18 :
value | = SOR_STATE_ASY_PIXELDEPTH_BPP_18_444 ;
break ;
default :
BUG ( ) ;
break ;
}
2013-11-15 16:06:05 +01:00
tegra_sor_writel ( sor , value , SOR_STATE_1 ) ;
/*
* TODO : The video timing programming below doesn ' t seem to match the
* register definitions .
*/
value = ( ( mode - > vtotal & 0x7fff ) < < 16 ) | ( mode - > htotal & 0x7fff ) ;
tegra_sor_writel ( sor , value , SOR_HEAD_STATE_1 ( 0 ) ) ;
vse = mode - > vsync_end - mode - > vsync_start - 1 ;
hse = mode - > hsync_end - mode - > hsync_start - 1 ;
value = ( ( vse & 0x7fff ) < < 16 ) | ( hse & 0x7fff ) ;
tegra_sor_writel ( sor , value , SOR_HEAD_STATE_2 ( 0 ) ) ;
vbe = vse + ( mode - > vsync_start - mode - > vdisplay ) ;
hbe = hse + ( mode - > hsync_start - mode - > hdisplay ) ;
value = ( ( vbe & 0x7fff ) < < 16 ) | ( hbe & 0x7fff ) ;
tegra_sor_writel ( sor , value , SOR_HEAD_STATE_3 ( 0 ) ) ;
vbs = vbe + mode - > vdisplay ;
hbs = hbe + mode - > hdisplay ;
value = ( ( vbs & 0x7fff ) < < 16 ) | ( hbs & 0x7fff ) ;
tegra_sor_writel ( sor , value , SOR_HEAD_STATE_4 ( 0 ) ) ;
2014-11-28 15:41:34 +01:00
/* CSTM (LVDS, link A/B, upper) */
value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B |
SOR_CSTM_UPPER ;
tegra_sor_writel ( sor , value , SOR_CSTM ) ;
/* PWM setup */
err = tegra_sor_setup_pwm ( sor , 250 ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to setup PWM: %d \n " , err ) ;
goto unlock ;
2013-11-15 16:06:05 +01:00
}
2014-12-08 16:32:47 +01:00
tegra_sor_update ( sor ) ;
2014-11-28 15:41:34 +01:00
value = tegra_dc_readl ( dc , DC_DISP_DISP_WIN_OPTIONS ) ;
value | = SOR_ENABLE ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_WIN_OPTIONS ) ;
2013-11-15 16:06:05 +01:00
2014-12-08 16:32:47 +01:00
tegra_dc_commit ( dc ) ;
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
err = tegra_sor_attach ( sor ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to attach SOR: %d \n " , err ) ;
goto unlock ;
}
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
err = tegra_sor_wakeup ( sor ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to enable DC: %d \n " , err ) ;
goto unlock ;
}
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
if ( output - > panel )
drm_panel_enable ( output - > panel ) ;
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
sor - > enabled = true ;
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
unlock :
mutex_unlock ( & sor - > lock ) ;
2013-11-15 16:06:05 +01:00
}
2014-11-28 15:41:34 +01:00
static void tegra_sor_encoder_disable ( struct drm_encoder * encoder )
2013-11-15 16:06:05 +01:00
{
2014-11-28 15:41:34 +01:00
struct tegra_output * output = encoder_to_output ( encoder ) ;
struct tegra_dc * dc = to_tegra_dc ( encoder - > crtc ) ;
2013-11-15 16:06:05 +01:00
struct tegra_sor * sor = to_sor ( output ) ;
2014-11-28 15:41:34 +01:00
u32 value ;
int err ;
2014-03-26 11:13:16 +01:00
mutex_lock ( & sor - > lock ) ;
2013-11-15 16:06:05 +01:00
if ( ! sor - > enabled )
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
2014-11-28 15:41:34 +01:00
if ( output - > panel )
drm_panel_disable ( output - > panel ) ;
2013-11-15 16:06:05 +01:00
err = tegra_sor_detach ( sor ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to detach SOR: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
tegra_sor_writel ( sor , 0 , SOR_STATE_1 ) ;
tegra_sor_update ( sor ) ;
/*
* The following accesses registers of the display controller , so make
* sure it ' s only executed when the output is attached to one .
*/
if ( dc ) {
value = tegra_dc_readl ( dc , DC_DISP_DISP_WIN_OPTIONS ) ;
value & = ~ SOR_ENABLE ;
tegra_dc_writel ( dc , value , DC_DISP_DISP_WIN_OPTIONS ) ;
2014-11-21 17:33:33 +01:00
tegra_dc_commit ( dc ) ;
2013-11-15 16:06:05 +01:00
}
err = tegra_sor_power_down ( sor ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to power down SOR: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
if ( sor - > dpaux ) {
err = tegra_dpaux_disable ( sor - > dpaux ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to disable DP: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
}
err = tegra_io_rail_power_off ( TEGRA_IO_RAIL_LVDS ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to power off I/O rail: %d \n " , err ) ;
2014-03-26 11:13:16 +01:00
goto unlock ;
2013-11-15 16:06:05 +01:00
}
2014-11-28 15:41:34 +01:00
if ( output - > panel )
drm_panel_unprepare ( output - > panel ) ;
2013-11-15 16:06:05 +01:00
clk_disable_unprepare ( sor - > clk ) ;
2014-11-28 15:41:34 +01:00
reset_control_assert ( sor - > rst ) ;
2013-11-15 16:06:05 +01:00
sor - > enabled = false ;
2014-03-26 11:13:16 +01:00
unlock :
mutex_unlock ( & sor - > lock ) ;
2014-01-31 10:02:15 +01:00
}
2014-12-08 17:26:46 +01:00
static int
tegra_sor_encoder_atomic_check ( struct drm_encoder * encoder ,
struct drm_crtc_state * crtc_state ,
struct drm_connector_state * conn_state )
{
struct tegra_output * output = encoder_to_output ( encoder ) ;
struct tegra_dc * dc = to_tegra_dc ( conn_state - > crtc ) ;
unsigned long pclk = crtc_state - > mode . clock * 1000 ;
struct tegra_sor * sor = to_sor ( output ) ;
int err ;
err = tegra_dc_state_setup_clock ( dc , crtc_state , sor - > clk_parent ,
pclk , 0 ) ;
if ( err < 0 ) {
dev_err ( output - > dev , " failed to setup CRTC state: %d \n " , err ) ;
return err ;
}
return 0 ;
}
2014-11-28 15:41:34 +01:00
static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = {
. dpms = tegra_sor_encoder_dpms ,
. prepare = tegra_sor_encoder_prepare ,
. commit = tegra_sor_encoder_commit ,
. mode_set = tegra_sor_encoder_mode_set ,
. disable = tegra_sor_encoder_disable ,
2014-12-08 17:26:46 +01:00
. atomic_check = tegra_sor_encoder_atomic_check ,
2014-01-31 10:02:15 +01:00
} ;
2013-11-15 16:06:05 +01:00
static int tegra_sor_init ( struct host1x_client * client )
{
2014-05-22 09:57:15 +02:00
struct drm_device * drm = dev_get_drvdata ( client - > parent ) ;
2013-11-15 16:06:05 +01:00
struct tegra_sor * sor = host1x_client_to_sor ( client ) ;
int err ;
if ( ! sor - > dpaux )
return - ENODEV ;
sor - > output . dev = sor - > dev ;
2014-11-28 15:41:34 +01:00
drm_connector_init ( drm , & sor - > output . connector ,
& tegra_sor_connector_funcs ,
DRM_MODE_CONNECTOR_eDP ) ;
drm_connector_helper_add ( & sor - > output . connector ,
& tegra_sor_connector_helper_funcs ) ;
sor - > output . connector . dpms = DRM_MODE_DPMS_OFF ;
drm_encoder_init ( drm , & sor - > output . encoder , & tegra_sor_encoder_funcs ,
DRM_MODE_ENCODER_TMDS ) ;
drm_encoder_helper_add ( & sor - > output . encoder ,
& tegra_sor_encoder_helper_funcs ) ;
drm_mode_connector_attach_encoder ( & sor - > output . connector ,
& sor - > output . encoder ) ;
drm_connector_register ( & sor - > output . connector ) ;
2014-12-19 15:51:35 +01:00
err = tegra_output_init ( drm , & sor - > output ) ;
if ( err < 0 ) {
dev_err ( client - > dev , " failed to initialize output: %d \n " , err ) ;
return err ;
}
2014-11-28 15:41:34 +01:00
2014-12-19 15:51:35 +01:00
sor - > output . encoder . possible_crtcs = 0x3 ;
2013-11-15 16:06:05 +01:00
2014-01-31 10:02:15 +01:00
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) ) {
2014-05-28 13:46:12 +02:00
err = tegra_sor_debugfs_init ( sor , drm - > primary ) ;
2014-01-31 10:02:15 +01:00
if ( err < 0 )
dev_err ( sor - > dev , " debugfs setup failed: %d \n " , err ) ;
}
2013-11-15 16:06:05 +01:00
if ( sor - > dpaux ) {
err = tegra_dpaux_attach ( sor - > dpaux , & sor - > output ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to attach DP: %d \n " , err ) ;
return err ;
}
}
2014-11-28 15:41:34 +01:00
err = clk_prepare_enable ( sor - > clk ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to enable clock: %d \n " , err ) ;
return err ;
}
err = clk_prepare_enable ( sor - > clk_safe ) ;
if ( err < 0 )
return err ;
err = clk_prepare_enable ( sor - > clk_dp ) ;
if ( err < 0 )
return err ;
2013-11-15 16:06:05 +01:00
return 0 ;
}
static int tegra_sor_exit ( struct host1x_client * client )
{
struct tegra_sor * sor = host1x_client_to_sor ( client ) ;
int err ;
2014-12-19 15:55:08 +01:00
tegra_output_exit ( & sor - > output ) ;
2013-11-15 16:06:05 +01:00
if ( sor - > dpaux ) {
err = tegra_dpaux_detach ( sor - > dpaux ) ;
if ( err < 0 ) {
dev_err ( sor - > dev , " failed to detach DP: %d \n " , err ) ;
return err ;
}
}
2014-11-28 15:41:34 +01:00
clk_disable_unprepare ( sor - > clk_safe ) ;
clk_disable_unprepare ( sor - > clk_dp ) ;
clk_disable_unprepare ( sor - > clk ) ;
2014-12-19 15:47:30 +01:00
if ( IS_ENABLED ( CONFIG_DEBUG_FS ) )
tegra_sor_debugfs_exit ( sor ) ;
2014-01-31 10:02:15 +01:00
2013-11-15 16:06:05 +01:00
return 0 ;
}
static const struct host1x_client_ops sor_client_ops = {
. init = tegra_sor_init ,
. exit = tegra_sor_exit ,
} ;
static int tegra_sor_probe ( struct platform_device * pdev )
{
struct device_node * np ;
struct tegra_sor * sor ;
struct resource * regs ;
int err ;
sor = devm_kzalloc ( & pdev - > dev , sizeof ( * sor ) , GFP_KERNEL ) ;
if ( ! sor )
return - ENOMEM ;
sor - > output . dev = sor - > dev = & pdev - > dev ;
np = of_parse_phandle ( pdev - > dev . of_node , " nvidia,dpaux " , 0 ) ;
if ( np ) {
sor - > dpaux = tegra_dpaux_find_by_of_node ( np ) ;
of_node_put ( np ) ;
if ( ! sor - > dpaux )
return - EPROBE_DEFER ;
}
err = tegra_output_probe ( & sor - > output ) ;
if ( err < 0 )
return err ;
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
sor - > regs = devm_ioremap_resource ( & pdev - > dev , regs ) ;
if ( IS_ERR ( sor - > regs ) )
return PTR_ERR ( sor - > regs ) ;
sor - > rst = devm_reset_control_get ( & pdev - > dev , " sor " ) ;
if ( IS_ERR ( sor - > rst ) )
return PTR_ERR ( sor - > rst ) ;
sor - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( sor - > clk ) )
return PTR_ERR ( sor - > clk ) ;
sor - > clk_parent = devm_clk_get ( & pdev - > dev , " parent " ) ;
if ( IS_ERR ( sor - > clk_parent ) )
return PTR_ERR ( sor - > clk_parent ) ;
sor - > clk_safe = devm_clk_get ( & pdev - > dev , " safe " ) ;
if ( IS_ERR ( sor - > clk_safe ) )
return PTR_ERR ( sor - > clk_safe ) ;
sor - > clk_dp = devm_clk_get ( & pdev - > dev , " dp " ) ;
if ( IS_ERR ( sor - > clk_dp ) )
return PTR_ERR ( sor - > clk_dp ) ;
INIT_LIST_HEAD ( & sor - > client . list ) ;
sor - > client . ops = & sor_client_ops ;
sor - > client . dev = & pdev - > dev ;
2014-03-26 11:13:16 +01:00
mutex_init ( & sor - > lock ) ;
2013-11-15 16:06:05 +01:00
err = host1x_client_register ( & sor - > client ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to register host1x client: %d \n " ,
err ) ;
return err ;
}
platform_set_drvdata ( pdev , sor ) ;
return 0 ;
}
static int tegra_sor_remove ( struct platform_device * pdev )
{
struct tegra_sor * sor = platform_get_drvdata ( pdev ) ;
int err ;
err = host1x_client_unregister ( & sor - > client ) ;
if ( err < 0 ) {
dev_err ( & pdev - > dev , " failed to unregister host1x client: %d \n " ,
err ) ;
return err ;
}
2014-12-19 15:55:08 +01:00
tegra_output_remove ( & sor - > output ) ;
2013-11-15 16:06:05 +01:00
return 0 ;
}
static const struct of_device_id tegra_sor_of_match [ ] = {
{ . compatible = " nvidia,tegra124-sor " , } ,
{ } ,
} ;
2014-06-18 16:21:55 -06:00
MODULE_DEVICE_TABLE ( of , tegra_sor_of_match ) ;
2013-11-15 16:06:05 +01:00
struct platform_driver tegra_sor_driver = {
. driver = {
. name = " tegra-sor " ,
. of_match_table = tegra_sor_of_match ,
} ,
. probe = tegra_sor_probe ,
. remove = tegra_sor_remove ,
} ;