The current CSI2 DPHY initialization sequence although
functional does not match with the documented sequence
in the Technical Reference Manual.

This may affect capture re-startability in stop/start
situations.

Signed-off-by: Benoit Parrot <bpar...@ti.com>
---
 drivers/media/platform/ti-vpe/cal.c | 199 ++++++++++++++++++++++++----
 1 file changed, 172 insertions(+), 27 deletions(-)

diff --git a/drivers/media/platform/ti-vpe/cal.c 
b/drivers/media/platform/ti-vpe/cal.c
index 3c293e263397..c912ea82b534 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti-vpe/cal.c
@@ -689,29 +689,76 @@ static void disable_irqs(struct cal_ctx *ctx)
        reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0);
 }
 
-static void csi2_init(struct cal_ctx *ctx)
+static void csi2_phy_config(struct cal_ctx *ctx);
+
+static void csi2_phy_init(struct cal_ctx *ctx)
 {
        int i;
        u32 val;
 
+       /* Steps
+        *  1. Configure D-PHY mode and enable required lanes
+        *  2. Reset complex IO - Wait for completion of reset
+        *          Note if the external sensor is not sending byte clock,
+        *          the reset will timeout
+        *  3 Program Stop States
+        *      A. Program THS_TERM, THS_SETTLE, etc... Timings parameters
+        *              in terms of DDR clock periods
+        *      B. Enable stop state transition timeouts
+        *  4.Force FORCERXMODE
+        *      D. Enable pull down using pad control
+        *      E. Power up PHY
+        *      F. Wait for power up completion
+        *      G. Wait for all enabled lane to reach stop state
+        *      H. Disable pull down using pad control
+        */
+
+       /* 1. Configure D-PHY mode and enable required lanes */
+       camerarx_phy_enable(ctx);
+
+       /* 2. Reset complex IO - Do not wait for reset completion */
+       val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+       set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
+                 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+       reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex 
IO Reset\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+
+       /* Dummy read to allow SCP to complete */
+       val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+
+       /* 3.A. Program Phy Timing Parameters */
+       csi2_phy_config(ctx);
+
+       /* 3.B. Program Stop States */
        val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
-       set_field(&val, CAL_GEN_ENABLE,
-                 CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
        set_field(&val, CAL_GEN_ENABLE,
                  CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK);
        set_field(&val, CAL_GEN_DISABLE,
                  CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK);
        set_field(&val, 407, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK);
        reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
-       ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x\n", ctx->csi2_port,
+       ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
+
+       /* 4. Force FORCERXMODE */
+       val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port));
+       set_field(&val, CAL_GEN_ENABLE,
+                 CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK);
+       reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val);
+       ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n",
+               ctx->csi2_port,
                reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)));
 
+       /* E. Power up the PHY using the complex IO */
        val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
-       set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL,
-                 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
        set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON,
                  CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
        reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+       /* F. Wait for power up completion */
        for (i = 0; i < 10; i++) {
                if (reg_read_field(ctx->dev,
                                   CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
@@ -720,18 +767,104 @@ static void csi2_init(struct cal_ctx *ctx)
                        break;
                usleep_range(1000, 1100);
        }
-       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", ctx->csi2_port,
-               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)));
+       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered UP %s\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)),
+               (i >= 10) ? "(timeout)" : "");
+}
 
-       val = reg_read(ctx->dev, CAL_CTRL);
-       set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
-       set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
-       set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
-                 CAL_CTRL_POSTED_WRITES_MASK);
-       set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
-       set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
-       reg_write(ctx->dev, CAL_CTRL, val);
-       ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
+static void csi2_wait_for_phy(struct cal_ctx *ctx)
+{
+       int i;
+
+       /* Steps
+        *  2. Wait for completion of reset
+        *          Note if the external sensor is not sending byte clock,
+        *          the reset will timeout
+        *  4.Force FORCERXMODE
+        *      G. Wait for all enabled lane to reach stop state
+        *      H. Disable pull down using pad control
+        */
+
+       /* 2. Wait for reset completion */
+       for (i = 0; i < 250; i++) {
+               if (reg_read_field(ctx->dev,
+                                  CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+                                  CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+                   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED)
+                       break;
+               usleep_range(1000, 1100);
+       }
+       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO Reset 
Done (%d) %s\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i,
+               (i >= 250) ? "(timeout)" : "");
+
+       /* 4. G. Wait for all enabled lane to reach stop state */
+       for (i = 0; i < 10; i++) {
+               if (reg_read_field(ctx->dev,
+                                  CAL_CSI2_TIMING(ctx->csi2_port),
+                                  CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) ==
+                   CAL_GEN_DISABLE)
+                       break;
+               usleep_range(1000, 1100);
+       }
+       ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop State Reached %s\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)),
+               (i >= 10) ? "(timeout)" : "");
+
+       ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n",
+               (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1));
+}
+
+static void csi2_phy_deinit(struct cal_ctx *ctx)
+{
+       int i;
+       u32 val;
+
+       /* Power down the PHY using the complex IO */
+       val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+       set_field(&val, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF,
+                 CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK);
+       reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+       /* Wait for power down completion */
+       for (i = 0; i < 10; i++) {
+               if (reg_read_field(ctx->dev,
+                                  CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+                                  CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK) ==
+                   CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_STATE_OFF)
+                       break;
+               usleep_range(1000, 1100);
+       }
+       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Powered Down %s\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)),
+               (i >= 10) ? "(timeout)" : "");
+
+       /* Assert Comple IO Reset */
+       val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port));
+       set_field(&val, CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL,
+                 CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK);
+       reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val);
+
+       /* Wait for power down completion */
+       for (i = 0; i < 10; i++) {
+               if (reg_read_field(ctx->dev,
+                                  CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port),
+                                  CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) ==
+                   CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING)
+                       break;
+               usleep_range(1000, 1100);
+       }
+       ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in 
Reset (%d) %s\n",
+               ctx->csi2_port,
+               reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i,
+               (i >= 10) ? "(timeout)" : "");
+
+       /* Disable the phy */
+       camerarx_phy_disable(ctx);
 }
 
 static void csi2_lane_config(struct cal_ctx *ctx)
@@ -894,6 +1027,16 @@ static void cal_wr_dma_config(struct cal_ctx *ctx,
        reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val);
        ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port,
                reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)));
+
+       val = reg_read(ctx->dev, CAL_CTRL);
+       set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK);
+       set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK);
+       set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED,
+                 CAL_CTRL_POSTED_WRITES_MASK);
+       set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK);
+       set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK);
+       reg_write(ctx->dev, CAL_CTRL, val);
+       ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL));
 }
 
 static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr)
@@ -1466,17 +1609,14 @@ static int cal_start_streaming(struct vb2_queue *vq, 
unsigned int count)
 
        cal_runtime_get(ctx->dev);
 
-       enable_irqs(ctx);
-       camerarx_phy_enable(ctx);
-       csi2_init(ctx);
-       csi2_phy_config(ctx);
-       csi2_lane_config(ctx);
        csi2_ctx_config(ctx);
        pix_proc_config(ctx);
        cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline,
                          ctx->v_fmt.fmt.pix.height);
-       cal_wr_dma_addr(ctx, addr);
-       csi2_ppi_enable(ctx);
+       csi2_lane_config(ctx);
+
+       enable_irqs(ctx);
+       csi2_phy_init(ctx);
 
        ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1);
        if (ret) {
@@ -1485,6 +1625,10 @@ static int cal_start_streaming(struct vb2_queue *vq, 
unsigned int count)
                goto err;
        }
 
+       csi2_wait_for_phy(ctx);
+       cal_wr_dma_addr(ctx, addr);
+       csi2_ppi_enable(ctx);
+
        if (debug >= 4)
                cal_quickdump_regs(ctx->dev);
 
@@ -1505,11 +1649,12 @@ static void cal_stop_streaming(struct vb2_queue *vq)
        struct cal_buffer *buf, *tmp;
        unsigned long flags;
 
-       if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
-               ctx_err(ctx, "stream off failed in subdev\n");
-
        csi2_ppi_disable(ctx);
        disable_irqs(ctx);
+       csi2_phy_deinit(ctx);
+
+       if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0))
+               ctx_err(ctx, "stream off failed in subdev\n");
 
        /* Release all active buffers */
        spin_lock_irqsave(&ctx->slock, flags);
-- 
2.17.1

Reply via email to