2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-01-04 18:36:35 +01:00
/*
* Copyright ( c ) 2015 MediaTek Inc .
*/
2020-10-06 07:37:07 +08:00
# include "phy-mtk-mipi-dsi.h"
2016-01-04 18:36:35 +01:00
2019-08-07 16:46:44 +08:00
inline struct mtk_mipi_tx * mtk_mipi_tx_from_clk_hw ( struct clk_hw * hw )
2016-01-04 18:36:35 +01:00
{
return container_of ( hw , struct mtk_mipi_tx , pll_hw ) ;
}
2019-08-07 16:46:44 +08:00
void mtk_mipi_tx_clear_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 bits )
2016-01-04 18:36:35 +01:00
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( temp & ~ bits , mipi_tx - > regs + offset ) ;
}
2019-08-07 16:46:44 +08:00
void mtk_mipi_tx_set_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 bits )
2016-01-04 18:36:35 +01:00
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( temp | bits , mipi_tx - > regs + offset ) ;
}
2019-08-07 16:46:44 +08:00
void mtk_mipi_tx_update_bits ( struct mtk_mipi_tx * mipi_tx , u32 offset ,
u32 mask , u32 data )
2016-01-04 18:36:35 +01:00
{
u32 temp = readl ( mipi_tx - > regs + offset ) ;
writel ( ( temp & ~ mask ) | ( data & mask ) , mipi_tx - > regs + offset ) ;
}
2019-08-07 16:46:44 +08:00
int mtk_mipi_tx_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
2016-01-04 18:36:35 +01:00
{
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 ;
}
2019-08-07 16:46:44 +08:00
unsigned long mtk_mipi_tx_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
2016-01-04 18:36:35 +01:00
{
struct mtk_mipi_tx * mipi_tx = mtk_mipi_tx_from_clk_hw ( hw ) ;
return mipi_tx - > data_rate ;
}
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 */
2019-08-07 16:46:44 +08:00
mipi_tx - > driver_data - > mipi_tx_enable_signal ( phy ) ;
2016-01-04 18:36:35 +01:00
return 0 ;
}
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 */
2019-08-07 16:46:44 +08:00
mipi_tx - > driver_data - > mipi_tx_disable_signal ( phy ) ;
2016-01-04 18:36:35 +01:00
/* 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 ,
} ;
2020-04-11 15:44:08 +08:00
static void mtk_mipi_tx_get_calibration_datal ( struct mtk_mipi_tx * mipi_tx )
{
struct nvmem_cell * cell ;
size_t len ;
u32 * buf ;
cell = nvmem_cell_get ( mipi_tx - > dev , " calibration-data " ) ;
if ( IS_ERR ( cell ) ) {
dev_info ( mipi_tx - > dev , " can't get nvmem_cell_get, ignore it \n " ) ;
return ;
}
buf = ( u32 * ) nvmem_cell_read ( cell , & len ) ;
nvmem_cell_put ( cell ) ;
if ( IS_ERR ( buf ) ) {
dev_info ( mipi_tx - > dev , " can't get data, ignore it \n " ) ;
return ;
}
if ( len < 3 * sizeof ( u32 ) ) {
dev_info ( mipi_tx - > dev , " invalid calibration data \n " ) ;
kfree ( buf ) ;
return ;
}
mipi_tx - > rt_code [ 0 ] = ( ( buf [ 0 ] > > 6 & 0x1f ) < < 5 ) |
( buf [ 0 ] > > 11 & 0x1f ) ;
mipi_tx - > rt_code [ 1 ] = ( ( buf [ 1 ] > > 27 & 0x1f ) < < 5 ) |
( buf [ 0 ] > > 1 & 0x1f ) ;
mipi_tx - > rt_code [ 2 ] = ( ( buf [ 1 ] > > 17 & 0x1f ) < < 5 ) |
( buf [ 1 ] > > 22 & 0x1f ) ;
mipi_tx - > rt_code [ 3 ] = ( ( buf [ 1 ] > > 7 & 0x1f ) < < 5 ) |
( buf [ 1 ] > > 12 & 0x1f ) ;
mipi_tx - > rt_code [ 4 ] = ( ( buf [ 2 ] > > 27 & 0x1f ) < < 5 ) |
( buf [ 1 ] > > 2 & 0x1f ) ;
kfree ( buf ) ;
}
2016-01-04 18:36:35 +01:00
static int mtk_mipi_tx_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mtk_mipi_tx * mipi_tx ;
const char * ref_clk_name ;
2019-08-07 16:46:44 +08:00
struct clk * ref_clk ;
2016-01-04 18:36:35 +01:00
struct clk_init_data clk_init = {
. 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 ;
2017-03-31 19:30:30 +08:00
mipi_tx - > driver_data = of_device_get_match_data ( dev ) ;
2021-12-24 08:21:03 +00:00
if ( ! mipi_tx - > driver_data )
return - ENODEV ;
2019-08-07 16:46:44 +08:00
2021-08-17 17:19:47 +08:00
mipi_tx - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( mipi_tx - > regs ) )
2021-04-08 07:48:50 -04:00
return PTR_ERR ( mipi_tx - > regs ) ;
2016-01-04 18:36:35 +01:00
ref_clk = devm_clk_get ( dev , NULL ) ;
2022-03-28 16:52:17 +02:00
if ( IS_ERR ( ref_clk ) )
return dev_err_probe ( dev , PTR_ERR ( ref_clk ) ,
" Failed to get reference clock \n " ) ;
2019-08-07 16:46:44 +08:00
2020-04-11 15:44:07 +08:00
ret = of_property_read_u32 ( dev - > of_node , " drive-strength-microamp " ,
& mipi_tx - > mipitx_drive ) ;
/* If can't get the "mipi_tx->mipitx_drive", set it default 0x8 */
if ( ret < 0 )
mipi_tx - > mipitx_drive = 4600 ;
/* check the mipitx_drive valid */
if ( mipi_tx - > mipitx_drive > 6000 | | mipi_tx - > mipitx_drive < 3000 ) {
dev_warn ( dev , " drive-strength-microamp is invalid %d, not in 3000 ~ 6000 \n " ,
mipi_tx - > mipitx_drive ) ;
mipi_tx - > mipitx_drive = clamp_val ( mipi_tx - > mipitx_drive , 3000 ,
6000 ) ;
}
2016-01-04 18:36:35 +01:00
ref_clk_name = __clk_get_name ( ref_clk ) ;
ret = of_property_read_string ( dev - > of_node , " clock-output-names " ,
& clk_init . name ) ;
2022-03-28 16:52:17 +02:00
if ( ret < 0 )
return dev_err_probe ( dev , ret , " Failed to read clock-output-names \n " ) ;
2016-01-04 18:36:35 +01:00
2019-08-07 16:46:44 +08:00
clk_init . ops = mipi_tx - > driver_data - > mipi_tx_clk_ops ;
2016-01-04 18:36:35 +01:00
mipi_tx - > pll_hw . init = & clk_init ;
mipi_tx - > pll = devm_clk_register ( dev , & mipi_tx - > pll_hw ) ;
2022-03-28 16:52:17 +02:00
if ( IS_ERR ( mipi_tx - > pll ) )
return dev_err_probe ( dev , PTR_ERR ( mipi_tx - > pll ) , " Failed to register PLL \n " ) ;
2016-01-04 18:36:35 +01:00
phy = devm_phy_create ( dev , NULL , & mtk_mipi_tx_ops ) ;
2022-03-28 16:52:17 +02:00
if ( IS_ERR ( phy ) )
return dev_err_probe ( dev , PTR_ERR ( phy ) , " Failed to create MIPI D-PHY \n " ) ;
2016-01-04 18:36:35 +01:00
phy_set_drvdata ( phy , mipi_tx ) ;
phy_provider = devm_of_phy_provider_register ( dev , of_phy_simple_xlate ) ;
2021-08-17 17:19:46 +08:00
if ( IS_ERR ( phy_provider ) )
return PTR_ERR ( phy_provider ) ;
2016-01-04 18:36:35 +01:00
mipi_tx - > dev = dev ;
2020-04-11 15:44:08 +08:00
mtk_mipi_tx_get_calibration_datal ( mipi_tx ) ;
2016-01-04 18:36:35 +01:00
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 [ ] = {
2017-03-31 19:30:39 +08:00
{ . compatible = " mediatek,mt2701-mipi-tx " ,
. data = & mt2701_mipitx_data } ,
2017-03-31 19:30:30 +08:00
{ . compatible = " mediatek,mt8173-mipi-tx " ,
. data = & mt8173_mipitx_data } ,
2019-08-07 16:46:45 +08:00
{ . compatible = " mediatek,mt8183-mipi-tx " ,
. data = & mt8183_mipitx_data } ,
2019-08-07 16:46:44 +08:00
{ } ,
2016-01-04 18:36:35 +01:00
} ;
2021-02-03 12:06:30 +01:00
MODULE_DEVICE_TABLE ( of , mtk_mipi_tx_match ) ;
2016-01-04 18:36:35 +01:00
2021-01-12 09:38:04 +08:00
static struct platform_driver mtk_mipi_tx_driver = {
2016-01-04 18:36:35 +01:00
. probe = mtk_mipi_tx_probe ,
. remove = mtk_mipi_tx_remove ,
. driver = {
. name = " mediatek-mipi-tx " ,
. of_match_table = mtk_mipi_tx_match ,
} ,
} ;
2020-10-04 10:30:03 +08:00
module_platform_driver ( mtk_mipi_tx_driver ) ;
2019-08-07 16:46:44 +08:00
2020-10-04 10:30:03 +08:00
MODULE_DESCRIPTION ( " MediaTek MIPI TX Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;