mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 12:17:12 +09:00
phy: rockchip: csi2-dphy: support rk3588 dcphy combo driver
Signed-off-by: Zefa Chen <zefa.chen@rock-chips.com> Change-Id: I37c373592e09425ab148ac44b31093a4e7e62e99
This commit is contained in:
@@ -8,9 +8,14 @@
|
||||
#ifndef _PHY_ROCKCHIP_CSI2_DPHY_COMMON_H_
|
||||
#define _PHY_ROCKCHIP_CSI2_DPHY_COMMON_H_
|
||||
|
||||
#define PHY_MAX 16
|
||||
#define MAX_DEV_NAME_LEN 32
|
||||
|
||||
/* add new chip id in tail by time order */
|
||||
enum csi2_dphy_chip_id {
|
||||
CHIP_ID_RK3568 = 0x0,
|
||||
CHIP_ID_RK3588 = 0x1,
|
||||
CHIP_ID_RK3588_DCPHY = 0x2,
|
||||
};
|
||||
|
||||
enum csi2_dphy_rx_pads {
|
||||
@@ -47,6 +52,10 @@ struct csi2_sensor {
|
||||
|
||||
struct csi2_dphy_hw;
|
||||
|
||||
struct dphy_drv_data {
|
||||
const char dev_name[MAX_DEV_NAME_LEN];
|
||||
};
|
||||
|
||||
struct csi2_dphy {
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
@@ -61,17 +70,20 @@ struct csi2_dphy {
|
||||
int phy_index;
|
||||
bool is_streaming;
|
||||
enum csi2_dphy_lane_mode lane_mode;
|
||||
const struct dphy_drv_data *drv_data;
|
||||
};
|
||||
|
||||
struct dphy_hw_drv_data {
|
||||
const struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
const struct hsfreq_range *hsfreq_ranges;
|
||||
int num_hsfreq_ranges;
|
||||
const struct hsfreq_range *hsfreq_ranges_cphy;
|
||||
int num_hsfreq_ranges_cphy;
|
||||
const struct grf_reg *grf_regs;
|
||||
const struct txrx_reg *txrx_regs;
|
||||
const struct csi2dphy_reg *csi2dphy_regs;
|
||||
void (*individual_init)(struct csi2_dphy_hw *hw);
|
||||
int (*stream_on)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
int (*stream_off)(struct csi2_dphy *dphy, struct v4l2_subdev *sd);
|
||||
enum csi2_dphy_chip_id chip_id;
|
||||
};
|
||||
|
||||
@@ -83,7 +95,8 @@ struct csi2_dphy_hw {
|
||||
const struct csi2dphy_reg *csi2dphy_regs;
|
||||
const struct dphy_hw_drv_data *drv_data;
|
||||
void __iomem *hw_base_addr;
|
||||
struct clk_bulk_data *clks;
|
||||
struct clk_bulk_data *clks_bulk;
|
||||
struct reset_control *rsts_bulk;
|
||||
struct csi2_dphy *dphy_dev[MAX_NUM_CSI2_DPHY];
|
||||
struct v4l2_subdev sd;
|
||||
struct mutex mutex; /* lock for updating protection */
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <linux/reset.h>
|
||||
#include "phy-rockchip-csi2-dphy-common.h"
|
||||
|
||||
/* GRF REG OFFSET */
|
||||
@@ -32,6 +33,8 @@
|
||||
#define GRF_CSI2PHY_SEL_SPLIT_0_1 (0x0)
|
||||
#define GRF_CSI2PHY_SEL_SPLIT_2_3 BIT(0)
|
||||
|
||||
#define GRF_DCPHY_CON0 (0x0)
|
||||
|
||||
/* PHY REG OFFSET */
|
||||
#define CSI2_DPHY_CTRL_INVALID_OFFSET (0xffff)
|
||||
#define CSI2_DPHY_CTRL_PWRCTL \
|
||||
@@ -51,6 +54,21 @@
|
||||
#define CSI2_DPHY_CLK1_WR_THS_SETTLE (0x3e0)
|
||||
#define CSI2_DPHY_CLK1_CALIB_EN (0x3e8)
|
||||
|
||||
#define CSI2_DCPHY_CLK_WR_THS_SETTLE (0xB20)
|
||||
#define CSI2_DCPHY_LANE0_WR_THS_SETTLE (0xC30)
|
||||
#define CSI2_DCPHY_LANE0_WR_ERR_SOT_SYNC (0xC34)
|
||||
#define CSI2_DCPHY_LANE1_WR_THS_SETTLE (0xD30)
|
||||
#define CSI2_DCPHY_LANE1_WR_ERR_SOT_SYNC (0xD34)
|
||||
#define CSI2_DCPHY_LANE2_WR_THS_SETTLE (0xE30)
|
||||
#define CSI2_DCPHY_LANE2_WR_ERR_SOT_SYNC (0xE34)
|
||||
#define CSI2_DCPHY_LANE3_WR_THS_SETTLE (0xF30)
|
||||
#define CSI2_DCPHY_LANE3_WR_ERR_SOT_SYNC (0xF34)
|
||||
#define CSI2_DCPHY_CLK_LANE_ENABLE (0xB00)
|
||||
#define CSI2_DCPHY_DATA_LANE0_ENABLE (0xC00)
|
||||
#define CSI2_DCPHY_DATA_LANE1_ENABLE (0xD00)
|
||||
#define CSI2_DCPHY_DATA_LANE2_ENABLE (0xE00)
|
||||
#define CSI2_DCPHY_DATA_LANE3_ENABLE (0xF00)
|
||||
|
||||
/* PHY REG BIT DEFINE */
|
||||
#define CSI2_DPHY_LANE_MODE_FULL (0x4)
|
||||
#define CSI2_DPHY_LANE_MODE_SPLIT (0x2)
|
||||
@@ -130,6 +148,7 @@ enum grf_reg_id {
|
||||
GRF_DPHY_CSI2PHY_LANE_SEL,
|
||||
GRF_DPHY_CSI2PHY_DATALANE_EN0,
|
||||
GRF_DPHY_CSI2PHY_DATALANE_EN1,
|
||||
GRF_CPHY_MODE,
|
||||
};
|
||||
|
||||
enum csi2dphy_reg_id {
|
||||
@@ -152,7 +171,17 @@ enum csi2dphy_reg_id {
|
||||
//rk3568 only
|
||||
CSI2PHY_DUAL_CLK_EN,
|
||||
CSI2PHY_CLK1_THS_SETTLE,
|
||||
CSI2PHY_CLK1_CALIB_ENABLE
|
||||
CSI2PHY_CLK1_CALIB_ENABLE,
|
||||
//rk3588
|
||||
CSI2PHY_CLK_LANE_ENABLE,
|
||||
CSI2PHY_DATA_LANE0_ENABLE,
|
||||
CSI2PHY_DATA_LANE1_ENABLE,
|
||||
CSI2PHY_DATA_LANE2_ENABLE,
|
||||
CSI2PHY_DATA_LANE3_ENABLE,
|
||||
CSI2PHY_LANE0_ERR_SOT_SYNC,
|
||||
CSI2PHY_LANE1_ERR_SOT_SYNC,
|
||||
CSI2PHY_LANE2_ERR_SOT_SYNC,
|
||||
CSI2PHY_LANE3_ERR_SOT_SYNC,
|
||||
};
|
||||
|
||||
#define HIWORD_UPDATE(val, mask, shift) \
|
||||
@@ -166,7 +195,7 @@ enum csi2dphy_reg_id {
|
||||
|
||||
struct hsfreq_range {
|
||||
u32 range_h;
|
||||
u8 cfg_bit;
|
||||
u16 cfg_bit;
|
||||
};
|
||||
|
||||
static inline void write_grf_reg(struct csi2_dphy_hw *hw,
|
||||
@@ -203,6 +232,18 @@ static inline void write_csi2_dphy_reg(struct csi2_dphy_hw *hw,
|
||||
writel(value, hw->hw_base_addr + reg->offset);
|
||||
}
|
||||
|
||||
static inline void write_csi2_dphy_reg_mask(struct csi2_dphy_hw *hw,
|
||||
int index, u32 value, u32 mask)
|
||||
{
|
||||
const struct csi2dphy_reg *reg = &hw->csi2dphy_regs[index];
|
||||
u32 read_val = 0;
|
||||
|
||||
read_val = readl(hw->hw_base_addr + reg->offset);
|
||||
read_val &= ~mask;
|
||||
read_val |= value;
|
||||
writel(read_val, hw->hw_base_addr + reg->offset);
|
||||
}
|
||||
|
||||
static inline void read_csi2_dphy_reg(struct csi2_dphy_hw *hw,
|
||||
int index, u32 *value)
|
||||
{
|
||||
@@ -280,8 +321,25 @@ static const struct csi2dphy_reg rk3568_csi2dphy_regs[] = {
|
||||
[CSI2PHY_CLK1_CALIB_ENABLE] = CSI2PHY_REG(CSI2_DPHY_CLK1_CALIB_EN),
|
||||
};
|
||||
|
||||
static const struct clk_bulk_data rk3568_csi2_dphy_hw_clks[] = {
|
||||
{ .id = "pclk" },
|
||||
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),
|
||||
};
|
||||
|
||||
/* These tables must be sorted by .range_h ascending. */
|
||||
@@ -293,6 +351,37 @@ 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;
|
||||
@@ -525,20 +614,193 @@ 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);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void rk3588_csi2_dcphy_hw_individual_init(struct csi2_dphy_hw *hw)
|
||||
{
|
||||
hw->grf_regs = rk3588_grf_dcphy_regs;
|
||||
}
|
||||
|
||||
static const struct dphy_hw_drv_data rk3568_csi2_dphy_hw_drv_data = {
|
||||
.clks = rk3568_csi2_dphy_hw_clks,
|
||||
.num_clks = ARRAY_SIZE(rk3568_csi2_dphy_hw_clks),
|
||||
.hsfreq_ranges = rk3568_csi2_dphy_hw_hsfreq_ranges,
|
||||
.num_hsfreq_ranges = ARRAY_SIZE(rk3568_csi2_dphy_hw_hsfreq_ranges),
|
||||
.csi2dphy_regs = rk3568_csi2dphy_regs,
|
||||
.grf_regs = rk3568_grf_dphy_regs,
|
||||
.individual_init = rk3568_csi2_dphy_hw_individual_init,
|
||||
.chip_id = CHIP_ID_RK3568,
|
||||
.stream_on = csi2_dphy_hw_stream_on,
|
||||
.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 of_device_id rockchip_csi2_dphy_hw_match_id[] = {
|
||||
@@ -546,6 +808,10 @@ static const struct of_device_id rockchip_csi2_dphy_hw_match_id[] = {
|
||||
.compatible = "rockchip,rk3568-csi2-dphy-hw",
|
||||
.data = &rk3568_csi2_dphy_hw_drv_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3588-csi2-dcphy-hw",
|
||||
.data = &rk3588_csi2_dcphy_hw_drv_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_csi2_dphy_hw_match_id);
|
||||
@@ -558,7 +824,6 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
const struct dphy_hw_drv_data *drv_data;
|
||||
int ret;
|
||||
|
||||
dphy_hw = devm_kzalloc(dev, sizeof(*dphy_hw), GFP_KERNEL);
|
||||
if (!dphy_hw)
|
||||
@@ -569,6 +834,8 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
drv_data = of_id->data;
|
||||
|
||||
grf = syscon_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(grf)) {
|
||||
grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
@@ -580,22 +847,13 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
}
|
||||
dphy_hw->regmap_grf = grf;
|
||||
|
||||
drv_data = of_id->data;
|
||||
dphy_hw->num_clks = drv_data->num_clks;
|
||||
dphy_hw->clks = devm_kmemdup(dev, drv_data->clks,
|
||||
drv_data->num_clks * sizeof(struct clk_bulk_data),
|
||||
GFP_KERNEL);
|
||||
if (!dphy_hw->clks) {
|
||||
dev_err(dev, "failed to acquire csi2 dphy clks mem\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = devm_clk_bulk_get(dev, dphy_hw->num_clks, dphy_hw->clks);
|
||||
if (ret == -EPROBE_DEFER) {
|
||||
dev_err(dev, "get csi2 dphy clks failed\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
if (ret)
|
||||
dphy_hw->num_clks = 0;
|
||||
dphy_hw->num_clks = devm_clk_bulk_get_all(dev, &dphy_hw->clks_bulk);
|
||||
if (dphy_hw->num_clks < 0)
|
||||
dev_err(dev, "failed to get csi2 clks\n");
|
||||
|
||||
dphy_hw->rsts_bulk = devm_reset_control_array_get_optional_exclusive(dev);
|
||||
if (IS_ERR(dphy_hw->rsts_bulk))
|
||||
dev_err_probe(dev, PTR_ERR(dphy_hw->rsts_bulk), "failed to get dphy reset\n");
|
||||
|
||||
dphy_hw->dphy_dev_num = 0;
|
||||
dphy_hw->drv_data = drv_data;
|
||||
@@ -616,8 +874,8 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
dphy_hw->stream_on = csi2_dphy_hw_stream_on;
|
||||
dphy_hw->stream_off = csi2_dphy_hw_stream_off;
|
||||
dphy_hw->stream_on = drv_data->stream_on;
|
||||
dphy_hw->stream_off = drv_data->stream_off;
|
||||
|
||||
atomic_set(&dphy_hw->stream_cnt, 0);
|
||||
|
||||
@@ -627,8 +885,6 @@ static int rockchip_csi2_dphy_hw_probe(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
platform_driver_register(&rockchip_csi2_dphy_driver);
|
||||
|
||||
dev_info(dev, "csi2 dphy hw probe successfully!\n");
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -236,7 +236,7 @@ static __maybe_unused int csi2_dphy_runtime_suspend(struct device *dev)
|
||||
struct csi2_dphy_hw *hw = dphy->dphy_hw;
|
||||
|
||||
if (hw)
|
||||
clk_bulk_disable_unprepare(hw->num_clks, hw->clks);
|
||||
clk_bulk_disable_unprepare(hw->num_clks, hw->clks_bulk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -250,7 +250,7 @@ static __maybe_unused int csi2_dphy_runtime_resume(struct device *dev)
|
||||
int ret;
|
||||
|
||||
if (hw) {
|
||||
ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks);
|
||||
ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks_bulk);
|
||||
if (ret) {
|
||||
dev_err(hw->dev, "failed to enable clks\n");
|
||||
return ret;
|
||||
@@ -547,9 +547,22 @@ static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dphy_drv_data r3568_dphy_drv_data = {
|
||||
.dev_name = "csi2dphy",
|
||||
};
|
||||
|
||||
static struct dphy_drv_data r3588_dcphy_drv_data = {
|
||||
.dev_name = "csi2dcphy",
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_csi2_dphy_match_id[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3568-csi2-dphy",
|
||||
.data = &r3568_dphy_drv_data,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3588-csi2-dcphy",
|
||||
.data = &r3588_dcphy_drv_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
@@ -561,6 +574,7 @@ static int rockchip_csi2_dphy_probe(struct platform_device *pdev)
|
||||
const struct of_device_id *of_id;
|
||||
struct csi2_dphy *csi2dphy;
|
||||
struct v4l2_subdev *sd;
|
||||
const struct dphy_drv_data *drv_data;
|
||||
int ret;
|
||||
|
||||
csi2dphy = devm_kzalloc(dev, sizeof(*csi2dphy), GFP_KERNEL);
|
||||
@@ -571,11 +585,11 @@ static int rockchip_csi2_dphy_probe(struct platform_device *pdev)
|
||||
of_id = of_match_device(rockchip_csi2_dphy_match_id, dev);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
csi2dphy->phy_index = of_alias_get_id(dev->of_node, "csi2dphy");
|
||||
if (csi2dphy->phy_index < 0 || csi2dphy->phy_index > 2)
|
||||
drv_data = of_id->data;
|
||||
csi2dphy->drv_data = drv_data;
|
||||
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 (ret) {
|
||||
dev_err(dev,
|
||||
@@ -638,7 +652,7 @@ struct platform_driver rockchip_csi2_dphy_driver = {
|
||||
.of_match_table = rockchip_csi2_dphy_match_id,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL(rockchip_csi2_dphy_driver);
|
||||
module_platform_driver(rockchip_csi2_dphy_driver);
|
||||
|
||||
MODULE_AUTHOR("Rockchip Camera/ISP team");
|
||||
MODULE_DESCRIPTION("Rockchip MIPI CSI2 DPHY driver");
|
||||
|
||||
Reference in New Issue
Block a user