phy: rockchip: mipi-dphy-tx1rx1: add mipi-dphy TX1RX1 channel for RK3288
This patch adds the function of mipi-dphy TX1RX1 for RK3288, the mipi-dphy TX1RX1 is matched when "txrx_base_addr" is valid. Change-Id: I01640925157a7082e942188b29f6bbf1318cf3d5 Signed-off-by: Wen Nuan <leo.wen@rock-chips.com>
This commit is contained in:
@@ -11,10 +11,12 @@ Required properties:
|
||||
|
||||
MIPI RX0 D-PHY use registers in "general register files", it
|
||||
should be a child of the GRF.
|
||||
MIPI TXRX D-PHY have its own registers, it must have a reg property.
|
||||
MIPI TX1RX1 D-PHY have its own registers, it must have a reg property.
|
||||
|
||||
Optional properties:
|
||||
- reg: offset and length of the register set for the device.
|
||||
- rockchip,grf: MIPI TX1RX1 D-PHY not only has its own register but also
|
||||
the GRF, so it is only necessary for MIPI TX1RX1 D-PHY.
|
||||
|
||||
port node
|
||||
-------------------
|
||||
|
||||
@@ -68,7 +68,21 @@
|
||||
#define LANE1_HS_RX_CONTROL 0x54
|
||||
#define LANE2_HS_RX_CONTROL 0x84
|
||||
#define LANE3_HS_RX_CONTROL 0x94
|
||||
#define HS_RX_DATA_LANES_THS_SETTLE__CONTROL 0x75
|
||||
#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
|
||||
|
||||
/*
|
||||
* CSI HOST
|
||||
*/
|
||||
#define CSIHOST_PHY_TEST_CTRL0 0x30
|
||||
#define CSIHOST_PHY_TEST_CTRL1 0x34
|
||||
#define CSIHOST_PHY_SHUTDOWNZ 0x08
|
||||
#define CSIHOST_DPHY_RSTZ 0x0c
|
||||
|
||||
#define PHY_TESTEN_ADDR (0x1 << 16)
|
||||
#define PHY_TESTEN_DATA (0x0 << 16)
|
||||
#define PHY_TESTCLK (0x1 << 1)
|
||||
#define PHY_TESTCLR (0x1 << 0)
|
||||
#define THS_SETTLE_COUNTER_THRESHOLD 0x04
|
||||
|
||||
#define HIWORD_UPDATE(val, mask, shift) \
|
||||
((val) << (shift) | (mask) << ((shift) + 16))
|
||||
@@ -185,6 +199,8 @@ struct hsfreq_range {
|
||||
u8 cfg_bit;
|
||||
};
|
||||
|
||||
struct mipidphy_priv;
|
||||
|
||||
struct dphy_drv_data {
|
||||
const char * const *clks;
|
||||
int num_clks;
|
||||
@@ -221,6 +237,8 @@ struct mipidphy_priv {
|
||||
struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
|
||||
int num_sensors;
|
||||
bool is_streaming;
|
||||
void __iomem *txrx_base_addr;
|
||||
int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
|
||||
};
|
||||
|
||||
static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
|
||||
@@ -228,7 +246,8 @@ static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
|
||||
return container_of(subdev, struct mipidphy_priv, sd);
|
||||
}
|
||||
|
||||
static inline void write_reg(struct mipidphy_priv *priv, int index, u8 value)
|
||||
static inline void write_grf_reg(struct mipidphy_priv *priv,
|
||||
int index, u8 value)
|
||||
{
|
||||
const struct dphy_reg *reg = &priv->grf_regs[index];
|
||||
unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
|
||||
@@ -237,7 +256,7 @@ static inline void write_reg(struct mipidphy_priv *priv, int index, u8 value)
|
||||
regmap_write(priv->regmap_grf, reg->offset, val);
|
||||
}
|
||||
|
||||
static void mipidphy_wr_reg(struct mipidphy_priv *priv,
|
||||
static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
|
||||
u8 test_code, u8 test_data)
|
||||
{
|
||||
/*
|
||||
@@ -245,13 +264,30 @@ static void mipidphy_wr_reg(struct mipidphy_priv *priv,
|
||||
* is latched internally as the current test code. Test data is
|
||||
* programmed internally by rising edge on TESTCLK.
|
||||
*/
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
}
|
||||
|
||||
static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
|
||||
unsigned char data)
|
||||
{
|
||||
/*
|
||||
* TESTEN =1,TESTDIN=addr
|
||||
* TESTCLK=0
|
||||
* TESTEN =0,TESTDIN=data
|
||||
* TESTCLK=1
|
||||
*/
|
||||
writel((PHY_TESTEN_ADDR | addr),
|
||||
priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
|
||||
writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
|
||||
writel((PHY_TESTEN_DATA | data),
|
||||
priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
|
||||
writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
|
||||
}
|
||||
|
||||
static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
|
||||
@@ -316,12 +352,7 @@ static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
|
||||
static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
|
||||
{
|
||||
struct mipidphy_priv *priv = to_dphy_priv(sd);
|
||||
const struct dphy_drv_data *drv_data = priv->drv_data;
|
||||
const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
|
||||
int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
|
||||
int i, ret, hsfreq = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (priv->is_streaming)
|
||||
return 0;
|
||||
@@ -330,44 +361,7 @@ static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < num_hsfreq_ranges; i++) {
|
||||
if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
write_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
|
||||
write_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
|
||||
/* Disable lan turn around, which is ignored in receive mode */
|
||||
write_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
|
||||
write_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
|
||||
|
||||
write_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
|
||||
|
||||
/* dphy start */
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
|
||||
usleep_range(100, 150);
|
||||
write_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
|
||||
usleep_range(100, 150);
|
||||
|
||||
/* set clock lane */
|
||||
/* HS hsfreq_range & lane 0 settle bypass */
|
||||
mipidphy_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane0 */
|
||||
mipidphy_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
|
||||
/* HS RX Control of lane1 */
|
||||
mipidphy_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane2 */
|
||||
mipidphy_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane3 */
|
||||
mipidphy_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
|
||||
/* HS RX Data Lanes Settle State Time Control */
|
||||
mipidphy_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE__CONTROL, 0x04);
|
||||
|
||||
/* Normal operation */
|
||||
mipidphy_wr_reg(priv, 0x0, 0);
|
||||
priv->stream_on(priv, sd);
|
||||
|
||||
priv->is_streaming = true;
|
||||
|
||||
@@ -521,6 +515,112 @@ static const char * const rk3288_mipidphy_clks[] = {
|
||||
"pclk",
|
||||
};
|
||||
|
||||
static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
|
||||
const struct dphy_drv_data *drv_data = priv->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;
|
||||
|
||||
for (i = 0; i < num_hsfreq_ranges; i++) {
|
||||
if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
|
||||
/* Disable lan turn around, which is ignored in receive mode */
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
|
||||
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
|
||||
|
||||
/* dphy start */
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
|
||||
usleep_range(100, 150);
|
||||
write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
|
||||
usleep_range(100, 150);
|
||||
|
||||
/* set clock lane */
|
||||
/* HS hsfreq_range & lane 0 settle bypass */
|
||||
mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane0 */
|
||||
mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
|
||||
/* HS RX Control of lane1 */
|
||||
mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane2 */
|
||||
mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
|
||||
/* HS RX Control of lane3 */
|
||||
mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
|
||||
/* HS RX Data Lanes Settle State Time Control */
|
||||
mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
|
||||
THS_SETTLE_COUNTER_THRESHOLD);
|
||||
|
||||
/* Normal operation */
|
||||
mipidphy0_wr_reg(priv, 0x0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
|
||||
struct v4l2_subdev *sd)
|
||||
{
|
||||
struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
|
||||
struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
|
||||
const struct dphy_drv_data *drv_data = priv->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;
|
||||
|
||||
for (i = 0; i < num_hsfreq_ranges; i++) {
|
||||
if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
|
||||
hsfreq = hsfreq_ranges[i].cfg_bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
|
||||
write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
|
||||
/* Disable lan turn around, which is ignored in receive mode */
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
|
||||
write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
|
||||
GENMASK(sensor->lanes - 1, 0));
|
||||
/* dphy start */
|
||||
writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
|
||||
writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
|
||||
writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
|
||||
writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
|
||||
usleep_range(100, 150);
|
||||
writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
|
||||
usleep_range(100, 150);
|
||||
|
||||
/* set clock lane */
|
||||
mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
|
||||
mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
|
||||
mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
|
||||
mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
|
||||
mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
|
||||
/* HS RX Data Lanes Settle State Time Control */
|
||||
mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
|
||||
THS_SETTLE_COUNTER_THRESHOLD);
|
||||
|
||||
/* Normal operation */
|
||||
mipidphy1_wr_reg(priv, 0x0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
|
||||
.clks = rk3288_mipidphy_clks,
|
||||
.num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
|
||||
@@ -704,6 +804,7 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev)
|
||||
struct v4l2_subdev *sd;
|
||||
struct mipidphy_priv *priv;
|
||||
struct regmap *grf;
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
const struct dphy_drv_data *drv_data;
|
||||
int i, ret;
|
||||
@@ -718,10 +819,14 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
|
||||
grf = syscon_node_to_regmap(dev->parent->of_node);
|
||||
if (IS_ERR(grf)) {
|
||||
grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
||||
"rockchip,grf");
|
||||
if (IS_ERR(grf)) {
|
||||
dev_err(dev, "Can't find GRF syscon\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
priv->regmap_grf = grf;
|
||||
|
||||
drv_data = of_id->data;
|
||||
@@ -736,6 +841,12 @@ static int rockchip_mipidphy_probe(struct platform_device *pdev)
|
||||
|
||||
priv->grf_regs = drv_data->regs;
|
||||
priv->drv_data = drv_data;
|
||||
priv->stream_on = mipidphy_txrx_stream_on;
|
||||
priv->txrx_base_addr = NULL;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
priv->txrx_base_addr = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->txrx_base_addr))
|
||||
priv->stream_on = mipidphy_rx_stream_on;
|
||||
|
||||
sd = &priv->sd;
|
||||
v4l2_subdev_init(sd, &mipidphy_subdev_ops);
|
||||
|
||||
Reference in New Issue
Block a user