428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | ||
| /*
 | ||
|  * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
 | ||
|  *
 | ||
|  */
 | ||
| 
 | ||
| #include <linux/kernel.h>
 | ||
| #include <linux/iopoll.h>
 | ||
| #include "rkx110_x120.h"
 | ||
| #include "rkx120_reg.h"
 | ||
| #include "serdes_combphy.h"
 | ||
| 
 | ||
| #define CLANE_PARA0		0x0000
 | ||
| #define CLANE_PARA1		0x0004
 | ||
| #define T_INITTIME_C(x)		UPDATE(x, 31, 0)
 | ||
| #define CLANE_PARA2		0x0008
 | ||
| #define T_CLKPREPARE_C(x)	UPDATE(x, 23, 16)
 | ||
| #define T_CLKZERO_C(x)		UPDATE(x, 15, 8)
 | ||
| #define T_CLKPRE_C(x)		UPDATE(x, 7, 0)
 | ||
| #define CLANE_PARA3		0x000c
 | ||
| #define T_CLKPOST_C_MASK	GENMASK(23, 16)
 | ||
| #define T_CLKPOST_C(x)		UPDATE(x, 23, 16)
 | ||
| #define T_CLKTRAIL_C_MASK	GENMASK(15, 8)
 | ||
| #define T_CLKTRAIL_C(x)		UPDATE(x, 15, 8)
 | ||
| #define T_HSEXIT_C_MASK		GENMASK(7, 0)
 | ||
| #define T_HSEXIT_C(x)		UPDATE(x, 7, 0)
 | ||
| #define DLANE0_PARA0		0x0010
 | ||
| #define T_RST2ENLPTX_D(x)	UPDATE(x, 15, 0)
 | ||
| #define DLANE0_PARA1		0x0014
 | ||
| #define T_INITTIME_D(x)		UPDATE(x, 31, 0)
 | ||
| #define DLANE0_PARA2		0x0018
 | ||
| #define T_HSPREPARE_D(x)	UPDATE(x, 31, 24)
 | ||
| #define T_HSZERO_D(x)		UPDATE(x, 23, 16)
 | ||
| #define T_HSTRAIL_D(x)		UPDATE(x, 15, 8)
 | ||
| #define T_HSEXIT_D(x)		UPDATE(x, 7, 0)
 | ||
| #define DLANE0_PARA3		0x001c
 | ||
| #define T_WAKEUP_D(x)		UPDATE(x, 31, 0)
 | ||
| #define DLANE0_PARA4		0x0020
 | ||
| #define T_TAGO_D0(x)		UPDATE(x, 23, 16)
 | ||
| #define T_TASURE_D0(x)		UPDATE(x, 15, 8)
 | ||
| #define T_TAGET_D0(x)		UPDATE(x, 7, 0)
 | ||
| #define DLANE_PARA0(x)		(0x0014 + (x * 0x0010))
 | ||
| #define DLANE_PARA1(x)		(0x0018 + (x * 0x0010))
 | ||
| #define DLANE_PARA2(x)		(0x001C + (x * 0x0010))
 | ||
| #define DLANE_PARA3(x)		(0x0020 + (x * 0x0010))
 | ||
| #define COMMON_PARA0		(0x0054)
 | ||
| #define T_LPX(x)		UPDATE(x, 7, 0)
 | ||
| #define CTRL_PARA0		0x0058
 | ||
| #define PWON_SEL		BIT(3)
 | ||
| #define PWON_DSI		BIT(1)
 | ||
| #define SU_IDDQ_EN		BIT(0)
 | ||
| #define PLL_CTRL_PARA0		0x005c
 | ||
| #define PLL_LOCK		BIT(27)
 | ||
| #define RATE_MASK		GENMASK(26, 24)
 | ||
| #define RATE(x)			UPDATE(x, 26, 24)
 | ||
| #define REFCLK_DIV_MASK		GENMASK(23, 19)
 | ||
| #define REFCLK_DIV(x)		UPDATE(x, 23, 19)
 | ||
| #define PLL_DIV_MASK		GENMASK(18, 4)
 | ||
| #define PLL_DIV(x)		UPDATE(x, 18, 4)
 | ||
| #define DSI_PIXELCLK_DIV(x)	UPDATE(x, 3, 0)
 | ||
| #define PLL_CTRL_PARA1		0x0060
 | ||
| #define PLL_CTRL(x)		UPDATE(x, 31, 0)
 | ||
| #define RCAL_CTRL		0x0064
 | ||
| #define RCAL_EN			BIT(13)
 | ||
| #define RCAL_TRIM(x)		UPDATE(x, 12, 9)
 | ||
| #define RCAL_DONE		BIT(0)
 | ||
| #define TRIM_PARA		0x0068
 | ||
| #define HSTX_AMP_TRIM(x)	UPDATE(x, 13, 11)
 | ||
| #define LPTX_SR_TRIM(x)		UPDATE(x, 10, 8)
 | ||
| #define LPRX_VREF_TRIM(x)	UPDATE(x, 7, 4)
 | ||
| #define LPCD_VREF_TRIM(x)	UPDATE(x, 3, 0)
 | ||
| #define TEST_PARA0		0x006c
 | ||
| #define FSET_EN			BIT(3)
 | ||
| 
 | ||
| #define CLANE_PARA4		0x0078
 | ||
| #define INTERFACE_PARA		0x007c
 | ||
| #define TXREADYESC_VLD(x)	UPDATE(x, 15, 8)
 | ||
| #define RXVALIDESC_VLD(x)	UPDATE(x, 7, 0)
 | ||
| 
 | ||
| #define GRF_MIPITX_CON0		0x0000
 | ||
| #define PHYSHUTDWN(x)		HIWORD_UPDATE(x, GENMASK(10, 10), 10)
 | ||
| #define LVDS_REFCLK_DIV(x)	HIWORD_UPDATE(x, GENMASK(9, 6), 6)
 | ||
| #define PHY_MODE(x)		HIWORD_UPDATE(x, GENMASK(4, 3), 3)
 | ||
| #define RATE_LVDS(x)		HIWORD_UPDATE(x, GENMASK(2, 1), 1)
 | ||
| #define GRF_MIPITX_CON1		0x0004
 | ||
| #define PWON_PLL(x)		HIWORD_UPDATE(x, GENMASK(15, 15), 15)
 | ||
| #define LVDS_PLL_DIV(x)		HIWORD_UPDATE(x, GENMASK(14, 0), 0)
 | ||
| 
 | ||
| #define GRF_MIPITX_CON5		0x0014
 | ||
| #define TXCLK_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(14, 12), 12)
 | ||
| #define TX3_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(11, 9), 9)
 | ||
| #define TX2_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(8, 6), 6)
 | ||
| #define TX1_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(5, 3), 3)
 | ||
| #define TX0_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(2, 0), 0)
 | ||
| #define GRF_MIPITX_CON6		0x0018
 | ||
| #define TX0_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
 | ||
| #define GRF_MIPITX_CON7		0x001c
 | ||
| #define TX1_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
 | ||
| #define GRF_MIPITX_CON8		0x0020
 | ||
| #define TX2_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
 | ||
| #define GRF_MIPITX_CON9		0x0024
 | ||
| #define TX3_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
 | ||
| #define GRF_MIPITX_CON10	0x0028
 | ||
| #define TXCK_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
 | ||
| 
 | ||
| #define GRF_MIPITX_CON13	0x0034
 | ||
| #define TX_IDLE(x)		HIWORD_UPDATE(x, GENMASK(4, 0), 0)
 | ||
| #define GRF_MIPITX_CON14	0x0038
 | ||
| #define TX_PD(x)		HIWORD_UPDATE(x, GENMASK(14, 10), 10)
 | ||
| 
 | ||
| #define GRF_MIPI_STATUS		0x0080
 | ||
| #define PHYLOCK			BIT(0)
 | ||
| 
 | ||
| static void rkx120_combtxphy_dsi_timing_init(struct rk_serdes *des,
 | ||
| 					     struct rkx120_combtxphy *combtxphy,
 | ||
| 					     u8 dev_id)
 | ||
| {
 | ||
| 	const struct configure_opts_combphy cfg = combtxphy->mipi_dphy_cfg;
 | ||
| 	u32 byte_clk = DIV_ROUND_CLOSEST_ULL(combtxphy->rate, 8);
 | ||
| 	u32 esc_div = DIV_ROUND_UP(byte_clk, 20 * USEC_PER_SEC);
 | ||
| 	u64 t_byte_clk = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, byte_clk);
 | ||
| 	u32 t_clkprepare, t_clkzero, t_clkpre, t_clkpost, t_clktrail;
 | ||
| 	u32 t_init, t_hsexit, t_hsprepare, t_hszero, t_wakeup, t_hstrail;
 | ||
| 	u32 t_tago, t_tasure, t_taget;
 | ||
| 	u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
 | ||
| 
 | ||
| 	serdes_combphy_write(des, dev_id, base + INTERFACE_PARA,
 | ||
| 			     TXREADYESC_VLD(esc_div - 2) |
 | ||
| 			     RXVALIDESC_VLD(esc_div - 2));
 | ||
| 	serdes_combphy_write(des, dev_id, base + COMMON_PARA0, esc_div);
 | ||
| 	serdes_combphy_update_bits(des, dev_id, base + TEST_PARA0, FSET_EN, FSET_EN);
 | ||
| 
 | ||
| 	t_init = DIV_ROUND_UP(cfg.init, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + CLANE_PARA1, T_INITTIME_C(t_init));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE0_PARA1, T_INITTIME_D(t_init));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA1(1), T_INITTIME_D(t_init));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA1(2), T_INITTIME_D(t_init));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA1(3), T_INITTIME_D(t_init));
 | ||
| 
 | ||
| 	t_clkprepare = DIV_ROUND_UP(cfg.clk_prepare, t_byte_clk) - 1;
 | ||
| 	t_clkzero = DIV_ROUND_UP(cfg.clk_zero, t_byte_clk) - 1;
 | ||
| 	t_clkpre = DIV_ROUND_UP(cfg.clk_pre, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + CLANE_PARA2,
 | ||
| 			     T_CLKPREPARE_C(t_clkprepare) |
 | ||
| 			     T_CLKZERO_C(t_clkzero) | T_CLKPRE_C(t_clkpre));
 | ||
| 
 | ||
| 	t_clkpost = DIV_ROUND_UP(cfg.clk_post, t_byte_clk) - 1;
 | ||
| 	t_clktrail = DIV_ROUND_UP(cfg.clk_trail, t_byte_clk) - 1;
 | ||
| 	t_hsexit = DIV_ROUND_UP(cfg.hs_exit, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + CLANE_PARA3,
 | ||
| 			     T_CLKPOST_C(t_clkpost) |
 | ||
| 			     T_CLKTRAIL_C(t_clktrail) |
 | ||
| 			     T_HSEXIT_C(t_hsexit));
 | ||
| 
 | ||
| 	t_hsprepare = DIV_ROUND_UP(cfg.hs_prepare, t_byte_clk) - 1;
 | ||
| 	t_hszero = DIV_ROUND_UP(cfg.hs_zero, t_byte_clk) - 1;
 | ||
| 	t_hstrail = DIV_ROUND_UP(cfg.hs_trail, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE0_PARA2,
 | ||
| 			     T_HSPREPARE_D(t_hsprepare) |
 | ||
| 			     T_HSZERO_D(t_hszero) |
 | ||
| 			     T_HSTRAIL_D(t_hstrail) |
 | ||
| 			     T_HSEXIT_D(t_hsexit));
 | ||
| 
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA2(1),
 | ||
| 			     T_HSPREPARE_D(t_hsprepare) |
 | ||
| 			     T_HSZERO_D(t_hszero) |
 | ||
| 			     T_HSTRAIL_D(t_hstrail) |
 | ||
| 			     T_HSEXIT_D(t_hsexit));
 | ||
| 
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA2(2),
 | ||
| 			     T_HSPREPARE_D(t_hsprepare) |
 | ||
| 			     T_HSZERO_D(t_hszero) |
 | ||
| 			     T_HSTRAIL_D(t_hstrail) |
 | ||
| 			     T_HSEXIT_D(t_hsexit));
 | ||
| 
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA2(3),
 | ||
| 			     T_HSPREPARE_D(t_hsprepare) |
 | ||
| 			     T_HSZERO_D(t_hszero) |
 | ||
| 			     T_HSTRAIL_D(t_hstrail) |
 | ||
| 			     T_HSEXIT_D(t_hsexit));
 | ||
| 
 | ||
| 	t_wakeup = DIV_ROUND_UP(cfg.wakeup, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE0_PARA3, T_WAKEUP_D(t_wakeup));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA3(1), T_WAKEUP_D(t_wakeup));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA3(2), T_WAKEUP_D(t_wakeup));
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE_PARA3(3), T_WAKEUP_D(t_wakeup));
 | ||
| 	serdes_combphy_write(des, dev_id, base + CLANE_PARA4, T_WAKEUP_D(t_wakeup));
 | ||
| 
 | ||
| 	t_tago = DIV_ROUND_UP(cfg.ta_go, t_byte_clk) - 1;
 | ||
| 	t_tasure = DIV_ROUND_UP(cfg.ta_sure, t_byte_clk) - 1;
 | ||
| 	t_taget = DIV_ROUND_UP(cfg.ta_get, t_byte_clk) - 1;
 | ||
| 	serdes_combphy_write(des, dev_id, base + DLANE0_PARA4,
 | ||
| 			     T_TAGO_D0(t_tago) |
 | ||
| 			     T_TASURE_D0(t_tasure) |
 | ||
| 			     T_TAGET_D0(t_taget));
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_dsi_pll_set(struct rk_serdes *des,
 | ||
| 					 struct rkx120_combtxphy *combtxphy, u8 dev_id)
 | ||
| {
 | ||
| 	u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
 | ||
| 
 | ||
| 	serdes_combphy_update_bits(des, dev_id, base + PLL_CTRL_PARA0,
 | ||
| 				   RATE_MASK | REFCLK_DIV_MASK | PLL_DIV_MASK,
 | ||
| 				   RATE(combtxphy->rate_factor) |
 | ||
| 				   REFCLK_DIV(combtxphy->ref_div - 1) |
 | ||
| 				   PLL_DIV(combtxphy->fb_div));
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_dsi_power_on(struct rk_serdes *des,
 | ||
| 					  struct rkx120_combtxphy *combtxphy,
 | ||
| 					  u8 dev_id)
 | ||
| {
 | ||
| 	struct i2c_client *client = des->chip[dev_id].client;
 | ||
| 	u32 grf_base = RKX120_GRF_MIPI0_BASE;
 | ||
| 	u32 val;
 | ||
| 	int ret;
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
 | ||
| 			   PHY_MODE(COMBTX_PHY_MODE_VIDEO_MIPI));
 | ||
| 
 | ||
| 	serdes_combphy_get_default_config(combtxphy->rate, &combtxphy->mipi_dphy_cfg);
 | ||
| 	rkx120_combtxphy_dsi_timing_init(des, combtxphy, dev_id);
 | ||
| 	rkx120_combtxphy_dsi_pll_set(des, combtxphy, dev_id);
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
 | ||
| 
 | ||
| 	ret = read_poll_timeout(des->i2c_read_reg, ret,
 | ||
| 				!(ret < 0) && (val & PLL_LOCK),
 | ||
| 				0, MSEC_PER_SEC, false, client,
 | ||
| 				RKX120_MIPI_LVDS_TX_PHY0_BASE + PLL_CTRL_PARA0,
 | ||
| 				&val);
 | ||
| 	if (ret < 0)
 | ||
| 		dev_err(des->dev, "PLL is not locked\n");
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_dsi_power_off(struct rk_serdes *des, u8 dev_id)
 | ||
| {
 | ||
| 	struct i2c_client *client = des->chip[dev_id].client;
 | ||
| 	u32 grf_base = RKX120_GRF_MIPI0_BASE;
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(0));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(0));
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_lvds_power_on(struct rk_serdes *des,
 | ||
| 					   struct rkx120_combtxphy *combtxphy,
 | ||
| 					   u8 dev_id, u8 phy_id)
 | ||
| {
 | ||
| 	struct i2c_client *client = des->chip[dev_id].client;
 | ||
| 	u32 grf_base = (phy_id == 0) ?
 | ||
| 			RKX120_GRF_MIPI0_BASE : RKX120_GRF_MIPI1_BASE;
 | ||
| 	const struct {
 | ||
| 		unsigned long max_lane_mbps;
 | ||
| 		u8 rate_lvds;
 | ||
| 		u8 refclk_div;
 | ||
| 		u16 pll_div;
 | ||
| 	} hsfreqrange_table[] = {
 | ||
| 		{250, 2, 0, 0x3800},
 | ||
| 		{500, 1, 1, 0x3800},
 | ||
| 		{1000, 0, 3, 0x3800},
 | ||
| 	};
 | ||
| 	int ret;
 | ||
| 	u32 val;
 | ||
| 	u8 index;
 | ||
| 
 | ||
| 	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
 | ||
| 		if (combtxphy->rate < hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
 | ||
| 			break;
 | ||
| 
 | ||
| 	if (index == ARRAY_SIZE(hsfreqrange_table))
 | ||
| 		--index;
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0x1f));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0x1f));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
 | ||
| 			   LVDS_REFCLK_DIV(hsfreqrange_table[index].refclk_div) |
 | ||
| 			   RATE_LVDS(hsfreqrange_table[index].rate_lvds));
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1,
 | ||
| 			   LVDS_PLL_DIV(hsfreqrange_table[index].pll_div));
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON5,
 | ||
| 			   TXCLK_BUS_WIDTH(2) | TX3_BUS_WIDTH(2) |
 | ||
| 			   TX2_BUS_WIDTH(2) | TX1_BUS_WIDTH(2) |
 | ||
| 			   TX0_BUS_WIDTH(2));
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON6, TX0_CTL_LOW(0x80));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON7, TX0_CTL_LOW(0x80));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON8, TX0_CTL_LOW(0x80));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON9, TX0_CTL_LOW(0x80));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON10, TX0_CTL_LOW(0x80));
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1) |
 | ||
| 			   PHY_MODE(COMBTX_PHY_MODE_VIDEO_LVDS));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
 | ||
| 
 | ||
| 	ret = read_poll_timeout(des->i2c_read_reg, ret,
 | ||
| 				!(ret < 0) && (val & PHYLOCK),
 | ||
| 				0, MSEC_PER_SEC, false, client,
 | ||
| 				grf_base + GRF_MIPI_STATUS, &val);
 | ||
| 	if (ret < 0)
 | ||
| 		dev_err(des->dev, "PLL is not locked\n");
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0));
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_lvds_power_off(struct rk_serdes *des,
 | ||
| 					    struct rkx120_combtxphy *combtxphy,
 | ||
| 					    u8 dev_id, u8 phy_id)
 | ||
| {
 | ||
| 	struct i2c_client *client = des->chip[dev_id].client;
 | ||
| 	u32 grf_base = (phy_id == 0) ?
 | ||
| 			RKX120_GRF_MIPI0_BASE : RKX120_GRF_MIPI1_BASE;
 | ||
| 
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(0));
 | ||
| 	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(0));
 | ||
| }
 | ||
| 
 | ||
| void rkx120_combtxphy_power_on(struct rk_serdes *des, struct rkx120_combtxphy *combtxphy,
 | ||
| 			       u8 dev_id, u8 phy_id)
 | ||
| {
 | ||
| 	switch (combtxphy->mode) {
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_MIPI:
 | ||
| 		rkx120_combtxphy_dsi_power_on(des, combtxphy, dev_id);
 | ||
| 		break;
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_LVDS:
 | ||
| 		rkx120_combtxphy_lvds_power_on(des, combtxphy, dev_id, phy_id);
 | ||
| 		break;
 | ||
| 	default:
 | ||
| 		break;
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| void rkx120_combtxphy_power_off(struct rk_serdes *des, struct rkx120_combtxphy *combtxphy,
 | ||
| 				u8 dev_id, u8 phy_id)
 | ||
| {
 | ||
| 	switch (combtxphy->mode) {
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_MIPI:
 | ||
| 		rkx120_combtxphy_dsi_power_off(des, dev_id);
 | ||
| 		break;
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_LVDS:
 | ||
| 		rkx120_combtxphy_lvds_power_off(des, combtxphy, dev_id, phy_id);
 | ||
| 		break;
 | ||
| 	case COMBTX_PHY_MODE_GPIO:
 | ||
| 		break;
 | ||
| 	default:
 | ||
| 		break;
 | ||
| 	}
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_dsi_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
 | ||
| {
 | ||
| 	const struct {
 | ||
| 		unsigned long max_lane_mbps;
 | ||
| 		u8 refclk_div;
 | ||
| 		u8 post_factor;
 | ||
| 	} hsfreqrange_table[] = {
 | ||
| 		{ 100, 1, 5 },
 | ||
| 		{ 200, 1, 4 },
 | ||
| 		{ 400, 1, 3 },
 | ||
| 		{ 800, 1, 2},
 | ||
| 		{ 1600, 1, 1},
 | ||
| 	};
 | ||
| 	u64 ref_clk = 24 * USEC_PER_SEC;
 | ||
| 	u64 fvco;
 | ||
| 	u16 fb_div;
 | ||
| 	u8 ref_div, post_div, index;
 | ||
| 
 | ||
| 	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
 | ||
| 		if (rate <= hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
 | ||
| 			break;
 | ||
| 
 | ||
| 	if (index == ARRAY_SIZE(hsfreqrange_table))
 | ||
| 		--index;
 | ||
| 
 | ||
| 	/*
 | ||
| 	 * FVCO = Fckref * 8 * ( PI_POST_DIV + PF_POST_DIV) / R
 | ||
| 	 * data_rate = FVCO / post_div;
 | ||
| 	 */
 | ||
| 
 | ||
| 	ref_div = hsfreqrange_table[index].refclk_div;
 | ||
| 	post_div = 1 << hsfreqrange_table[index].post_factor;
 | ||
| 	fvco = rate * post_div;
 | ||
| 	fb_div = DIV_ROUND_CLOSEST_ULL((fvco * ref_div) << 10, ref_clk * 8);
 | ||
| 
 | ||
| 	rate = DIV_ROUND_CLOSEST_ULL(ref_clk * 8 * fb_div,
 | ||
| 				     (u64)(ref_div * post_div) << 10);
 | ||
| 
 | ||
| 	combtxphy->ref_div = ref_div;
 | ||
| 	combtxphy->fb_div = fb_div;
 | ||
| 	combtxphy->rate_factor = hsfreqrange_table[index].post_factor;
 | ||
| 	combtxphy->rate = rate;
 | ||
| }
 | ||
| 
 | ||
| static void rkx120_combtxphy_lvds_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
 | ||
| {
 | ||
| }
 | ||
| 
 | ||
| void rkx120_combtxphy_set_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
 | ||
| {
 | ||
| 	switch (combtxphy->mode) {
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_MIPI:
 | ||
| 		rkx120_combtxphy_dsi_pll_calc_rate(combtxphy, rate);
 | ||
| 		break;
 | ||
| 	case COMBTX_PHY_MODE_VIDEO_LVDS:
 | ||
| 		rkx120_combtxphy_lvds_pll_calc_rate(combtxphy, rate);
 | ||
| 		break;
 | ||
| 	default:
 | ||
| 		break;
 | ||
| 	}
 | ||
| 
 | ||
| 	combtxphy->rate = rate;
 | ||
| }
 | ||
| 
 | ||
| u64 rkx120_combtxphy_get_rate(struct rkx120_combtxphy *combtxphy)
 | ||
| {
 | ||
| 	return combtxphy->rate;
 | ||
| }
 | ||
| 
 | ||
| void rkx120_combtxphy_set_mode(struct rkx120_combtxphy *combtxphy, enum combtx_phy_mode mode)
 | ||
| {
 | ||
| 	combtxphy->mode = mode;
 | ||
| }
 |