293 lines
9.2 KiB
C
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;
|
|
}
|