2013-10-08 13:07:00 +05:30
/*
* HDMI PHY
*
* Copyright ( C ) 2013 Texas Instruments Incorporated
*
* 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/kernel.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/platform_device.h>
2013-09-23 12:58:52 +05:30
# include <linux/slab.h>
2016-05-11 18:01:45 +02:00
# include <linux/seq_file.h>
2013-10-08 13:07:00 +05:30
# include <video/omapdss.h>
# include "dss.h"
2013-09-12 17:45:57 +05:30
# include "hdmi.h"
2013-10-08 13:07:00 +05:30
2013-09-23 12:58:52 +05:30
struct hdmi_phy_features {
bool bist_ctrl ;
bool ldo_voltage ;
unsigned long max_phy ;
} ;
static const struct hdmi_phy_features * phy_feat ;
2013-10-08 13:07:00 +05:30
void hdmi_phy_dump ( struct hdmi_phy_data * phy , struct seq_file * s )
{
# define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
hdmi_read_reg ( phy - > base , r ) )
DUMPPHY ( HDMI_TXPHY_TX_CTRL ) ;
DUMPPHY ( HDMI_TXPHY_DIGITAL_CTRL ) ;
DUMPPHY ( HDMI_TXPHY_POWER_CTRL ) ;
DUMPPHY ( HDMI_TXPHY_PAD_CFG_CTRL ) ;
2013-09-23 12:58:52 +05:30
if ( phy_feat - > bist_ctrl )
DUMPPHY ( HDMI_TXPHY_BIST_CONTROL ) ;
2013-10-08 13:07:00 +05:30
}
2014-04-17 12:54:02 +03:00
int hdmi_phy_parse_lanes ( struct hdmi_phy_data * phy , const u32 * lanes )
{
int i ;
for ( i = 0 ; i < 8 ; i + = 2 ) {
u8 lane , pol ;
int dx , dy ;
dx = lanes [ i ] ;
dy = lanes [ i + 1 ] ;
if ( dx < 0 | | dx > = 8 )
return - EINVAL ;
if ( dy < 0 | | dy > = 8 )
return - EINVAL ;
if ( dx & 1 ) {
if ( dy ! = dx - 1 )
return - EINVAL ;
pol = 1 ;
} else {
if ( dy ! = dx + 1 )
return - EINVAL ;
pol = 0 ;
}
lane = dx / 2 ;
phy - > lane_function [ lane ] = i / 2 ;
phy - > lane_polarity [ lane ] = pol ;
}
return 0 ;
}
static void hdmi_phy_configure_lanes ( struct hdmi_phy_data * phy )
{
static const u16 pad_cfg_list [ ] = {
0x0123 ,
0x0132 ,
0x0312 ,
0x0321 ,
0x0231 ,
0x0213 ,
0x1023 ,
0x1032 ,
0x3012 ,
0x3021 ,
0x2031 ,
0x2013 ,
0x1203 ,
0x1302 ,
0x3102 ,
0x3201 ,
0x2301 ,
0x2103 ,
0x1230 ,
0x1320 ,
0x3120 ,
0x3210 ,
0x2310 ,
0x2130 ,
} ;
u16 lane_cfg = 0 ;
int i ;
unsigned lane_cfg_val ;
u16 pol_val = 0 ;
for ( i = 0 ; i < 4 ; + + i )
lane_cfg | = phy - > lane_function [ i ] < < ( ( 3 - i ) * 4 ) ;
pol_val | = phy - > lane_polarity [ 0 ] < < 0 ;
pol_val | = phy - > lane_polarity [ 1 ] < < 3 ;
pol_val | = phy - > lane_polarity [ 2 ] < < 2 ;
pol_val | = phy - > lane_polarity [ 3 ] < < 1 ;
for ( i = 0 ; i < ARRAY_SIZE ( pad_cfg_list ) ; + + i )
if ( pad_cfg_list [ i ] = = lane_cfg )
break ;
if ( WARN_ON ( i = = ARRAY_SIZE ( pad_cfg_list ) ) )
i = 0 ;
lane_cfg_val = i ;
REG_FLD_MOD ( phy - > base , HDMI_TXPHY_PAD_CFG_CTRL , lane_cfg_val , 26 , 22 ) ;
REG_FLD_MOD ( phy - > base , HDMI_TXPHY_PAD_CFG_CTRL , pol_val , 30 , 27 ) ;
}
2014-09-15 15:40:47 +03:00
int hdmi_phy_configure ( struct hdmi_phy_data * phy , unsigned long hfbitclk ,
unsigned long lfbitclk )
2013-10-08 13:07:00 +05:30
{
2013-09-23 12:58:52 +05:30
u8 freqout ;
2013-10-08 13:07:00 +05:30
/*
* Read address 0 in order to get the SCP reset done completed
* Dummy access performed to make sure reset is done
*/
hdmi_read_reg ( phy - > base , HDMI_TXPHY_TX_CTRL ) ;
2013-09-23 12:58:52 +05:30
/*
* In OMAP5 + , the HFBITCLK must be divided by 2 before issuing the
* HDMI_PHYPWRCMD_LDOON command .
*/
if ( phy_feat - > bist_ctrl )
REG_FLD_MOD ( phy - > base , HDMI_TXPHY_BIST_CONTROL , 1 , 11 , 11 ) ;
2014-09-15 15:40:47 +03:00
/*
* If the hfbitclk ! = lfbitclk , it means the lfbitclk was configured
* to be used for TMDS .
*/
if ( hfbitclk ! = lfbitclk )
freqout = 0 ;
else if ( hfbitclk / 10 < phy_feat - > max_phy )
2013-09-23 12:58:52 +05:30
freqout = 1 ;
2014-09-15 15:40:47 +03:00
else
freqout = 2 ;
2013-09-23 12:58:52 +05:30
2013-10-08 13:07:00 +05:30
/*
* Write to phy address 0 to configure the clock
* use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
*/
2013-09-23 12:58:52 +05:30
REG_FLD_MOD ( phy - > base , HDMI_TXPHY_TX_CTRL , freqout , 31 , 30 ) ;
2013-10-08 13:07:00 +05:30
/* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
hdmi_write_reg ( phy - > base , HDMI_TXPHY_DIGITAL_CTRL , 0xF0000000 ) ;
/* Setup max LDO voltage */
2013-09-23 12:58:52 +05:30
if ( phy_feat - > ldo_voltage )
REG_FLD_MOD ( phy - > base , HDMI_TXPHY_POWER_CTRL , 0xB , 3 , 0 ) ;
2013-10-08 13:07:00 +05:30
2014-04-17 12:54:02 +03:00
hdmi_phy_configure_lanes ( phy ) ;
2013-10-08 13:07:00 +05:30
return 0 ;
}
2013-09-23 12:58:52 +05:30
static const struct hdmi_phy_features omap44xx_phy_feats = {
. bist_ctrl = false ,
. ldo_voltage = true ,
. max_phy = 185675000 ,
} ;
static const struct hdmi_phy_features omap54xx_phy_feats = {
. bist_ctrl = true ,
. ldo_voltage = false ,
. max_phy = 186000000 ,
} ;
static int hdmi_phy_init_features ( struct platform_device * pdev )
{
struct hdmi_phy_features * dst ;
const struct hdmi_phy_features * src ;
dst = devm_kzalloc ( & pdev - > dev , sizeof ( * dst ) , GFP_KERNEL ) ;
if ( ! dst ) {
dev_err ( & pdev - > dev , " Failed to allocate HDMI PHY Features \n " ) ;
return - ENOMEM ;
}
switch ( omapdss_get_version ( ) ) {
case OMAPDSS_VER_OMAP4430_ES1 :
case OMAPDSS_VER_OMAP4430_ES2 :
case OMAPDSS_VER_OMAP4 :
src = & omap44xx_phy_feats ;
break ;
case OMAPDSS_VER_OMAP5 :
2014-12-31 11:26:18 +02:00
case OMAPDSS_VER_DRA7xx :
2013-09-23 12:58:52 +05:30
src = & omap54xx_phy_feats ;
break ;
default :
return - ENODEV ;
}
memcpy ( dst , src , sizeof ( * dst ) ) ;
phy_feat = dst ;
return 0 ;
}
2013-10-08 13:07:00 +05:30
int hdmi_phy_init ( struct platform_device * pdev , struct hdmi_phy_data * phy )
{
2013-09-23 12:58:52 +05:30
int r ;
2013-10-08 13:07:00 +05:30
struct resource * res ;
2013-09-23 12:58:52 +05:30
r = hdmi_phy_init_features ( pdev ) ;
if ( r )
return r ;
2013-12-17 14:41:14 +02:00
res = platform_get_resource_byname ( pdev , IORESOURCE_MEM , " phy " ) ;
2013-10-08 13:07:00 +05:30
if ( ! res ) {
2014-04-28 16:11:01 +03:00
DSSERR ( " can't get PHY mem resource \n " ) ;
return - EINVAL ;
2013-10-08 13:07:00 +05:30
}
2014-04-28 16:11:01 +03:00
phy - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
2014-05-23 14:50:09 +03:00
if ( IS_ERR ( phy - > base ) ) {
2013-10-08 13:07:00 +05:30
DSSERR ( " can't ioremap TX PHY \n " ) ;
2014-05-23 14:50:09 +03:00
return PTR_ERR ( phy - > base ) ;
2013-10-08 13:07:00 +05:30
}
return 0 ;
}