2016-01-04 20:36:35 +03:00
/*
* Copyright ( c ) 2015 MediaTek Inc .
*
* 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 .
*
* This program is distributed in the hope that 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 .
*/
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/phy/phy.h>
# define MIPITX_DSI_CON 0x00
# define RG_DSI_LDOCORE_EN BIT(0)
# define RG_DSI_CKG_LDOOUT_EN BIT(1)
# define RG_DSI_BCLK_SEL (3 << 2)
# define RG_DSI_LD_IDX_SEL (7 << 4)
# define RG_DSI_PHYCLK_SEL (2 << 8)
# define RG_DSI_DSICLK_FREQ_SEL BIT(10)
# define RG_DSI_LPTX_CLMP_EN BIT(11)
# define MIPITX_DSI_CLOCK_LANE 0x04
# define MIPITX_DSI_DATA_LANE0 0x08
# define MIPITX_DSI_DATA_LANE1 0x0c
# define MIPITX_DSI_DATA_LANE2 0x10
# define MIPITX_DSI_DATA_LANE3 0x14
# define RG_DSI_LNTx_LDOOUT_EN BIT(0)
# define RG_DSI_LNTx_CKLANE_EN BIT(1)
# define RG_DSI_LNTx_LPTX_IPLUS1 BIT(2)
# define RG_DSI_LNTx_LPTX_IPLUS2 BIT(3)
# define RG_DSI_LNTx_LPTX_IMINUS BIT(4)
# define RG_DSI_LNTx_LPCD_IPLUS BIT(5)
# define RG_DSI_LNTx_LPCD_IMINUS BIT(6)
# define RG_DSI_LNTx_RT_CODE (0xf << 8)
# define MIPITX_DSI_TOP_CON 0x40
# define RG_DSI_LNT_INTR_EN BIT(0)
# define RG_DSI_LNT_HS_BIAS_EN BIT(1)
# define RG_DSI_LNT_IMP_CAL_EN BIT(2)
# define RG_DSI_LNT_TESTMODE_EN BIT(3)
# define RG_DSI_LNT_IMP_CAL_CODE (0xf << 4)
# define RG_DSI_LNT_AIO_SEL (7 << 8)
# define RG_DSI_PAD_TIE_LOW_EN BIT(11)
# define RG_DSI_DEBUG_INPUT_EN BIT(12)
# define RG_DSI_PRESERVE (7 << 13)
# define MIPITX_DSI_BG_CON 0x44
# define RG_DSI_BG_CORE_EN BIT(0)
# define RG_DSI_BG_CKEN BIT(1)
# define RG_DSI_BG_DIV (0x3 << 2)
# define RG_DSI_BG_FAST_CHARGE BIT(4)
# define RG_DSI_VOUT_MSK (0x3ffff << 5)
# define RG_DSI_V12_SEL (7 << 5)
# define RG_DSI_V10_SEL (7 << 8)
# define RG_DSI_V072_SEL (7 << 11)
# define RG_DSI_V04_SEL (7 << 14)
# define RG_DSI_V032_SEL (7 << 17)
# define RG_DSI_V02_SEL (7 << 20)
# define RG_DSI_BG_R1_TRIM (0xf << 24)
# define RG_DSI_BG_R2_TRIM (0xf << 28)
# define MIPITX_DSI_PLL_CON0 0x50
# define RG_DSI_MPPLL_PLL_EN BIT(0)
# define RG_DSI_MPPLL_DIV_MSK (0x1ff << 1)
# define RG_DSI_MPPLL_PREDIV (3 << 1)
# define RG_DSI_MPPLL_TXDIV0 (3 << 3)
# define RG_DSI_MPPLL_TXDIV1 (3 << 5)
# define RG_DSI_MPPLL_POSDIV (7 << 7)
# define RG_DSI_MPPLL_MONVC_EN BIT(10)
# define RG_DSI_MPPLL_MONREF_EN BIT(11)
# define RG_DSI_MPPLL_VOD_EN BIT(12)
# define MIPITX_DSI_PLL_CON1 0x54
# define RG_DSI_MPPLL_SDM_FRA_EN BIT(0)
# define RG_DSI_MPPLL_SDM_SSC_PH_INIT BIT(1)
# define RG_DSI_MPPLL_SDM_SSC_EN BIT(2)
# define RG_DSI_MPPLL_SDM_SSC_PRD (0xffff << 16)
# define MIPITX_DSI_PLL_CON2 0x58
# define MIPITX_DSI_PLL_PWR 0x68
# define RG_DSI_MPPLL_SDM_PWR_ON BIT(0)
# define RG_DSI_MPPLL_SDM_ISO_EN BIT(1)
# define RG_DSI_MPPLL_SDM_PWR_ACK BIT(8)
# define MIPITX_DSI_SW_CTRL 0x80
# define SW_CTRL_EN BIT(0)
# define MIPITX_DSI_SW_CTRL_CON0 0x84
# define SW_LNTC_LPTX_PRE_OE BIT(0)
# define SW_LNTC_LPTX_OE BIT(1)
# define SW_LNTC_LPTX_P BIT(2)
# define SW_LNTC_LPTX_N BIT(3)
# define SW_LNTC_HSTX_PRE_OE BIT(4)
# define SW_LNTC_HSTX_OE BIT(5)
# define SW_LNTC_HSTX_ZEROCLK BIT(6)
# define SW_LNT0_LPTX_PRE_OE BIT(7)
# define SW_LNT0_LPTX_OE BIT(8)
# define SW_LNT0_LPTX_P BIT(9)
# define SW_LNT0_LPTX_N BIT(10)
# define SW_LNT0_HSTX_PRE_OE BIT(11)
# define SW_LNT0_HSTX_OE BIT(12)
# define SW_LNT0_LPRX_EN BIT(13)
# define SW_LNT1_LPTX_PRE_OE BIT(14)
# define SW_LNT1_LPTX_OE BIT(15)
# define SW_LNT1_LPTX_P BIT(16)
# define SW_LNT1_LPTX_N BIT(17)
# define SW_LNT1_HSTX_PRE_OE BIT(18)
# define SW_LNT1_HSTX_OE BIT(19)
# define SW_LNT2_LPTX_PRE_OE BIT(20)
# define SW_LNT2_LPTX_OE BIT(21)
# define SW_LNT2_LPTX_P BIT(22)
# define SW_LNT2_LPTX_N BIT(23)
# define SW_LNT2_HSTX_PRE_OE BIT(24)
# define SW_LNT2_HSTX_OE BIT(25)
struct mtk_mipi_tx {
struct device * dev ;
void __iomem * regs ;
unsigned int data_rate ;
struct clk_hw pll_hw ;
struct clk * pll ;
} ;
static inline struct mtk_mipi_tx * mtk_mipi_tx_from_clk_hw ( struct clk_hw * hw )
{
return container_of ( hw , struct mtk_mipi_tx , pll_hw ) ;
}
static void mtk_mipi_tx_clear_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 bits )
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( temp & ~ bits , mipi_tx - > regs + offset ) ;
}
static void mtk_mipi_tx_set_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 bits )
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( temp | bits , mipi_tx - > regs + offset ) ;
}
static void mtk_mipi_tx_update_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 mask , u32 data )
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( ( temp & ~ mask ) | ( data & mask ) , mipi_tx - > regs + offset ) ;
}
static int mtk_mipi_tx_pll_prepare ( struct clk_hw * hw )
{
struct mtk_mipi_tx * mipi_tx = mtk_mipi_tx_from_clk_hw ( hw ) ;
unsigned int txdiv , txdiv0 , txdiv1 ;
u64 pcw ;
dev_dbg ( mipi_tx - > dev , " prepare: %u Hz \n " , mipi_tx - > data_rate ) ;
if ( mipi_tx - > data_rate > = 500000000 ) {
txdiv = 1 ;
txdiv0 = 0 ;
txdiv1 = 0 ;
} else if ( mipi_tx - > data_rate > = 250000000 ) {
txdiv = 2 ;
txdiv0 = 1 ;
txdiv1 = 0 ;
} else if ( mipi_tx - > data_rate > = 125000000 ) {
txdiv = 4 ;
txdiv0 = 2 ;
txdiv1 = 0 ;
} else if ( mipi_tx - > data_rate > 62000000 ) {
txdiv = 8 ;
txdiv0 = 2 ;
txdiv1 = 1 ;
} else if ( mipi_tx - > data_rate > = 50000000 ) {
txdiv = 16 ;
txdiv0 = 2 ;
txdiv1 = 2 ;
} else {
return - EINVAL ;
}
mtk_mipi_tx_update_bits ( mipi_tx , MIPITX_DSI_BG_CON ,
RG_DSI_VOUT_MSK |
RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN ,
( 4 < < 20 ) | ( 4 < < 17 ) | ( 4 < < 14 ) |
( 4 < < 11 ) | ( 4 < < 8 ) | ( 4 < < 5 ) |
RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN ) ;
usleep_range ( 30 , 100 ) ;
mtk_mipi_tx_update_bits ( mipi_tx , MIPITX_DSI_TOP_CON ,
RG_DSI_LNT_IMP_CAL_CODE | RG_DSI_LNT_HS_BIAS_EN ,
( 8 < < 4 ) | RG_DSI_LNT_HS_BIAS_EN ) ;
mtk_mipi_tx_set_bits ( mipi_tx , MIPITX_DSI_CON ,
RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN ) ;
mtk_mipi_tx_update_bits ( mipi_tx , MIPITX_DSI_PLL_PWR ,
RG_DSI_MPPLL_SDM_PWR_ON |
RG_DSI_MPPLL_SDM_ISO_EN ,
RG_DSI_MPPLL_SDM_PWR_ON ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_PLL_CON0 ,
RG_DSI_MPPLL_PLL_EN ) ;
mtk_mipi_tx_update_bits ( mipi_tx , MIPITX_DSI_PLL_CON0 ,
RG_DSI_MPPLL_TXDIV0 | RG_DSI_MPPLL_TXDIV1 |
RG_DSI_MPPLL_PREDIV ,
( txdiv0 < < 3 ) | ( txdiv1 < < 5 ) ) ;
/*
* PLL PCW config
* PCW bit 24 ~ 30 = integer part of pcw
* PCW bit 0 ~ 23 = fractional part of pcw
* pcw = data_Rate * 4 * txdiv / ( Ref_clk * 2 ) ;
* Post DIV = 4 , so need data_Rate * 4
* Ref_clk is 26 MHz
*/
pcw = div_u64 ( ( ( u64 ) mipi_tx - > data_rate * 2 * txdiv ) < < 24 ,
26000000 ) ;
writel ( pcw , mipi_tx - > regs + MIPITX_DSI_PLL_CON2 ) ;
mtk_mipi_tx_set_bits ( mipi_tx , MIPITX_DSI_PLL_CON1 ,
RG_DSI_MPPLL_SDM_FRA_EN ) ;
mtk_mipi_tx_set_bits ( mipi_tx , MIPITX_DSI_PLL_CON0 , RG_DSI_MPPLL_PLL_EN ) ;
usleep_range ( 20 , 100 ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_PLL_CON1 ,
RG_DSI_MPPLL_SDM_SSC_EN ) ;
return 0 ;
}
static void mtk_mipi_tx_pll_unprepare ( struct clk_hw * hw )
{
struct mtk_mipi_tx * mipi_tx = mtk_mipi_tx_from_clk_hw ( hw ) ;
dev_dbg ( mipi_tx - > dev , " unprepare \n " ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_PLL_CON0 ,
RG_DSI_MPPLL_PLL_EN ) ;
mtk_mipi_tx_update_bits ( mipi_tx , MIPITX_DSI_PLL_PWR ,
RG_DSI_MPPLL_SDM_ISO_EN |
RG_DSI_MPPLL_SDM_PWR_ON ,
RG_DSI_MPPLL_SDM_ISO_EN ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_TOP_CON ,
RG_DSI_LNT_HS_BIAS_EN ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_CON ,
RG_DSI_CKG_LDOOUT_EN | RG_DSI_LDOCORE_EN ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_BG_CON ,
RG_DSI_BG_CKEN | RG_DSI_BG_CORE_EN ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_PLL_CON0 ,
RG_DSI_MPPLL_DIV_MSK ) ;
}
static long mtk_mipi_tx_pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
return clamp_val ( rate , 50000000 , 1250000000 ) ;
}
static int mtk_mipi_tx_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct mtk_mipi_tx * mipi_tx = mtk_mipi_tx_from_clk_hw ( hw ) ;
dev_dbg ( mipi_tx - > dev , " set rate: %lu Hz \n " , rate ) ;
mipi_tx - > data_rate = rate ;
return 0 ;
}
static unsigned long mtk_mipi_tx_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct mtk_mipi_tx * mipi_tx = mtk_mipi_tx_from_clk_hw ( hw ) ;
return mipi_tx - > data_rate ;
}
static const struct clk_ops mtk_mipi_tx_pll_ops = {
. prepare = mtk_mipi_tx_pll_prepare ,
. unprepare = mtk_mipi_tx_pll_unprepare ,
. round_rate = mtk_mipi_tx_pll_round_rate ,
. set_rate = mtk_mipi_tx_pll_set_rate ,
. recalc_rate = mtk_mipi_tx_pll_recalc_rate ,
} ;
static int mtk_mipi_tx_power_on_signal ( struct phy * phy )
{
struct mtk_mipi_tx * mipi_tx = phy_get_drvdata ( phy ) ;
unsigned int reg ;
for ( reg = MIPITX_DSI_CLOCK_LANE ;
reg < = MIPITX_DSI_DATA_LANE3 ; reg + = 4 )
mtk_mipi_tx_set_bits ( mipi_tx , reg , RG_DSI_LNTx_LDOOUT_EN ) ;
mtk_mipi_tx_clear_bits ( mipi_tx , MIPITX_DSI_TOP_CON ,
RG_DSI_PAD_TIE_LOW_EN ) ;
return 0 ;
}
static int mtk_mipi_tx_power_on ( struct phy * phy )
{
struct mtk_mipi_tx * mipi_tx = phy_get_drvdata ( phy ) ;
int ret ;
/* Power up core and enable PLL */
ret = clk_prepare_enable ( mipi_tx - > pll ) ;
if ( ret < 0 )
return ret ;
/* Enable DSI Lane LDO outputs, disable pad tie low */
mtk_mipi_tx_power_on_signal ( phy ) ;
return 0 ;
}
static void mtk_mipi_tx_power_off_signal ( struct phy * phy )
{
struct mtk_mipi_tx * mipi_tx = phy_get_drvdata ( phy ) ;
unsigned int reg ;
mtk_mipi_tx_set_bits ( mipi_tx , MIPITX_DSI_TOP_CON ,
RG_DSI_PAD_TIE_LOW_EN ) ;
for ( reg = MIPITX_DSI_CLOCK_LANE ;
reg < = MIPITX_DSI_DATA_LANE3 ; reg + = 4 )
mtk_mipi_tx_clear_bits ( mipi_tx , reg , RG_DSI_LNTx_LDOOUT_EN ) ;
}
static int mtk_mipi_tx_power_off ( struct phy * phy )
{
struct mtk_mipi_tx * mipi_tx = phy_get_drvdata ( phy ) ;
/* Enable pad tie low, disable DSI Lane LDO outputs */
mtk_mipi_tx_power_off_signal ( phy ) ;
/* Disable PLL and power down core */
clk_disable_unprepare ( mipi_tx - > pll ) ;
return 0 ;
}
static const struct phy_ops mtk_mipi_tx_ops = {
. power_on = mtk_mipi_tx_power_on ,
. power_off = mtk_mipi_tx_power_off ,
. owner = THIS_MODULE ,
} ;
static int mtk_mipi_tx_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mtk_mipi_tx * mipi_tx ;
struct resource * mem ;
struct clk * ref_clk ;
const char * ref_clk_name ;
struct clk_init_data clk_init = {
. ops = & mtk_mipi_tx_pll_ops ,
. num_parents = 1 ,
. parent_names = ( const char * const * ) & ref_clk_name ,
. flags = CLK_SET_RATE_GATE ,
} ;
struct phy * phy ;
struct phy_provider * phy_provider ;
int ret ;
mipi_tx = devm_kzalloc ( dev , sizeof ( * mipi_tx ) , GFP_KERNEL ) ;
if ( ! mipi_tx )
return - ENOMEM ;
mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
mipi_tx - > regs = devm_ioremap_resource ( dev , mem ) ;
if ( IS_ERR ( mipi_tx - > regs ) ) {
ret = PTR_ERR ( mipi_tx - > regs ) ;
dev_err ( dev , " Failed to get memory resource: %d \n " , ret ) ;
return ret ;
}
ref_clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( ref_clk ) ) {
ret = PTR_ERR ( ref_clk ) ;
dev_err ( dev , " Failed to get reference clock: %d \n " , ret ) ;
return ret ;
}
ref_clk_name = __clk_get_name ( ref_clk ) ;
ret = of_property_read_string ( dev - > of_node , " clock-output-names " ,
& clk_init . name ) ;
if ( ret < 0 ) {
dev_err ( dev , " Failed to read clock-output-names: %d \n " , ret ) ;
return ret ;
}
mipi_tx - > pll_hw . init = & clk_init ;
mipi_tx - > pll = devm_clk_register ( dev , & mipi_tx - > pll_hw ) ;
if ( IS_ERR ( mipi_tx - > pll ) ) {
ret = PTR_ERR ( mipi_tx - > pll ) ;
dev_err ( dev , " Failed to register PLL: %d \n " , ret ) ;
return ret ;
}
phy = devm_phy_create ( dev , NULL , & mtk_mipi_tx_ops ) ;
if ( IS_ERR ( phy ) ) {
ret = PTR_ERR ( phy ) ;
dev_err ( dev , " Failed to create MIPI D-PHY: %d \n " , ret ) ;
return ret ;
}
phy_set_drvdata ( phy , mipi_tx ) ;
phy_provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
2016-07-01 16:59:34 +03:00
if ( IS_ERR ( phy_provider ) ) {
2016-01-04 20:36:35 +03:00
ret = PTR_ERR ( phy_provider ) ;
return ret ;
}
mipi_tx - > dev = dev ;
return of_clk_add_provider ( dev - > of_node , of_clk_src_simple_get ,
mipi_tx - > pll ) ;
}
static int mtk_mipi_tx_remove ( struct platform_device * pdev )
{
of_clk_del_provider ( pdev - > dev . of_node ) ;
return 0 ;
}
static const struct of_device_id mtk_mipi_tx_match [ ] = {
{ . compatible = " mediatek,mt8173-mipi-tx " , } ,
{ } ,
} ;
struct platform_driver mtk_mipi_tx_driver = {
. probe = mtk_mipi_tx_probe ,
. remove = mtk_mipi_tx_remove ,
. driver = {
. name = " mediatek-mipi-tx " ,
. of_match_table = mtk_mipi_tx_match ,
} ,
} ;