mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
phy: rockchip: csi2-dphy: rk3588 dcphy tx/rx combine to one driver
Signed-off-by: Zefa Chen <zefa.chen@rock-chips.com> Change-Id: I5c28feef615dd4a6b8d7e8ec0514c32625da567b
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
#ifndef _PHY_ROCKCHIP_CSI2_DPHY_COMMON_H_
|
||||
#define _PHY_ROCKCHIP_CSI2_DPHY_COMMON_H_
|
||||
|
||||
#include <linux/rk-camera-module.h>
|
||||
|
||||
#define PHY_MAX 16
|
||||
#define MAX_DEV_NAME_LEN 32
|
||||
|
||||
@@ -52,15 +54,18 @@ struct csi2_sensor {
|
||||
};
|
||||
|
||||
struct csi2_dphy_hw;
|
||||
struct samsung_mipi_dcphy;
|
||||
|
||||
struct dphy_drv_data {
|
||||
const char dev_name[MAX_DEV_NAME_LEN];
|
||||
enum csi2_dphy_vendor vendor;
|
||||
};
|
||||
|
||||
struct csi2_dphy {
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
struct csi2_dphy_hw *dphy_hw;
|
||||
struct samsung_mipi_dcphy *samsung_phy;
|
||||
struct v4l2_async_notifier notifier;
|
||||
struct v4l2_subdev sd;
|
||||
struct mutex mutex; /* lock for updating protection */
|
||||
@@ -72,6 +77,7 @@ struct csi2_dphy {
|
||||
bool is_streaming;
|
||||
enum csi2_dphy_lane_mode lane_mode;
|
||||
const struct dphy_drv_data *drv_data;
|
||||
struct rkmodule_csi_dphy_param *dphy_param;
|
||||
};
|
||||
|
||||
struct dphy_hw_drv_data {
|
||||
|
||||
@@ -41,9 +41,6 @@
|
||||
#define GRF_CSI2PHY_SEL_SPLIT_0_1 (0x0)
|
||||
#define GRF_CSI2PHY_SEL_SPLIT_2_3 BIT(0)
|
||||
|
||||
/*RK3588 DCPHY GRF REG OFFSET */
|
||||
#define GRF_DCPHY_CON0 (0x0)
|
||||
|
||||
/* PHY REG OFFSET */
|
||||
#define CSI2_DPHY_CTRL_INVALID_OFFSET (0xffff)
|
||||
#define CSI2_DPHY_CTRL_PWRCTL \
|
||||
@@ -71,27 +68,6 @@
|
||||
#define CSI2_DPHY_PATH1_MODE_SEL (0x84C)
|
||||
#define CSI2_DPHY_PATH1_LVDS_MODE_SEL (0x880)
|
||||
|
||||
#define CSI2_DCPHY_CLK_WR_THS_SETTLE (0x030)
|
||||
#define CSI2_DCPHY_LANE0_WR_THS_SETTLE (0x130)
|
||||
#define CSI2_DCPHY_LANE0_WR_ERR_SOT_SYNC (0x134)
|
||||
#define CSI2_DCPHY_LANE1_WR_THS_SETTLE (0x230)
|
||||
#define CSI2_DCPHY_LANE1_WR_ERR_SOT_SYNC (0x234)
|
||||
#define CSI2_DCPHY_LANE2_WR_THS_SETTLE (0x330)
|
||||
#define CSI2_DCPHY_LANE2_WR_ERR_SOT_SYNC (0x334)
|
||||
#define CSI2_DCPHY_LANE3_WR_THS_SETTLE (0x430)
|
||||
#define CSI2_DCPHY_LANE3_WR_ERR_SOT_SYNC (0x434)
|
||||
#define CSI2_DCPHY_CLK_LANE_ENABLE (0x000)
|
||||
#define CSI2_DCPHY_DATA_LANE0_ENABLE (0x100)
|
||||
#define CSI2_DCPHY_DATA_LANE1_ENABLE (0x200)
|
||||
#define CSI2_DCPHY_DATA_LANE2_ENABLE (0x300)
|
||||
#define CSI2_DCPHY_DATA_LANE3_ENABLE (0x400)
|
||||
|
||||
#define CSI2_DCPHY_S0C_GNR_CON1 (0x004)
|
||||
#define CSI2_DCPHY_COMBO_S0D0_GNR_CON1 (0x104)
|
||||
#define CSI2_DCPHY_COMBO_S0D1_GNR_CON1 (0x204)
|
||||
#define CSI2_DCPHY_COMBO_S0D2_GNR_CON1 (0x304)
|
||||
#define CSI2_DCPHY_S0D3_GNR_CON1 (0x304)
|
||||
|
||||
/* PHY REG BIT DEFINE */
|
||||
#define CSI2_DPHY_LANE_MODE_FULL (0x4)
|
||||
#define CSI2_DPHY_LANE_MODE_SPLIT (0x2)
|
||||
@@ -410,32 +386,6 @@ static const struct csi2dphy_reg rk3588_csi2dphy_regs[] = {
|
||||
[CSI2PHY_CLK1_LANE_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_LANE_EN),
|
||||
};
|
||||
|
||||
static const struct grf_reg rk3588_grf_dcphy_regs[] = {
|
||||
[GRF_CPHY_MODE] = GRF_REG(GRF_DCPHY_CON0, 9, 0),
|
||||
};
|
||||
|
||||
static const struct csi2dphy_reg rk3588_csi2dcphy_regs[] = {
|
||||
[CSI2PHY_CLK_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_CLK_WR_THS_SETTLE),
|
||||
[CSI2PHY_LANE0_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE0_WR_THS_SETTLE),
|
||||
[CSI2PHY_LANE0_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE0_WR_ERR_SOT_SYNC),
|
||||
[CSI2PHY_LANE1_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE1_WR_THS_SETTLE),
|
||||
[CSI2PHY_LANE1_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE1_WR_ERR_SOT_SYNC),
|
||||
[CSI2PHY_LANE2_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE2_WR_THS_SETTLE),
|
||||
[CSI2PHY_LANE2_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE2_WR_ERR_SOT_SYNC),
|
||||
[CSI2PHY_LANE3_THS_SETTLE] = CSI2PHY_REG(CSI2_DCPHY_LANE3_WR_THS_SETTLE),
|
||||
[CSI2PHY_LANE3_ERR_SOT_SYNC] = CSI2PHY_REG(CSI2_DCPHY_LANE3_WR_ERR_SOT_SYNC),
|
||||
[CSI2PHY_CLK_LANE_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_CLK_LANE_ENABLE),
|
||||
[CSI2PHY_DATA_LANE0_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE0_ENABLE),
|
||||
[CSI2PHY_DATA_LANE1_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE1_ENABLE),
|
||||
[CSI2PHY_DATA_LANE2_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE2_ENABLE),
|
||||
[CSI2PHY_DATA_LANE3_ENABLE] = CSI2PHY_REG(CSI2_DCPHY_DATA_LANE3_ENABLE),
|
||||
[CSI2PHY_S0C_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0C_GNR_CON1),
|
||||
[CSI2PHY_COMBO_S0D0_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D0_GNR_CON1),
|
||||
[CSI2PHY_COMBO_S0D1_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D1_GNR_CON1),
|
||||
[CSI2PHY_COMBO_S0D2_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_COMBO_S0D2_GNR_CON1),
|
||||
[CSI2PHY_S0D3_GNR_CON1] = CSI2PHY_REG(CSI2_DCPHY_S0D3_GNR_CON1),
|
||||
};
|
||||
|
||||
static const struct grf_reg rv1106_grf_dphy_regs[] = {
|
||||
[GRF_DPHY_CSI2PHY_FORCERXMODE] = GRF_REG(GRF_VI_CSIPHY_CON5, 4, 0),
|
||||
[GRF_DPHY_CSI2PHY_CLKLANE_EN] = GRF_REG(GRF_VI_CSIPHY_CON5, 1, 8),
|
||||
@@ -480,37 +430,6 @@ static const struct hsfreq_range rk3568_csi2_dphy_hw_hsfreq_ranges[] = {
|
||||
{2199, 0x3c}, {2399, 0x41}, {2499, 0x46}
|
||||
};
|
||||
|
||||
/* These tables must be sorted by .range_h ascending. */
|
||||
static const struct hsfreq_range rk3588_csi2_dcphy_d_hw_hsfreq_ranges[] = {
|
||||
{ 80, 0x105}, { 100, 0x106}, { 120, 0x107}, { 140, 0x108},
|
||||
{ 160, 0x109}, { 180, 0x10a}, { 200, 0x10b}, { 220, 0x10c},
|
||||
{ 240, 0x10d}, { 270, 0x10e}, { 290, 0x10f}, { 310, 0x110},
|
||||
{ 330, 0x111}, { 350, 0x112}, { 370, 0x113}, { 390, 0x114},
|
||||
{ 410, 0x115}, { 430, 0x116}, { 450, 0x117}, { 470, 0x118},
|
||||
{ 490, 0x119}, { 510, 0x11a}, { 540, 0x11b}, { 560, 0x11c},
|
||||
{ 580, 0x11d}, { 600, 0x11e}, { 620, 0x11f}, { 640, 0x120},
|
||||
{ 660, 0x121}, { 680, 0x122}, { 700, 0x123}, { 720, 0x124},
|
||||
{ 740, 0x125}, { 760, 0x126}, { 790, 0x127}, { 810, 0x128},
|
||||
{ 830, 0x129}, { 850, 0x12a}, { 870, 0x12b}, { 890, 0x12c},
|
||||
{ 910, 0x12d}, { 930, 0x12e}, { 950, 0x12f}, { 970, 0x130},
|
||||
{ 990, 0x131}, {1010, 0x132}, {1030, 0x133}, {1060, 0x134},
|
||||
{1080, 0x135}, {1100, 0x136}, {1120, 0x137}, {1140, 0x138},
|
||||
{1160, 0x139}, {1180, 0x13a}, {1200, 0x13b}, {1220, 0x13c},
|
||||
{1240, 0x13d}, {1260, 0x13e}, {1280, 0x13f}, {1310, 0x140},
|
||||
{1330, 0x141}, {1350, 0x142}, {1370, 0x143}, {1390, 0x144},
|
||||
{1410, 0x145}, {1430, 0x146}, {1450, 0x147}, {1470, 0x148},
|
||||
{1490, 0x149}, {1580, 0x007}, {1740, 0x008}, {1910, 0x009},
|
||||
{2070, 0x00a}, {2240, 0x00b}, {2410, 0x00c}, {2570, 0x00d},
|
||||
{2740, 0x00e}, {2910, 0x00f}, {3070, 0x010}, {3240, 0x011},
|
||||
{3410, 0x012}, {3570, 0x013}, {3740, 0x014}, {3890, 0x015},
|
||||
{4070, 0x016}, {4240, 0x017}, {4400, 0x018}, {4500, 0x019},
|
||||
};
|
||||
|
||||
/* These tables must be sorted by .range_h ascending. */
|
||||
static const struct hsfreq_range rk3588_csi2_dcphy_c_hw_hsfreq_ranges[] = {
|
||||
{ 500, 0x102}, { 990, 0x002}, { 2500, 0x001},
|
||||
};
|
||||
|
||||
static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct media_pad *local, *remote;
|
||||
@@ -863,167 +782,6 @@ static int csi2_dphy_hw_stream_off(struct csi2_dphy *dphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi_dcphy_wait_lane_prepare(struct csi2_dphy_hw *hw, int index)
|
||||
{
|
||||
int count = 0;
|
||||
u32 val = 0;
|
||||
|
||||
read_csi2_dphy_reg(hw, index, &val);
|
||||
while (!(val & BIT(1))) {
|
||||
usleep_range(10, 20);
|
||||
read_csi2_dphy_reg(hw, index, &val);
|
||||
count++;
|
||||
if (count > 2000)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_dcphy_hw_stream_on(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
const struct dphy_hw_drv_data *drv_data = hw->drv_data;
|
||||
const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
|
||||
int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
|
||||
int i, hsfreq = 0;
|
||||
u32 sot_sync = 0;
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) {
|
||||
hsfreq_ranges = drv_data->hsfreq_ranges;
|
||||
num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
|
||||
sot_sync = 0x03;
|
||||
} else if (sensor->mbus.type == V4L2_MBUS_CSI2_CPHY) {
|
||||
hsfreq_ranges = drv_data->hsfreq_ranges_cphy;
|
||||
num_hsfreq_ranges = drv_data->num_hsfreq_ranges_cphy;
|
||||
sot_sync = 0x32;
|
||||
}
|
||||
|
||||
mutex_lock(&hw->mutex);
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_CPHY)
|
||||
write_grf_reg(hw, GRF_CPHY_MODE, 0x9);
|
||||
|
||||
if (hw->rsts_bulk)
|
||||
reset_control_assert(hw->rsts_bulk);
|
||||
|
||||
/*clk settle fix to 0x301*/
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) {
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_CLK_THS_SETTLE, 0x301);
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_S0C_GNR_CON1, 0x1450);
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_COMBO_S0D0_GNR_CON1, 0x1450);
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_COMBO_S0D1_GNR_CON1, 0x1450);
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_COMBO_S0D2_GNR_CON1, 0x1450);
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_S0D3_GNR_CON1, 0x1450);
|
||||
}
|
||||
/* set data lane */
|
||||
for (i = 0; i < num_hsfreq_ranges; i++) {
|
||||
if (hsfreq_ranges[i].range_h >= dphy->data_rate_mbps) {
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == num_hsfreq_ranges) {
|
||||
i = num_hsfreq_ranges - 1;
|
||||
dev_warn(dphy->dev, "data rate: %lld mbps, max support %d mbps",
|
||||
dphy->data_rate_mbps, hsfreq_ranges[i].range_h + 1);
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
}
|
||||
if (sensor->lanes > 0x00) {
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE0_THS_SETTLE, hsfreq, 0x1ff);
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE0_ERR_SOT_SYNC, sot_sync, 0xff);
|
||||
}
|
||||
if (sensor->lanes > 0x01) {
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE1_THS_SETTLE, hsfreq, 0x1ff);
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE1_ERR_SOT_SYNC, sot_sync, 0xff);
|
||||
}
|
||||
if (sensor->lanes > 0x02) {
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE2_THS_SETTLE, hsfreq, 0x1ff);
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE2_ERR_SOT_SYNC, sot_sync, 0xff);
|
||||
}
|
||||
if (sensor->lanes > 0x03) {
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE3_THS_SETTLE, hsfreq, 0x1ff);
|
||||
write_csi2_dphy_reg_mask(hw, CSI2PHY_LANE3_ERR_SOT_SYNC, sot_sync, 0xff);
|
||||
}
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_CLK_LANE_ENABLE, BIT(0));
|
||||
|
||||
if (sensor->lanes > 0x00)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE0_ENABLE, BIT(0));
|
||||
if (sensor->lanes > 0x01)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE1_ENABLE, BIT(0));
|
||||
if (sensor->lanes > 0x02)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE2_ENABLE, BIT(0));
|
||||
if (sensor->lanes > 0x03)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE3_ENABLE, BIT(0));
|
||||
|
||||
/*wait for clk lane ready*/
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
if (csi_dcphy_wait_lane_prepare(hw, CSI2PHY_CLK_LANE_ENABLE))
|
||||
goto out_streamon;
|
||||
|
||||
/*wait for data lane ready*/
|
||||
if (sensor->lanes > 0x00)
|
||||
if (csi_dcphy_wait_lane_prepare(hw, CSI2PHY_DATA_LANE0_ENABLE))
|
||||
goto out_streamon;
|
||||
if (sensor->lanes > 0x01)
|
||||
if (csi_dcphy_wait_lane_prepare(hw, CSI2PHY_DATA_LANE1_ENABLE))
|
||||
goto out_streamon;
|
||||
if (sensor->lanes > 0x02)
|
||||
if (csi_dcphy_wait_lane_prepare(hw, CSI2PHY_DATA_LANE2_ENABLE))
|
||||
goto out_streamon;
|
||||
if (sensor->lanes > 0x03)
|
||||
if (csi_dcphy_wait_lane_prepare(hw, CSI2PHY_DATA_LANE3_ENABLE))
|
||||
goto out_streamon;
|
||||
|
||||
if (hw->rsts_bulk)
|
||||
reset_control_deassert(hw->rsts_bulk);
|
||||
atomic_inc(&hw->stream_cnt);
|
||||
|
||||
mutex_unlock(&hw->mutex);
|
||||
|
||||
return 0;
|
||||
out_streamon:
|
||||
if (hw->rsts_bulk)
|
||||
reset_control_deassert(hw->rsts_bulk);
|
||||
mutex_unlock(&hw->mutex);
|
||||
dev_err(dphy->dev, "stream on error\n");
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
static int csi2_dcphy_hw_stream_off(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
|
||||
if (atomic_dec_return(&hw->stream_cnt))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&hw->mutex);
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_CLK_LANE_ENABLE, 0);
|
||||
if (sensor->lanes > 0x00)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE0_ENABLE, 0);
|
||||
if (sensor->lanes > 0x01)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE1_ENABLE, 0);
|
||||
if (sensor->lanes > 0x02)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE2_ENABLE, 0);
|
||||
if (sensor->lanes > 0x03)
|
||||
write_csi2_dphy_reg(hw, CSI2PHY_DATA_LANE3_ENABLE, 0);
|
||||
|
||||
usleep_range(500, 1000);
|
||||
|
||||
mutex_unlock(&hw->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rk3568_csi2_dphy_hw_individual_init(struct csi2_dphy_hw *hw)
|
||||
{
|
||||
hw->grf_regs = rk3568_grf_dphy_regs;
|
||||
@@ -1034,11 +792,6 @@ static void rk3588_csi2_dphy_hw_individual_init(struct csi2_dphy_hw *hw)
|
||||
hw->grf_regs = rk3588_grf_dphy_regs;
|
||||
}
|
||||
|
||||
static void rk3588_csi2_dcphy_hw_individual_init(struct csi2_dphy_hw *hw)
|
||||
{
|
||||
hw->grf_regs = rk3588_grf_dcphy_regs;
|
||||
}
|
||||
|
||||
static void rv1106_csi2_dphy_hw_individual_init(struct csi2_dphy_hw *hw)
|
||||
{
|
||||
hw->grf_regs = rv1106_grf_dphy_regs;
|
||||
@@ -1066,19 +819,6 @@ static const struct dphy_hw_drv_data rk3588_csi2_dphy_hw_drv_data = {
|
||||
.stream_off = csi2_dphy_hw_stream_off,
|
||||
};
|
||||
|
||||
static const struct dphy_hw_drv_data rk3588_csi2_dcphy_hw_drv_data = {
|
||||
.hsfreq_ranges = rk3588_csi2_dcphy_d_hw_hsfreq_ranges,
|
||||
.num_hsfreq_ranges = ARRAY_SIZE(rk3588_csi2_dcphy_d_hw_hsfreq_ranges),
|
||||
.hsfreq_ranges_cphy = rk3588_csi2_dcphy_c_hw_hsfreq_ranges,
|
||||
.num_hsfreq_ranges_cphy = ARRAY_SIZE(rk3588_csi2_dcphy_c_hw_hsfreq_ranges),
|
||||
.csi2dphy_regs = rk3588_csi2dcphy_regs,
|
||||
.grf_regs = rk3588_grf_dcphy_regs,
|
||||
.individual_init = rk3588_csi2_dcphy_hw_individual_init,
|
||||
.chip_id = CHIP_ID_RK3588_DCPHY,
|
||||
.stream_on = csi2_dcphy_hw_stream_on,
|
||||
.stream_off = csi2_dcphy_hw_stream_off,
|
||||
};
|
||||
|
||||
static const struct dphy_hw_drv_data rv1106_csi2_dphy_hw_drv_data = {
|
||||
.hsfreq_ranges = rk3568_csi2_dphy_hw_hsfreq_ranges,
|
||||
.num_hsfreq_ranges = ARRAY_SIZE(rk3568_csi2_dphy_hw_hsfreq_ranges),
|
||||
@@ -1099,10 +839,6 @@ static const struct of_device_id rockchip_csi2_dphy_hw_match_id[] = {
|
||||
.compatible = "rockchip,rk3588-csi2-dphy-hw",
|
||||
.data = &rk3588_csi2_dphy_hw_drv_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3588-csi2-dcphy-hw",
|
||||
.data = &rk3588_csi2_dcphy_hw_drv_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rv1106-csi2-dphy-hw",
|
||||
.data = &rv1106_csi2_dphy_hw_drv_data,
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include "phy-rockchip-csi2-dphy-common.h"
|
||||
#include <linux/rk-camera-module.h>
|
||||
#include "phy-rockchip-samsung-dcphy.h"
|
||||
|
||||
struct sensor_async_subdev {
|
||||
struct v4l2_async_subdev asd;
|
||||
@@ -127,44 +128,58 @@ static int csi2_dphy_update_sensor_mbus(struct v4l2_subdev *sd)
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = v4l2_subdev_call(sensor_sd, core, ioctl, RKMODULE_GET_BUS_CONFIG, &bus_config);
|
||||
if (!ret) {
|
||||
dev_info(dphy->dev, "phy_mode %d,lane %d\n",
|
||||
bus_config.bus.phy_mode, bus_config.bus.lanes);
|
||||
if (bus_config.bus.phy_mode == PHY_FULL_MODE) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588 && dphy->phy_index % 3 == 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
dphy->lane_mode = LANE_MODE_FULL;
|
||||
} else if (bus_config.bus.phy_mode == PHY_SPLIT_01) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
|
||||
dev_err(dphy->dev, "%s The chip not support split mode\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else if (dphy->phy_index % 3 == 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dphy->lane_mode = LANE_MODE_SPLIT;
|
||||
}
|
||||
} else if (bus_config.bus.phy_mode == PHY_SPLIT_23) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
|
||||
dev_err(dphy->dev, "%s The chip not support split mode\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else if (dphy->phy_index % 3 != 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d not support PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dphy->lane_mode = LANE_MODE_SPLIT;
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_INNO) {
|
||||
ret = v4l2_subdev_call(sensor_sd, core, ioctl,
|
||||
RKMODULE_GET_BUS_CONFIG, &bus_config);
|
||||
if (!ret) {
|
||||
dev_info(dphy->dev, "phy_mode %d,lane %d\n",
|
||||
bus_config.bus.phy_mode, bus_config.bus.lanes);
|
||||
if (bus_config.bus.phy_mode == PHY_FULL_MODE) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588 &&
|
||||
dphy->phy_index % 3 == 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
dphy->lane_mode = LANE_MODE_FULL;
|
||||
} else if (bus_config.bus.phy_mode == PHY_SPLIT_01) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
|
||||
dev_err(dphy->dev, "%s The chip not support split mode\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else if (dphy->phy_index % 3 == 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dphy->lane_mode = LANE_MODE_SPLIT;
|
||||
}
|
||||
} else if (bus_config.bus.phy_mode == PHY_SPLIT_23) {
|
||||
if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
|
||||
dev_err(dphy->dev, "%s The chip not support split mode\n",
|
||||
__func__);
|
||||
ret = -EINVAL;
|
||||
} else if (dphy->phy_index % 3 != 2) {
|
||||
dev_err(dphy->dev, "%s dphy%d not support PHY_SPLIT_23\n",
|
||||
__func__, dphy->phy_index);
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
dphy->lane_mode = LANE_MODE_SPLIT;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
ret = v4l2_subdev_call(sensor_sd, core, ioctl,
|
||||
RKMODULE_GET_CSI_DPHY_PARAM,
|
||||
dphy->dphy_param);
|
||||
if (ret) {
|
||||
dev_err(dphy->dev, "%s fail to get dphy param, used default value\n",
|
||||
__func__);
|
||||
ret = 0;
|
||||
}
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -173,6 +188,7 @@ static int csi2_dphy_s_stream_start(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct csi2_dphy *dphy = to_csi2_dphy(sd);
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
|
||||
int ret = 0;
|
||||
|
||||
if (dphy->is_streaming)
|
||||
@@ -184,8 +200,13 @@ static int csi2_dphy_s_stream_start(struct v4l2_subdev *sd)
|
||||
|
||||
csi2_dphy_update_sensor_mbus(sd);
|
||||
|
||||
if (hw->stream_on)
|
||||
hw->stream_on(dphy, sd);
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
if (samsung_phy && samsung_phy->stream_on)
|
||||
samsung_phy->stream_on(dphy, sd);
|
||||
} else {
|
||||
if (hw->stream_on)
|
||||
hw->stream_on(dphy, sd);
|
||||
}
|
||||
|
||||
dphy->is_streaming = true;
|
||||
|
||||
@@ -196,12 +217,18 @@ static int csi2_dphy_s_stream_stop(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct csi2_dphy *dphy = to_csi2_dphy(sd);
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
|
||||
|
||||
if (!dphy->is_streaming)
|
||||
return 0;
|
||||
|
||||
if (hw->stream_off)
|
||||
hw->stream_off(dphy, sd);
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
if (samsung_phy && samsung_phy->stream_off)
|
||||
samsung_phy->stream_off(dphy, sd);
|
||||
} else {
|
||||
if (hw->stream_off)
|
||||
hw->stream_off(dphy, sd);
|
||||
}
|
||||
|
||||
dphy->is_streaming = false;
|
||||
|
||||
@@ -273,9 +300,15 @@ static __maybe_unused int csi2_dphy_runtime_suspend(struct device *dev)
|
||||
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
|
||||
struct csi2_dphy *dphy = to_csi2_dphy(sd);
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
|
||||
|
||||
if (hw)
|
||||
clk_bulk_disable_unprepare(hw->num_clks, hw->clks_bulk);
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
if (samsung_phy)
|
||||
clk_disable_unprepare(samsung_phy->pclk);
|
||||
} else {
|
||||
if (hw)
|
||||
clk_bulk_disable_unprepare(hw->num_clks, hw->clks_bulk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -286,13 +319,19 @@ static __maybe_unused int csi2_dphy_runtime_resume(struct device *dev)
|
||||
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
|
||||
struct csi2_dphy *dphy = to_csi2_dphy(sd);
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
|
||||
int ret;
|
||||
|
||||
if (hw) {
|
||||
ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks_bulk);
|
||||
if (ret) {
|
||||
dev_err(hw->dev, "failed to enable clks\n");
|
||||
return ret;
|
||||
if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
if (samsung_phy)
|
||||
clk_prepare_enable(samsung_phy->pclk);
|
||||
} else {
|
||||
if (hw) {
|
||||
ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks_bulk);
|
||||
if (ret) {
|
||||
dev_err(hw->dev, "failed to enable clks\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,6 +542,47 @@ static int rockchip_csi2dphy_media_init(struct csi2_dphy *dphy)
|
||||
return v4l2_async_register_subdev(&dphy->sd);
|
||||
}
|
||||
|
||||
static int rockchip_csi2_dphy_attach_samsung_phy(struct csi2_dphy *dphy)
|
||||
{
|
||||
struct device *dev = dphy->dev;
|
||||
struct phy *dcphy;
|
||||
struct samsung_mipi_dcphy *dphy_hw;
|
||||
int ret = 0;
|
||||
|
||||
dcphy = devm_phy_optional_get(dev, "dcphy");
|
||||
if (IS_ERR(dcphy)) {
|
||||
ret = PTR_ERR(dcphy);
|
||||
dev_err(dphy->dev, "failed to get mipi dcphy: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dphy_hw = phy_get_drvdata(dcphy);
|
||||
dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
|
||||
dphy_hw->dphy_dev_num++;
|
||||
dphy->samsung_phy = dphy_hw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_csi2_dphy_detach_samsung_phy(struct csi2_dphy *dphy)
|
||||
{
|
||||
struct samsung_mipi_dcphy *dphy_hw = dphy->samsung_phy;
|
||||
struct csi2_dphy *csi2_dphy = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
|
||||
csi2_dphy = dphy_hw->dphy_dev[i];
|
||||
if (csi2_dphy &&
|
||||
csi2_dphy->phy_index == dphy->phy_index) {
|
||||
dphy_hw->dphy_dev[i] = NULL;
|
||||
dphy_hw->dphy_dev_num--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy)
|
||||
{
|
||||
struct platform_device *plat_dev;
|
||||
@@ -591,14 +671,28 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy)
|
||||
|
||||
static struct dphy_drv_data rk3568_dphy_drv_data = {
|
||||
.dev_name = "csi2dphy",
|
||||
.vendor = PHY_VENDOR_INNO,
|
||||
};
|
||||
|
||||
static struct dphy_drv_data rk3588_dcphy_drv_data = {
|
||||
.dev_name = "csi2dcphy",
|
||||
.vendor = PHY_VENDOR_SAMSUNG,
|
||||
};
|
||||
|
||||
static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
|
||||
.vendor = PHY_VENDOR_SAMSUNG,
|
||||
.lp_vol_ref = 3,
|
||||
.lp_hys_sw = {3, 0, 0, 0},
|
||||
.lp_escclk_pol_sel = {1, 0, 0, 0},
|
||||
.skew_data_cal_clk = {0, 3, 3, 3},
|
||||
.clk_hs_term_sel = 2,
|
||||
.data_hs_term_sel = {2, 2, 2, 2},
|
||||
.reserved = {0},
|
||||
};
|
||||
|
||||
static struct dphy_drv_data rv1106_dphy_drv_data = {
|
||||
.dev_name = "csi2dphy",
|
||||
.vendor = PHY_VENDOR_INNO,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_csi2_dphy_match_id[] = {
|
||||
@@ -640,7 +734,12 @@ static int rockchip_csi2_dphy_probe(struct platform_device *pdev)
|
||||
csi2dphy->phy_index = of_alias_get_id(dev->of_node, drv_data->dev_name);
|
||||
if (csi2dphy->phy_index < 0 || csi2dphy->phy_index >= PHY_MAX)
|
||||
csi2dphy->phy_index = 0;
|
||||
ret = rockchip_csi2_dphy_attach_hw(csi2dphy);
|
||||
if (csi2dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
|
||||
ret = rockchip_csi2_dphy_attach_samsung_phy(csi2dphy);
|
||||
csi2dphy->dphy_param = &rk3588_dcphy_param;
|
||||
} else {
|
||||
ret = rockchip_csi2_dphy_attach_hw(csi2dphy);
|
||||
}
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"csi2 dphy hw can't be attached, register dphy%d failed!\n",
|
||||
@@ -670,7 +769,10 @@ static int rockchip_csi2_dphy_probe(struct platform_device *pdev)
|
||||
|
||||
detach_hw:
|
||||
mutex_destroy(&csi2dphy->mutex);
|
||||
rockchip_csi2_dphy_detach_hw(csi2dphy);
|
||||
if (csi2dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG)
|
||||
rockchip_csi2_dphy_detach_samsung_phy(csi2dphy);
|
||||
else
|
||||
rockchip_csi2_dphy_detach_hw(csi2dphy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include "phy-rockchip-csi2-dphy-common.h"
|
||||
#include "phy-rockchip-samsung-dcphy.h"
|
||||
|
||||
#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
|
||||
#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
|
||||
@@ -150,6 +156,66 @@
|
||||
#define MAX_DPHY_BW 4500000L
|
||||
#define MAX_CPHY_BW 2000000L
|
||||
|
||||
#define RX_CLK_THS_SETTLE (0xb30)
|
||||
#define RX_LANE0_THS_SETTLE (0xC30)
|
||||
#define RX_LANE0_ERR_SOT_SYNC (0xC34)
|
||||
#define RX_LANE1_THS_SETTLE (0xD30)
|
||||
#define RX_LANE1_ERR_SOT_SYNC (0xD34)
|
||||
#define RX_LANE2_THS_SETTLE (0xE30)
|
||||
#define RX_LANE2_ERR_SOT_SYNC (0xE34)
|
||||
#define RX_LANE3_THS_SETTLE (0xF30)
|
||||
#define RX_LANE3_ERR_SOT_SYNC (0xF34)
|
||||
#define RX_CLK_LANE_ENABLE (0xB00)
|
||||
#define RX_DATA_LANE0_ENABLE (0xC00)
|
||||
#define RX_DATA_LANE1_ENABLE (0xD00)
|
||||
#define RX_DATA_LANE2_ENABLE (0xE00)
|
||||
#define RX_DATA_LANE3_ENABLE (0xF00)
|
||||
|
||||
#define RX_S0C_GNR_CON1 (0xB04)
|
||||
#define RX_S0C_ANA_CON1 (0xB0c)
|
||||
#define RX_S0C_ANA_CON2 (0xB10)
|
||||
#define RX_S0C_ANA_CON3 (0xB14)
|
||||
#define RX_COMBO_S0D0_GNR_CON1 (0xC04)
|
||||
#define RX_COMBO_S0D0_ANA_CON1 (0xC0c)
|
||||
#define RX_COMBO_S0D0_ANA_CON2 (0xC10)
|
||||
#define RX_COMBO_S0D0_ANA_CON3 (0xC14)
|
||||
#define RX_COMBO_S0D0_ANA_CON6 (0xC20)
|
||||
#define RX_COMBO_S0D0_ANA_CON7 (0xC24)
|
||||
#define RX_COMBO_S0D0_DESKEW_CON0 (0xC40)
|
||||
#define RX_COMBO_S0D0_DESKEW_CON2 (0xC48)
|
||||
#define RX_COMBO_S0D0_DESKEW_CON4 (0xC50)
|
||||
#define RX_COMBO_S0D0_CRC_CON1 (0xC64)
|
||||
#define RX_COMBO_S0D0_CRC_CON2 (0xC68)
|
||||
#define RX_COMBO_S0D1_GNR_CON1 (0xD04)
|
||||
#define RX_COMBO_S0D1_ANA_CON1 (0xD0c)
|
||||
#define RX_COMBO_S0D1_ANA_CON2 (0xD10)
|
||||
#define RX_COMBO_S0D1_ANA_CON3 (0xD14)
|
||||
#define RX_COMBO_S0D1_ANA_CON6 (0xD20)
|
||||
#define RX_COMBO_S0D1_ANA_CON7 (0xD24)
|
||||
#define RX_COMBO_S0D1_DESKEW_CON0 (0xD40)
|
||||
#define RX_COMBO_S0D1_DESKEW_CON2 (0xD48)
|
||||
#define RX_COMBO_S0D1_DESKEW_CON4 (0xD50)
|
||||
#define RX_COMBO_S0D1_CRC_CON1 (0xD64)
|
||||
#define RX_COMBO_S0D1_CRC_CON2 (0xD68)
|
||||
#define RX_COMBO_S0D2_GNR_CON1 (0xE04)
|
||||
#define RX_COMBO_S0D2_ANA_CON1 (0xE0c)
|
||||
#define RX_COMBO_S0D2_ANA_CON2 (0xE10)
|
||||
#define RX_COMBO_S0D2_ANA_CON3 (0xE14)
|
||||
#define RX_COMBO_S0D2_ANA_CON6 (0xE20)
|
||||
#define RX_COMBO_S0D2_ANA_CON7 (0xE24)
|
||||
#define RX_COMBO_S0D2_DESKEW_CON0 (0xE40)
|
||||
#define RX_COMBO_S0D2_DESKEW_CON2 (0xE48)
|
||||
#define RX_COMBO_S0D2_DESKEW_CON4 (0xE50)
|
||||
#define RX_COMBO_S0D2_CRC_CON1 (0xE64)
|
||||
#define RX_COMBO_S0D2_CRC_CON2 (0xE68)
|
||||
#define RX_S0D3_GNR_CON1 (0xF04)
|
||||
#define RX_S0D3_ANA_CON1 (0xF0c)
|
||||
#define RX_S0D3_ANA_CON2 (0xF10)
|
||||
#define RX_S0D3_ANA_CON3 (0xF14)
|
||||
#define RX_S0D3_DESKEW_CON0 (0xF40)
|
||||
#define RX_S0D3_DESKEW_CON2 (0xF48)
|
||||
#define RX_S0D3_DESKEW_CON4 (0xF50)
|
||||
|
||||
struct samsung_mipi_dphy_timing {
|
||||
unsigned int max_lane_mbps;
|
||||
u8 clk_prepare;
|
||||
@@ -174,32 +240,6 @@ struct samsung_mipi_cphy_timing {
|
||||
u8 settle_3;
|
||||
};
|
||||
|
||||
struct samsung_mipi_dcphy {
|
||||
struct device *dev;
|
||||
struct clk *ref_clk;
|
||||
struct clk *pclk;
|
||||
struct regmap *regmap;
|
||||
struct regmap *grf_regmap;
|
||||
struct reset_control *phy_rst;
|
||||
struct reset_control *apb_rst;
|
||||
struct reset_control *grf_apb_rst;
|
||||
bool c_option;
|
||||
|
||||
unsigned int lanes;
|
||||
|
||||
struct {
|
||||
unsigned long long rate;
|
||||
u8 prediv;
|
||||
u16 fbdiv;
|
||||
long dsm;
|
||||
u8 scaler;
|
||||
|
||||
bool ssc_en;
|
||||
u8 mfr;
|
||||
u8 mrr;
|
||||
} pll;
|
||||
};
|
||||
|
||||
static const
|
||||
struct samsung_mipi_dphy_timing samsung_mipi_dphy_timing_table[] = {
|
||||
{6500, 32, 117, 31, 28, 30, 56, 27, 24, 44, 37},
|
||||
@@ -1194,11 +1234,58 @@ struct samsung_mipi_cphy_timing samsung_mipi_cphy_timing_table[] = {
|
||||
{ 80, 1, 50, 25, 2, 0, 2 },
|
||||
};
|
||||
|
||||
struct hsfreq_range {
|
||||
u32 range_h;
|
||||
u16 cfg_bit;
|
||||
};
|
||||
/* These tables must be sorted by .range_h ascending. */
|
||||
static const struct hsfreq_range samsung_dphy_rx_hsfreq_ranges[] = {
|
||||
{ 80, 0x105}, { 100, 0x106}, { 120, 0x107}, { 140, 0x108},
|
||||
{ 160, 0x109}, { 180, 0x10a}, { 200, 0x10b}, { 220, 0x10c},
|
||||
{ 240, 0x10d}, { 270, 0x10e}, { 290, 0x10f}, { 310, 0x110},
|
||||
{ 330, 0x111}, { 350, 0x112}, { 370, 0x113}, { 390, 0x114},
|
||||
{ 410, 0x115}, { 430, 0x116}, { 450, 0x117}, { 470, 0x118},
|
||||
{ 490, 0x119}, { 510, 0x11a}, { 540, 0x11b}, { 560, 0x11c},
|
||||
{ 580, 0x11d}, { 600, 0x11e}, { 620, 0x11f}, { 640, 0x120},
|
||||
{ 660, 0x121}, { 680, 0x122}, { 700, 0x123}, { 720, 0x124},
|
||||
{ 740, 0x125}, { 760, 0x126}, { 790, 0x127}, { 810, 0x128},
|
||||
{ 830, 0x129}, { 850, 0x12a}, { 870, 0x12b}, { 890, 0x12c},
|
||||
{ 910, 0x12d}, { 930, 0x12e}, { 950, 0x12f}, { 970, 0x130},
|
||||
{ 990, 0x131}, {1010, 0x132}, {1030, 0x133}, {1060, 0x134},
|
||||
{1080, 0x135}, {1100, 0x136}, {1120, 0x137}, {1140, 0x138},
|
||||
{1160, 0x139}, {1180, 0x13a}, {1200, 0x13b}, {1220, 0x13c},
|
||||
{1240, 0x13d}, {1260, 0x13e}, {1280, 0x13f}, {1310, 0x140},
|
||||
{1330, 0x141}, {1350, 0x142}, {1370, 0x143}, {1390, 0x144},
|
||||
{1410, 0x145}, {1430, 0x146}, {1450, 0x147}, {1470, 0x148},
|
||||
{1490, 0x149}, {1580, 0x007}, {1740, 0x008}, {1910, 0x009},
|
||||
{2070, 0x00a}, {2240, 0x00b}, {2410, 0x00c}, {2570, 0x00d},
|
||||
{2740, 0x00e}, {2910, 0x00f}, {3070, 0x010}, {3240, 0x011},
|
||||
{3410, 0x012}, {3570, 0x013}, {3740, 0x014}, {3890, 0x015},
|
||||
{4070, 0x016}, {4240, 0x017}, {4400, 0x018}, {4500, 0x019},
|
||||
};
|
||||
|
||||
/* These tables must be sorted by .range_h ascending. */
|
||||
static const struct hsfreq_range samsung_cphy_rx_hsfreq_ranges[] = {
|
||||
{ 500, 0x102}, { 990, 0x002}, { 2500, 0x001},
|
||||
};
|
||||
|
||||
static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung)
|
||||
{
|
||||
struct csi2_dphy *csi_dphy = samsung->dphy_dev[0];
|
||||
u32 bias_con2 = 0x3223;
|
||||
|
||||
if (csi_dphy &&
|
||||
csi_dphy->dphy_param->lp_vol_ref != 3 &&
|
||||
csi_dphy->dphy_param->lp_vol_ref < 0x7) {
|
||||
bias_con2 &= 0xfffffff8;
|
||||
bias_con2 |= csi_dphy->dphy_param->lp_vol_ref;
|
||||
dev_info(samsung->dev,
|
||||
"rx change lp_vol_ref to %d, it may cause tx exception\n",
|
||||
csi_dphy->dphy_param->lp_vol_ref);
|
||||
}
|
||||
regmap_write(samsung->regmap, BIAS_CON0, 0x0010);
|
||||
regmap_write(samsung->regmap, BIAS_CON1, 0x0110);
|
||||
regmap_write(samsung->regmap, BIAS_CON2, 0x3223);
|
||||
regmap_write(samsung->regmap, BIAS_CON2, bias_con2);
|
||||
|
||||
/* default output voltage select:
|
||||
* dphy: 400mv
|
||||
@@ -1612,7 +1699,7 @@ samsung_mipi_dcphy_hs_vreg_amp_configure(struct samsung_mipi_dcphy *samsung)
|
||||
|
||||
static void samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
|
||||
{
|
||||
reset_control_assert(samsung->phy_rst);
|
||||
reset_control_assert(samsung->m_phy_rst);
|
||||
|
||||
samsung_mipi_dcphy_bias_block_enable(samsung);
|
||||
samsung_mipi_dcphy_pll_configure(samsung);
|
||||
@@ -1621,7 +1708,7 @@ static void samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
|
||||
samsung_mipi_dcphy_pll_enable(samsung);
|
||||
samsung_mipi_dphy_lane_enable(samsung);
|
||||
|
||||
reset_control_deassert(samsung->phy_rst);
|
||||
reset_control_deassert(samsung->m_phy_rst);
|
||||
|
||||
/* The TSKEWCAL maximum is 100 µsec
|
||||
* at initial calibration.
|
||||
@@ -1632,7 +1719,7 @@ static void samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
|
||||
static void samsung_mipi_cphy_power_on(struct samsung_mipi_dcphy *samsung)
|
||||
{
|
||||
regmap_write(samsung->grf_regmap, MIPI_DCPHY_GRF_CON0, M_CPHY_MODE);
|
||||
reset_control_assert(samsung->phy_rst);
|
||||
reset_control_assert(samsung->m_phy_rst);
|
||||
|
||||
samsung_mipi_dcphy_bias_block_enable(samsung);
|
||||
samsung_mipi_dcphy_hs_vreg_amp_configure(samsung);
|
||||
@@ -1641,7 +1728,7 @@ static void samsung_mipi_cphy_power_on(struct samsung_mipi_dcphy *samsung)
|
||||
samsung_mipi_dcphy_pll_enable(samsung);
|
||||
samsung_mipi_cphy_lane_enable(samsung);
|
||||
|
||||
reset_control_deassert(samsung->phy_rst);
|
||||
reset_control_deassert(samsung->m_phy_rst);
|
||||
}
|
||||
|
||||
static int samsung_mipi_dcphy_power_on(struct phy *phy)
|
||||
@@ -1796,6 +1883,369 @@ static int samsung_mipi_dcphy_configure(struct phy *phy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct media_pad *local, *remote;
|
||||
struct media_entity *sensor_me;
|
||||
|
||||
local = &sd->entity.pads[CSI2_DPHY_RX_PAD_SINK];
|
||||
remote = media_entity_remote_pad(local);
|
||||
if (!remote) {
|
||||
v4l2_warn(sd, "No link between dphy and sensor\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sensor_me = media_entity_remote_pad(local)->entity;
|
||||
return media_entity_to_v4l2_subdev(sensor_me);
|
||||
}
|
||||
|
||||
static struct csi2_sensor *sd_to_sensor(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dphy->num_sensors; ++i)
|
||||
if (dphy->sensors[i].sd == sd)
|
||||
return &dphy->sensors[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void samsung_dcphy_rx_config_settle(struct csi2_dphy *dphy,
|
||||
struct csi2_sensor *sensor)
|
||||
{
|
||||
struct samsung_mipi_dcphy *samsung = dphy->samsung_phy;
|
||||
const struct hsfreq_range *hsfreq_ranges = NULL;
|
||||
int num_hsfreq_ranges = 0;
|
||||
int i, hsfreq = 0;
|
||||
u32 sot_sync = 0;
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) {
|
||||
hsfreq_ranges = samsung_dphy_rx_hsfreq_ranges;
|
||||
num_hsfreq_ranges = ARRAY_SIZE(samsung_dphy_rx_hsfreq_ranges);
|
||||
sot_sync = 0x03;
|
||||
} else if (sensor->mbus.type == V4L2_MBUS_CSI2_CPHY) {
|
||||
hsfreq_ranges = samsung_cphy_rx_hsfreq_ranges;
|
||||
num_hsfreq_ranges = ARRAY_SIZE(samsung_cphy_rx_hsfreq_ranges);
|
||||
sot_sync = 0x32;
|
||||
} else {
|
||||
dev_err(dphy->dev, "mbus type %d is not support",
|
||||
sensor->mbus.type);
|
||||
return;
|
||||
}
|
||||
/* set data lane */
|
||||
for (i = 0; i < num_hsfreq_ranges; i++) {
|
||||
if (hsfreq_ranges[i].range_h >= dphy->data_rate_mbps) {
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == num_hsfreq_ranges) {
|
||||
i = num_hsfreq_ranges - 1;
|
||||
dev_warn(dphy->dev, "data rate: %lld mbps, max support %d mbps",
|
||||
dphy->data_rate_mbps, hsfreq_ranges[i].range_h + 1);
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
}
|
||||
/*clk settle fix to 0x301*/
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
regmap_write(samsung->regmap, RX_CLK_THS_SETTLE, 0x301);
|
||||
|
||||
if (sensor->lanes > 0x00) {
|
||||
regmap_update_bits(samsung->regmap, RX_LANE0_THS_SETTLE, 0x1ff, hsfreq);
|
||||
regmap_update_bits(samsung->regmap, RX_LANE0_ERR_SOT_SYNC, 0xff, sot_sync);
|
||||
}
|
||||
if (sensor->lanes > 0x01) {
|
||||
regmap_update_bits(samsung->regmap, RX_LANE1_THS_SETTLE, 0x1ff, hsfreq);
|
||||
regmap_update_bits(samsung->regmap, RX_LANE1_ERR_SOT_SYNC, 0xff, sot_sync);
|
||||
}
|
||||
if (sensor->lanes > 0x02) {
|
||||
regmap_update_bits(samsung->regmap, RX_LANE2_THS_SETTLE, 0x1ff, hsfreq);
|
||||
regmap_update_bits(samsung->regmap, RX_LANE2_ERR_SOT_SYNC, 0xff, sot_sync);
|
||||
}
|
||||
if (sensor->lanes > 0x03) {
|
||||
regmap_update_bits(samsung->regmap, RX_LANE3_THS_SETTLE, 0x1ff, hsfreq);
|
||||
regmap_update_bits(samsung->regmap, RX_LANE3_ERR_SOT_SYNC, 0xff, sot_sync);
|
||||
}
|
||||
}
|
||||
|
||||
static int samsung_dcphy_rx_config_common(struct csi2_dphy *dphy,
|
||||
struct csi2_sensor *sensor)
|
||||
{
|
||||
struct samsung_mipi_dcphy *samsung = dphy->samsung_phy;
|
||||
u32 dlysel = 0;
|
||||
int i = 0;
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) {
|
||||
if (dphy->data_rate_mbps < 1500)
|
||||
dlysel = 0;
|
||||
else if (dphy->data_rate_mbps < 2000)
|
||||
dlysel = 3 << 8;
|
||||
else if (dphy->data_rate_mbps < 3000)
|
||||
dlysel = 2 << 8;
|
||||
else if (dphy->data_rate_mbps < 4000)
|
||||
dlysel = 1 << 8;
|
||||
else if (dphy->data_rate_mbps < 6500)
|
||||
dlysel = 0;
|
||||
if (dphy->dphy_param->clk_hs_term_sel > 0x7) {
|
||||
dev_err(dphy->dev, "clk_hs_term_sel error param %d\n",
|
||||
dphy->dphy_param->clk_hs_term_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < sensor->lanes; i++) {
|
||||
if (dphy->dphy_param->data_hs_term_sel[i] > 0x7) {
|
||||
dev_err(dphy->dev, "data_hs_term_sel[%d] error param %d\n",
|
||||
i,
|
||||
dphy->dphy_param->data_hs_term_sel[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dphy->dphy_param->lp_hys_sw[i] > 0x3) {
|
||||
dev_err(dphy->dev, "lp_hys_sw[%d] error param %d\n",
|
||||
i,
|
||||
dphy->dphy_param->lp_hys_sw[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dphy->dphy_param->lp_escclk_pol_sel[i] > 0x1) {
|
||||
dev_err(dphy->dev, "lp_escclk_pol_sel[%d] error param %d\n",
|
||||
i,
|
||||
dphy->dphy_param->lp_escclk_pol_sel[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dphy->dphy_param->skew_data_cal_clk[i] > 0x1f) {
|
||||
dev_err(dphy->dev, "skew_data_cal_clk[%d] error param %d\n",
|
||||
i,
|
||||
dphy->dphy_param->skew_data_cal_clk[i]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
regmap_write(samsung->regmap, RX_S0C_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_S0C_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_S0C_ANA_CON2, dphy->dphy_param->clk_hs_term_sel);
|
||||
regmap_write(samsung->regmap, RX_S0C_ANA_CON3, 0x0600);
|
||||
if (sensor->lanes > 0x00) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON2, dlysel |
|
||||
dphy->dphy_param->data_hs_term_sel[0]);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON3, 0x0600 |
|
||||
(dphy->dphy_param->lp_hys_sw[0] << 4) |
|
||||
(dphy->dphy_param->lp_escclk_pol_sel[0] << 11));
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_DESKEW_CON2,
|
||||
dphy->dphy_param->skew_data_cal_clk[0]);
|
||||
}
|
||||
if (sensor->lanes > 0x01) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON2, dlysel |
|
||||
dphy->dphy_param->data_hs_term_sel[1]);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON3, 0x0600 |
|
||||
(dphy->dphy_param->lp_hys_sw[1] << 4) |
|
||||
(dphy->dphy_param->lp_escclk_pol_sel[1] << 11));
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_DESKEW_CON2,
|
||||
dphy->dphy_param->skew_data_cal_clk[1]);
|
||||
}
|
||||
if (sensor->lanes > 0x02) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON2, dlysel |
|
||||
dphy->dphy_param->data_hs_term_sel[2]);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON3, 0x0600 |
|
||||
(dphy->dphy_param->lp_hys_sw[2] << 4) |
|
||||
(dphy->dphy_param->lp_escclk_pol_sel[2] << 11));
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_DESKEW_CON2,
|
||||
dphy->dphy_param->skew_data_cal_clk[2]);
|
||||
}
|
||||
if (sensor->lanes > 0x03) {
|
||||
regmap_write(samsung->regmap, RX_S0D3_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_S0D3_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_S0D3_ANA_CON2, dlysel |
|
||||
dphy->dphy_param->data_hs_term_sel[3]);
|
||||
regmap_write(samsung->regmap, RX_S0D3_ANA_CON3, 0x0600 |
|
||||
(dphy->dphy_param->lp_hys_sw[3] << 4) |
|
||||
(dphy->dphy_param->lp_escclk_pol_sel[3] << 11));
|
||||
regmap_write(samsung->regmap, RX_S0D3_DESKEW_CON2,
|
||||
dphy->dphy_param->skew_data_cal_clk[3]);
|
||||
}
|
||||
} else {
|
||||
if (sensor->lanes > 0x00) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON2, 0x5);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON3, 0x600);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON6, 0x608);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_CRC_CON1, 0x1500);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D0_CRC_CON2, 0x30);
|
||||
}
|
||||
if (sensor->lanes > 0x01) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON2, 0x5);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON3, 0x600);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON6, 0x608);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_CRC_CON1, 0x1500);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D1_CRC_CON2, 0x30);
|
||||
}
|
||||
if (sensor->lanes > 0x02) {
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_GNR_CON1, 0x1450);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON1, 0x8000);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON2, 0x5);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON3, 0x600);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON6, 0x608);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_ANA_CON7, 0x40);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_CRC_CON1, 0x1500);
|
||||
regmap_write(samsung->regmap, RX_COMBO_S0D2_CRC_CON2, 0x30);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int samsung_dcphy_rx_lane_enable(struct csi2_dphy *dphy,
|
||||
struct csi2_sensor *sensor)
|
||||
{
|
||||
struct samsung_mipi_dcphy *samsung = dphy->samsung_phy;
|
||||
u32 sts;
|
||||
int ret = 0;
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
regmap_update_bits(samsung->regmap, RX_CLK_LANE_ENABLE, PHY_ENABLE, PHY_ENABLE);
|
||||
|
||||
if (sensor->lanes > 0x00)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE0_ENABLE, PHY_ENABLE, PHY_ENABLE);
|
||||
if (sensor->lanes > 0x01)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE1_ENABLE, PHY_ENABLE, PHY_ENABLE);
|
||||
if (sensor->lanes > 0x02)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE2_ENABLE, PHY_ENABLE, PHY_ENABLE);
|
||||
if (sensor->lanes > 0x03)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE3_ENABLE, PHY_ENABLE, PHY_ENABLE);
|
||||
|
||||
/*wait for clk lane ready*/
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY) {
|
||||
ret = regmap_read_poll_timeout(samsung->regmap, RX_CLK_LANE_ENABLE,
|
||||
sts, (sts & PHY_READY), 200, 4000);
|
||||
if (ret < 0) {
|
||||
dev_err(samsung->dev, "phy rx clk lane is not locked\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*wait for data lane ready*/
|
||||
if (sensor->lanes > 0x00) {
|
||||
ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE0_ENABLE,
|
||||
sts, (sts & PHY_READY), 200, 2000);
|
||||
if (ret < 0) {
|
||||
dev_err(samsung->dev, "phy rx data lane 0 is not locked\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (sensor->lanes > 0x01) {
|
||||
ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE1_ENABLE,
|
||||
sts, (sts & PHY_READY), 200, 2000);
|
||||
if (ret < 0) {
|
||||
dev_err(samsung->dev, "phy rx data lane 1 is not locked\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
if (sensor->lanes > 0x02) {
|
||||
ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE2_ENABLE,
|
||||
sts, (sts & PHY_READY), 200, 2000);
|
||||
if (ret < 0) {
|
||||
dev_err(samsung->dev, "phy rx data lane 2 is not locked\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (sensor->lanes > 0x03) {
|
||||
ret = regmap_read_poll_timeout(samsung->regmap, RX_DATA_LANE3_ENABLE,
|
||||
sts, (sts & PHY_READY), 200, 2000);
|
||||
if (ret < 0) {
|
||||
dev_err(samsung->dev, "phy rx data lane 3 is not locked\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int samsung_dcphy_rx_stream_on(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
struct samsung_mipi_dcphy *samsung = dphy->samsung_phy;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&samsung->mutex);
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_CPHY)
|
||||
regmap_write(samsung->grf_regmap, MIPI_DCPHY_GRF_CON0, S_CPHY_MODE);
|
||||
|
||||
if (samsung->s_phy_rst)
|
||||
reset_control_assert(samsung->s_phy_rst);
|
||||
|
||||
samsung_mipi_dcphy_bias_block_enable(samsung);
|
||||
ret = samsung_dcphy_rx_config_common(dphy, sensor);
|
||||
if (ret)
|
||||
goto out_streamon;
|
||||
samsung_dcphy_rx_config_settle(dphy, sensor);
|
||||
|
||||
ret = samsung_dcphy_rx_lane_enable(dphy, sensor);
|
||||
if (ret)
|
||||
goto out_streamon;
|
||||
|
||||
if (samsung->s_phy_rst)
|
||||
reset_control_deassert(samsung->s_phy_rst);
|
||||
|
||||
atomic_inc(&samsung->stream_cnt);
|
||||
mutex_unlock(&samsung->mutex);
|
||||
|
||||
return 0;
|
||||
out_streamon:
|
||||
if (samsung->s_phy_rst)
|
||||
reset_control_deassert(samsung->s_phy_rst);
|
||||
mutex_unlock(&samsung->mutex);
|
||||
dev_err(dphy->dev, "stream on error\n");
|
||||
return -EINVAL;
|
||||
|
||||
}
|
||||
|
||||
static int samsung_dcphy_rx_stream_off(struct csi2_dphy *dphy,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct samsung_mipi_dcphy *samsung = dphy->samsung_phy;
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct csi2_sensor *sensor = sd_to_sensor(dphy, sensor_sd);
|
||||
|
||||
if (atomic_dec_return(&samsung->stream_cnt))
|
||||
return 0;
|
||||
|
||||
mutex_lock(&samsung->mutex);
|
||||
if (samsung->s_phy_rst)
|
||||
reset_control_assert(samsung->s_phy_rst);
|
||||
|
||||
if (sensor->mbus.type == V4L2_MBUS_CSI2_DPHY)
|
||||
regmap_update_bits(samsung->regmap, RX_CLK_LANE_ENABLE, PHY_ENABLE, 0);
|
||||
|
||||
if (sensor->lanes > 0x00)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE0_ENABLE, PHY_ENABLE, 0);
|
||||
if (sensor->lanes > 0x01)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE1_ENABLE, PHY_ENABLE, 0);
|
||||
if (sensor->lanes > 0x02)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE2_ENABLE, PHY_ENABLE, 0);
|
||||
if (sensor->lanes > 0x03)
|
||||
regmap_update_bits(samsung->regmap, RX_DATA_LANE3_ENABLE, PHY_ENABLE, 0);
|
||||
|
||||
if (samsung->s_phy_rst)
|
||||
reset_control_deassert(samsung->s_phy_rst);
|
||||
usleep_range(500, 1000);
|
||||
|
||||
mutex_unlock(&samsung->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int samsung_mipi_dcphy_init(struct phy *phy)
|
||||
{
|
||||
struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
|
||||
@@ -1824,11 +2274,11 @@ static const struct phy_ops samsung_mipi_dcphy_ops = {
|
||||
};
|
||||
|
||||
static const struct regmap_config samsung_mipi_dcphy_regmap_config = {
|
||||
.name = "dcphy_tx",
|
||||
.name = "dcphy",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x0b00,
|
||||
.max_register = 0x10000,
|
||||
};
|
||||
|
||||
static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
|
||||
@@ -1880,10 +2330,16 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(samsung->pclk);
|
||||
}
|
||||
|
||||
samsung->phy_rst = devm_reset_control_get(dev, "phy");
|
||||
if (IS_ERR(samsung->phy_rst)) {
|
||||
dev_err(dev, "failed to get system phy_rst control\n");
|
||||
return PTR_ERR(samsung->phy_rst);
|
||||
samsung->m_phy_rst = devm_reset_control_get(dev, "m_phy");
|
||||
if (IS_ERR(samsung->m_phy_rst)) {
|
||||
dev_err(dev, "failed to get system m_phy_rst control\n");
|
||||
return PTR_ERR(samsung->m_phy_rst);
|
||||
}
|
||||
|
||||
samsung->s_phy_rst = devm_reset_control_get(dev, "s_phy");
|
||||
if (IS_ERR(samsung->s_phy_rst)) {
|
||||
dev_err(dev, "failed to get system s_phy_rst control\n");
|
||||
return PTR_ERR(samsung->s_phy_rst);
|
||||
}
|
||||
|
||||
samsung->apb_rst = devm_reset_control_get(dev, "apb");
|
||||
@@ -1912,6 +2368,9 @@ static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(phy_provider);
|
||||
}
|
||||
|
||||
samsung->stream_on = samsung_dcphy_rx_stream_on;
|
||||
samsung->stream_off = samsung_dcphy_rx_stream_off;
|
||||
mutex_init(&samsung->mutex);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
@@ -1922,6 +2381,7 @@ static int samsung_mipi_dcphy_remove(struct platform_device *pdev)
|
||||
struct samsung_mipi_dcphy *samsung = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_disable(samsung->dev);
|
||||
mutex_destroy(&samsung->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1952,7 +2412,9 @@ static const struct dev_pm_ops samsung_mipi_dcphy_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct of_device_id samsung_mipi_dcphy_of_match[] = {
|
||||
{ .compatible = "rockchip,rk3588-mipi-dcphy", },
|
||||
{
|
||||
.compatible = "rockchip,rk3588-mipi-dcphy",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, samsung_mipi_dcphy_of_match);
|
||||
|
||||
47
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h
Normal file
47
drivers/phy/rockchip/phy-rockchip-samsung-dcphy.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Rockchip Samsung mipi dcphy driver
|
||||
*
|
||||
* Copyright (C) 2020 Rockchip Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _PHY_ROCKCHIP_SAMSUNG_DCPHY_H_
|
||||
#define _PHY_ROCKCHIP_SAMSUNG_DCPHY_H_
|
||||
|
||||
#define MAX_NUM_CSI2_DPHY (0x2)
|
||||
|
||||
struct samsung_mipi_dcphy {
|
||||
struct device *dev;
|
||||
struct clk *ref_clk;
|
||||
struct clk *pclk;
|
||||
struct regmap *regmap;
|
||||
struct regmap *grf_regmap;
|
||||
struct reset_control *m_phy_rst;
|
||||
struct reset_control *s_phy_rst;
|
||||
struct reset_control *apb_rst;
|
||||
struct reset_control *grf_apb_rst;
|
||||
struct mutex mutex;
|
||||
struct csi2_dphy *dphy_dev[MAX_NUM_CSI2_DPHY];
|
||||
atomic_t stream_cnt;
|
||||
int dphy_dev_num;
|
||||
bool c_option;
|
||||
|
||||
unsigned int lanes;
|
||||
|
||||
struct {
|
||||
unsigned long long rate;
|
||||
u8 prediv;
|
||||
u16 fbdiv;
|
||||
long dsm;
|
||||
u8 scaler;
|
||||
|
||||
bool ssc_en;
|
||||
u8 mfr;
|
||||
u8 mrr;
|
||||
} pll;
|
||||
|
||||
int (*stream_on)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
int (*stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user