2017-12-06 12:29:47 +01:00
// SPDX-License-Identifier: GPL-2.0
2014-07-30 19:24:55 +02:00
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Author : Vincent Abriou < vincent . abriou @ st . com > for STMicroelectronics .
*/
2019-06-05 15:48:35 +02:00
# include <drm/drm_print.h>
2014-07-30 19:24:55 +02:00
# include "sti_hdmi_tx3g4c28phy.h"
# define HDMI_SRZ_CFG 0x504
# define HDMI_SRZ_PLL_CFG 0x510
# define HDMI_SRZ_ICNTL 0x518
# define HDMI_SRZ_CALCODE_EXT 0x520
# define HDMI_SRZ_CFG_EN BIT(0)
# define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
# define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16)
# define HDMI_SRZ_CFG_RBIAS_EXT BIT(17)
# define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18)
# define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19)
# define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24)
# define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \
HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
HDMI_SRZ_CFG_EXTERNAL_DATA | \
HDMI_SRZ_CFG_RBIAS_EXT | \
HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \
HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \
HDMI_SRZ_CFG_EN_SRC_TERMINATION )
# define PLL_CFG_EN BIT(0)
# define PLL_CFG_NDIV_SHIFT (8)
# define PLL_CFG_IDF_SHIFT (16)
# define PLL_CFG_ODF_SHIFT (24)
# define ODF_DIV_1 (0)
# define ODF_DIV_2 (1)
# define ODF_DIV_4 (2)
# define ODF_DIV_8 (3)
# define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
struct plldividers_s {
uint32_t min ;
uint32_t max ;
uint32_t idf ;
uint32_t odf ;
} ;
/*
* Functional specification recommended values
*/
# define NB_PLL_MODE 5
static struct plldividers_s plldividers [ NB_PLL_MODE ] = {
{ 0 , 20000000 , 1 , ODF_DIV_8 } ,
{ 20000000 , 42500000 , 2 , ODF_DIV_8 } ,
{ 42500000 , 85000000 , 4 , ODF_DIV_4 } ,
{ 85000000 , 170000000 , 8 , ODF_DIV_2 } ,
{ 170000000 , 340000000 , 16 , ODF_DIV_1 }
} ;
# define NB_HDMI_PHY_CONFIG 2
static struct hdmi_phy_config hdmiphy_config [ NB_HDMI_PHY_CONFIG ] = {
{ 0 , 250000000 , { 0x0 , 0x0 , 0x0 , 0x0 } } ,
{ 250000000 , 300000000 , { 0x1110 , 0x0 , 0x0 , 0x0 } } ,
} ;
/**
2021-06-02 15:32:39 +01:00
* sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28
2014-07-30 19:24:55 +02:00
*
* @ hdmi : pointer on the hdmi internal structure
*
* Return false if an error occur
*/
static bool sti_hdmi_tx3g4c28phy_start ( struct sti_hdmi * hdmi )
{
u32 ckpxpll = hdmi - > mode . clock * 1000 ;
u32 val , tmdsck , idf , odf , pllctrl = 0 ;
bool foundplldivides = false ;
int i ;
DRM_DEBUG_DRIVER ( " ckpxpll = %dHz \n " , ckpxpll ) ;
for ( i = 0 ; i < NB_PLL_MODE ; i + + ) {
if ( ckpxpll > = plldividers [ i ] . min & &
ckpxpll < plldividers [ i ] . max ) {
idf = plldividers [ i ] . idf ;
odf = plldividers [ i ] . odf ;
foundplldivides = true ;
break ;
}
}
if ( ! foundplldivides ) {
DRM_ERROR ( " input TMDS clock speed (%d) not supported \n " ,
ckpxpll ) ;
goto err ;
}
/* Assuming no pixel repetition and 24bits color */
tmdsck = ckpxpll ;
pllctrl | = 40 < < PLL_CFG_NDIV_SHIFT ;
if ( tmdsck > 340000000 ) {
DRM_ERROR ( " output TMDS clock (%d) out of range \n " , tmdsck ) ;
goto err ;
}
pllctrl | = idf < < PLL_CFG_IDF_SHIFT ;
pllctrl | = odf < < PLL_CFG_ODF_SHIFT ;
/*
* Configure and power up the PHY PLL
*/
hdmi - > event_received = false ;
DRM_DEBUG_DRIVER ( " pllctrl = 0x%x \n " , pllctrl ) ;
hdmi_write ( hdmi , ( pllctrl | PLL_CFG_EN ) , HDMI_SRZ_PLL_CFG ) ;
/* wait PLL interrupt */
wait_event_interruptible_timeout ( hdmi - > wait_event ,
hdmi - > event_received = = true ,
msecs_to_jiffies
( HDMI_TIMEOUT_PLL_LOCK ) ) ;
if ( ( hdmi_read ( hdmi , HDMI_STA ) & HDMI_STA_DLL_LCK ) = = 0 ) {
DRM_ERROR ( " hdmi phy pll not locked \n " ) ;
goto err ;
}
DRM_DEBUG_DRIVER ( " got PHY PLL Lock \n " ) ;
val = ( HDMI_SRZ_CFG_EN |
HDMI_SRZ_CFG_EXTERNAL_DATA |
HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION ) ;
if ( tmdsck > 165000000 )
val | = HDMI_SRZ_CFG_EN_SRC_TERMINATION ;
/*
* To configure the source termination and pre - emphasis appropriately
* for different high speed TMDS clock frequencies a phy configuration
* table must be provided , tailored to the SoC and board combination .
*/
for ( i = 0 ; i < NB_HDMI_PHY_CONFIG ; i + + ) {
if ( ( hdmiphy_config [ i ] . min_tmds_freq < = tmdsck ) & &
( hdmiphy_config [ i ] . max_tmds_freq > = tmdsck ) ) {
val | = ( hdmiphy_config [ i ] . config [ 0 ]
& ~ HDMI_SRZ_CFG_INTERNAL_MASK ) ;
hdmi_write ( hdmi , val , HDMI_SRZ_CFG ) ;
val = hdmiphy_config [ i ] . config [ 1 ] ;
hdmi_write ( hdmi , val , HDMI_SRZ_ICNTL ) ;
val = hdmiphy_config [ i ] . config [ 2 ] ;
hdmi_write ( hdmi , val , HDMI_SRZ_CALCODE_EXT ) ;
DRM_DEBUG_DRIVER ( " serializer cfg 0x%x 0x%x 0x%x \n " ,
hdmiphy_config [ i ] . config [ 0 ] ,
hdmiphy_config [ i ] . config [ 1 ] ,
hdmiphy_config [ i ] . config [ 2 ] ) ;
return true ;
}
}
/*
* Default , power up the serializer with no pre - emphasis or
* output swing correction
*/
hdmi_write ( hdmi , val , HDMI_SRZ_CFG ) ;
hdmi_write ( hdmi , 0x0 , HDMI_SRZ_ICNTL ) ;
hdmi_write ( hdmi , 0x0 , HDMI_SRZ_CALCODE_EXT ) ;
return true ;
err :
return false ;
}
/**
2021-06-02 15:32:39 +01:00
* sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28
2014-07-30 19:24:55 +02:00
*
* @ hdmi : pointer on the hdmi internal structure
*/
static void sti_hdmi_tx3g4c28phy_stop ( struct sti_hdmi * hdmi )
{
int val = 0 ;
DRM_DEBUG_DRIVER ( " \n " ) ;
hdmi - > event_received = false ;
val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION ;
val | = HDMI_SRZ_CFG_EN_BIASRES_DETECTION ;
hdmi_write ( hdmi , val , HDMI_SRZ_CFG ) ;
hdmi_write ( hdmi , 0 , HDMI_SRZ_PLL_CFG ) ;
/* wait PLL interrupt */
wait_event_interruptible_timeout ( hdmi - > wait_event ,
hdmi - > event_received = = true ,
msecs_to_jiffies
( HDMI_TIMEOUT_PLL_LOCK ) ) ;
if ( hdmi_read ( hdmi , HDMI_STA ) & HDMI_STA_DLL_LCK )
DRM_ERROR ( " hdmi phy pll not well disabled \n " ) ;
}
struct hdmi_phy_ops tx3g4c28phy_ops = {
. start = sti_hdmi_tx3g4c28phy_start ,
. stop = sti_hdmi_tx3g4c28phy_stop ,
} ;