293 lines
9.2 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2022 Rockchip Electronics Co., Ltd.
*
* Author: Guochun Huang <hero.huang@rock-chips.com>
*/
#include "hal/cru_api.h"
#include <linux/kernel.h>
#include <linux/iopoll.h>
#include "rkx110_x120.h"
#include "rkx110_reg.h"
#include "serdes_combphy.h"
#define SOFT_RST 0x0000
#define HS_CLK_SOFT_RST BIT(1)
#define CFG_CLK_SOFT_RST BIT(0)
#define PHY_RCAL 0x0004
#define RCAL_DONE BIT(17)
#define RCAL_OUT(x) UPDATE(x, 16, 13)
#define RCAL_CTL(x) UPDATE(x, 12, 5)
#define RCAL_TRIM(x) UPDATE(x, 4, 1)
#define RCAL_EN BIT(0)
#define ULP_RX_EN 0x0008
#define VOFFCAL_OUT 0x000c
#define CSI_CLK_VOFFCAL_DONE BIT(29)
#define CSI_CLK_VOFFCAL_OUT(x) UPDATE(x, 28, 24)
#define CSI_0_VOFFCAL_DONE BIT(23)
#define CSI_0_VOFFCAL_OUT(x) UPDATE(x, 22, 18)
#define CSI_1_VOFFCAL_DONE BIT(17)
#define CSI_1_VOFFCAL_OUT(x) UPDATE(x, 16, 12)
#define CSI_2_VOFFCAL_DONE BIT(11)
#define CSI_2_VOFFCAL_OUT(x) UPDATE(x, 10, 6)
#define CSI_3_VOFFCAL_DONE BIT(5)
#define CSI_3_VOFFCAL_OUT(x) UPDATE(x, 4, 0)
#define CSI_CTL01 0x0010
#define CSI_CTL1(x) UPDATE(x, 31, 16)
#define CSI_CTL0(x) UPDATE(x, 15, 0)
#define CSI_CTL23 0x0014
#define CSI_CTL3(x) UPDATE(x, 31, 16)
#define CSI_CTL2(x) UPDATE(x, 15, 0)
#define CSI_VINIT 0x001c
#define CSI_LPRX_VREF_TRIM UPDATE(x, 23, 20)
#define CSI_CLK_LPRX_VINIT(x) UPDATE(x, 19, 16)
#define CSI_3_LPRX_VINIT(x) UPDATE(x, 15, 12)
#define CSI_2_LPRX_VINIT(x) UPDATE(x, 11, 8)
#define CSI_1_LPRX_VINIT(x) UPDATE(x, 7, 4)
#define CSI_0_LPRX_VINIT(x) UPDATE(x, 3, 0)
#define CLANE_PARA 0x0020
#define T_CLK_TERM_EN(x) UPDATE(x, 15, 8)
#define T_CLK_SETTLE(x) UPDATE(x, 7, 0)
#define T_HS_TERMEN 0x0024
#define T_D3_TERMEN(x) UPDATE(x, 31, 24)
#define T_D2_TERMEN(x) UPDATE(x, 23, 16)
#define T_D1_TERMEN(x) UPDATE(x, 15, 8)
#define T_D0_TERMEN(x) UPDATE(x, 7, 0)
#define T_HS_SETTLE 0x0028
#define T_D3_SETTLE(x) UPDATE(x, 31, 24)
#define T_D2_SETTLE(x) UPDATE(x, 23, 16)
#define T_D1_SETTLE(x) UPDATE(x, 15, 8)
#define T_D0_SETTLE(x) UPDATE(x, 7, 0)
#define T_CLANE_INIT 0x0030
#define T_CLK_INIT(x) UPDATE(x, 23, 0)
#define T_LANE0_INIT 0x0034
#define T_D0_INIT(x) UPDATE(x, 23, 0)
#define T_LANE1_INIT 0x0038
#define T_D1_INIT(x) UPDATE(x, 23, 0)
#define T_LANE2_INIT 0x003c
#define T_D2_INIT(x) UPDATE(x, 23, 0)
#define T_LANE3_INIT 0x0040
#define T_D3_INIT(x) UPDATE(x, 23, 0)
#define TLPX_CTRL 0x0044
#define EN_TLPX_CHECK BIT(8)
#define TLPX(x) UPDATE(x, 7, 0)
#define NE_SWAP 0x0048
#define DPDN_SWAP_LANE(x) UPDATE(1 << x, 11, 8)
#define LANE_SWAP_LAN3(x) UPDATE(x, 7, 6)
#define LANE_SWAP_LANE2(x) UPDATE(x, 5, 4)
#define LANE_SWAP_LANE1(x) UPDATE(x, 3, 2)
#define LANE_SWAP_LANE0(x) UPDATE(x, 1, 0)
#define MISC_INFO 0x004c
#define ULPS_LP10_SEL BIT(1)
#define LONG_SOTSYNC_EN BIT(0)
#define GRF_MIPI_RX_CON0 0x0000
#define RXCK_RTRM(x) HIWORD_UPDATE(x, GENMASK(15, 12), 12)
#define LVDS_RX3_PD(x) HIWORD_UPDATE(x, BIT(10), 10)
#define LVDS_RX2_PD(x) HIWORD_UPDATE(x, BIT(9), 9)
#define LVDS_RX1_PD(x) HIWORD_UPDATE(x, BIT(8), 8)
#define LVDS_RX0_PD(x) HIWORD_UPDATE(x, BIT(7), 7)
#define LVDS_RXCK_PD(x) HIWORD_UPDATE(x, BIT(6), 6)
#define LANE3_ENABLE(x) HIWORD_UPDATE(x, BIT(5), 5)
#define LANE2_ENABLE(x) HIWORD_UPDATE(x, BIT(4), 4)
#define LANE1_ENABLE(x) HIWORD_UPDATE(x, BIT(3), 3)
#define LANE0_ENABLE(x) HIWORD_UPDATE(x, BIT(2), 2)
#define PHY_MODE(x) HIWORD_UPDATE(x, GENMASK(1, 0), 0)
#define GRF_MIPI_RX_CON2 0x0008
#define RXCK_CTL_5(x) HIWORD_UPDATE(x, BIT(11), 11)
#define DDR_CLK_DUTY_CYCLE(x) HIWORD_UPDATE(x, GENMASK(10, 8), 8)
#define BUS_WIDTH_SELECTION(x) HIWORD_UPDATE(x, GENMASK(7, 5), 5)
#define RXCK_CTL_2(x) HIWORD_UPDATE(x, BIT(4), 4)
#define RXCK_CTL_1(x) HIWORD_UPDATE(x, GENMASK(2, 1), 1)
#define RXCK_CTL_0(x) HIWORD_UPDATE(x, BIT(0), 0)
#define GRF_MIPI_RX_CON4 0x0010
#define GRF_MIPI_RX_CON5 0x0014
#define GRF_MIPI_RX_CON6 0x0018
#define GRF_MIPI_RX_CON7 0x001c
#define GRF_SOC_STATUS 0x0080
#define DLL_LOCK BIT(24)
#define LVDS1_MSBSEL(x) HIWORD_UPDATE(x, BIT(5), 5)
#define LVDS0_MSBSEL(x) HIWORD_UPDATE(x, BIT(2), 2)
static void
rkx110_combrxphy_dsi_timing_init(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
}
static void rkx110_combrxphy_dsi_power_on(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
struct hwclk *hwclk = ser->chip[dev_id].hwclk;
struct i2c_client *client = ser->chip[dev_id].client;
u32 val = 0;
u32 grf_base;
switch (id) {
case COMBPHY_0:
hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0, 100 * USEC_PER_SEC);
dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY0:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0));
break;
case COMBPHY_1:
hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1, 100 * USEC_PER_SEC);
dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY1:%d\n",
hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1));
break;
default:
break;
}
serdes_combphy_get_default_config(combrxphy->rate,
&combrxphy->mipi_dphy_cfg);
switch (combrxphy->lanes) {
case 4:
val |= LANE3_ENABLE(1);
fallthrough;
case 3:
val |= LANE2_ENABLE(1);
fallthrough;
case 2:
val |= LANE1_ENABLE(1);
fallthrough;
case 1:
default:
val |= LANE0_ENABLE(1);
break;
}
grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
PHY_MODE(COMBRX_PHY_MODE_VIDEO_MIPI) | val);
rkx110_combrxphy_dsi_timing_init(ser, combrxphy, dev_id, id);
}
static void rkx110_combrxphy_dsi_power_off(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[dev_id].client;
u32 grf_base;
grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LANE3_ENABLE(0) | LANE2_ENABLE(0) |
LANE1_ENABLE(0) | LANE0_ENABLE(0));
}
static void rkx110_combrxphy_lvds_power_on(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[dev_id].client;
u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
u32 val;
int ret;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RXCK_PD(0) | PHY_MODE(COMBRX_PHY_MODE_VIDEO_LVDS));
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON2,
BUS_WIDTH_SELECTION(7) | RXCK_CTL_2(1) |
RXCK_CTL_1(1) | RXCK_CTL_0(0));
ser->i2c_write_reg(client, SER_GRF_SOC_CON2,
id ? LVDS1_MSBSEL(0) : LVDS0_MSBSEL(0));
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON4, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON5, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON6, 0xffff0f08);
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON7, 0xffff0f08);
ret = read_poll_timeout(ser->i2c_read_reg, ret,
!(ret < 0) && (val & DLL_LOCK),
0, MSEC_PER_SEC, false, client,
grf_base + GRF_SOC_STATUS, &val);
if (ret < 0)
dev_err(ser->dev, "DLL is not locked\n");
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RX3_PD(0) | LVDS_RX2_PD(0) |
LVDS_RX1_PD(0) | LVDS_RX0_PD(0));
}
static void rkx110_combrxphy_lvds_power_off(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
struct i2c_client *client = ser->chip[dev_id].client;
u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
LVDS_RX3_PD(1) | LVDS_RX2_PD(1) |
LVDS_RX1_PD(1) | LVDS_RX0_PD(1));
}
static void rkx110_combrxphy_lvds_camera_power_on(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
}
static void rkx110_combrxphy_lvds_camera_power_off(struct rk_serdes *ser,
struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
}
void rkx110_combrxphy_power_on(struct rk_serdes *ser, struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
switch (combrxphy->mode) {
case COMBRX_PHY_MODE_VIDEO_MIPI:
rkx110_combrxphy_dsi_power_on(ser, combrxphy, dev_id, id);
break;
case COMBRX_PHY_MODE_VIDEO_LVDS:
rkx110_combrxphy_lvds_power_on(ser, combrxphy, dev_id, id);
break;
case COMBRX_PHY_MODE_LVDS_CAMERA:
rkx110_combrxphy_lvds_camera_power_on(ser, combrxphy, dev_id, id);
break;
default:
break;
}
}
void rkx110_combrxphy_power_off(struct rk_serdes *ser, struct rkx110_combrxphy *combrxphy,
u8 dev_id, enum comb_phy_id id)
{
switch (combrxphy->mode) {
case COMBRX_PHY_MODE_VIDEO_MIPI:
rkx110_combrxphy_dsi_power_off(ser, combrxphy, dev_id, id);
break;
case COMBRX_PHY_MODE_VIDEO_LVDS:
rkx110_combrxphy_lvds_power_off(ser, combrxphy, dev_id, id);
break;
case COMBRX_PHY_MODE_LVDS_CAMERA:
rkx110_combrxphy_lvds_camera_power_off(ser, combrxphy, dev_id, id);
break;
default:
break;
}
}
void rkx110_combrxphy_set_rate(struct rkx110_combrxphy *combrxphy, u64 rate)
{
combrxphy->rate = rate;
}
void rkx110_combrxphy_set_lanes(struct rkx110_combrxphy *combrxphy, uint8_t lanes)
{
combrxphy->lanes = lanes;
}
void rkx110_combrxphy_set_mode(struct rkx110_combrxphy *combrxphy, enum combrx_phy_mode mode)
{
combrxphy->mode = mode;
}