428 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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;
}