]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
media: tegra_camera: add SoC fops for CSI
authorBryan Wu <pengw@nvidia.com>
Sat, 18 Jun 2016 00:16:13 +0000 (17:16 -0700)
committermobile promotions <svcmobile_promotions@nvidia.com>
Tue, 12 Jul 2016 04:02:10 +0000 (21:02 -0700)
Add an SoC abstraction layer for for CSI which can be extended to
support different Tegra SoC in the future. Now start with T210.

Bug 1779020

Change-Id: I7c081ccb008bb7775591300495154aaf4505aee5
Signed-off-by: Bryan Wu <pengw@nvidia.com>
Reviewed-on: http://git-master/r/1169883
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Jihoon Bang <jbang@nvidia.com>
drivers/media/platform/tegra/csi/Makefile
drivers/media/platform/tegra/csi/csi.c
drivers/media/platform/tegra/csi/csi.h
drivers/media/platform/tegra/csi/csi2_fops.c [new file with mode: 0644]
drivers/media/platform/tegra/csi/csi2_fops.h [new file with mode: 0644]
drivers/media/platform/tegra/tpg/tpg.c
drivers/media/platform/tegra/vi/vi.c
drivers/media/platform/tegra/vi/vi.h

index 33b59fad9205f1d9803c9f10b2ec35fcf43e28c5..648d5bbd279a6d27a8fcf6badcc4724d93d3334e 100644 (file)
@@ -3,4 +3,4 @@ ccflags-y += -Idrivers/video/tegra/host
 ccflags-y += -Idrivers/media/platform/tegra
 ccflags-y += -Werror
 
-obj-y += csi.o
+obj-y += csi.o csi2_fops.o
index a019e027dc451cafb57f565f48e1991244a97b7e..566e73e4d19b4156bbcd15ee2b481862ffcbd778 100644 (file)
 #include "vi/vi.h"
 #include "csi/csi.h"
 
-#define DEBUG 0
-
-static void csi_write(struct tegra_csi_device *csi, unsigned int addr,
-               u32 val, u8 port)
-{
-#if DEBUG
-       dev_info(csi->dev, "%s:port %d offset 0x%08x val:0x%08x\n",
-                               __func__, port, addr, val);
-#endif
-       writel(val, (csi->iomem[port] + addr));
-}
-
-static u32 csi_read(struct tegra_csi_device *csi, unsigned int addr,
-               u8 port)
-{
-#if DEBUG
-       dev_info(csi->dev, "%s:port %d offset 0x%08x\n", __func__, port, addr);
-#endif
-       return readl((csi->iomem[port] + addr));
-}
-
-/* Pixel parser registers accessors */
-static void pp_write(struct tegra_csi_port *port, u32 addr, u32 val)
-{
-#if DEBUG
-       pr_info("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val);
-#endif
-       writel(val, port->pixel_parser + addr);
-}
-
-static u32 pp_read(struct tegra_csi_port *port, u32 addr)
-{
-#if DEBUG
-       pr_info("%s:offset 0x%08x\n", __func__, addr);
-#endif
-       return readl(port->pixel_parser + addr);
-}
-
-/* CSI CIL registers accessors */
-static void cil_write(struct tegra_csi_port *port, u32 addr, u32 val)
-{
-#if DEBUG
-       pr_info("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val);
-#endif
-       writel(val, port->cil + addr);
-}
-
-static u32 cil_read(struct tegra_csi_port *port, u32 addr)
-{
-#if DEBUG
-       pr_info("%s:offset 0x%08x\n", __func__, addr);
-#endif
-       return readl(port->cil + addr);
-}
-
-/* Test pattern generator registers accessor */
-static void tpg_write(struct tegra_csi_port *port, unsigned int addr, u32 val)
-{
-       writel(val, port->tpg + addr);
-}
-
 static int csi_get_clks(struct tegra_csi_device *csi,
                        struct platform_device *pdev)
 {
@@ -305,210 +244,39 @@ EXPORT_SYMBOL(tegra_csi_power);
 void tegra_csi_tpg_start_streaming(struct tegra_csi_device *csi,
                                   enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-
-       tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
-                       ((csi->pg_mode - 1) << PG_MODE_OFFSET) |
-                       PG_ENABLE);
-       tpg_write(port, TEGRA_CSI_PG_PHASE, 0x0);
-       tpg_write(port, TEGRA_CSI_PG_RED_FREQ,
-                       (0x10 << PG_RED_VERT_INIT_FREQ_OFFSET) |
-                       (0x10 << PG_RED_HOR_INIT_FREQ_OFFSET));
-       tpg_write(port, TEGRA_CSI_PG_RED_FREQ_RATE, 0x0);
-       tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ,
-                       (0x10 << PG_GREEN_VERT_INIT_FREQ_OFFSET) |
-                       (0x10 << PG_GREEN_HOR_INIT_FREQ_OFFSET));
-       tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ_RATE, 0x0);
-       tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ,
-                       (0x10 << PG_BLUE_VERT_INIT_FREQ_OFFSET) |
-                       (0x10 << PG_BLUE_HOR_INIT_FREQ_OFFSET));
-       tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ_RATE, 0x0);
+       csi->fops->soc_tpg_start_streaming(csi, port_num);
 }
 
 void tegra_csi_start_streaming(struct tegra_csi_device *csi,
                                enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-
-       csi_write(csi, TEGRA_CSI_CLKEN_OVERRIDE, 0, port_num>>1);
-
-       /* Clean up status */
-       pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, 0xFFFFFFFF);
-       cil_write(port, TEGRA_CSI_CIL_STATUS, 0xFFFFFFFF);
-       cil_write(port, TEGRA_CSI_CILX_STATUS, 0xFFFFFFFF);
-
-       cil_write(port, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0);
-
-       /* CIL PHY registers setup */
-       cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
-       cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL, BYPASS_LP_SEQ | 0xA);
-
-       /*
-        * The CSI unit provides for connection of up to six cameras in
-        * the system and is organized as three identical instances of
-        * two MIPI support blocks, each with a separate 4-lane
-        * interface that can be configured as a single camera with 4
-        * lanes or as a dual camera with 2 lanes available for each
-        * camera.
-        */
-       if (port->lanes == 4) {
-               int port_val = ((port_num >> 1) << 1);
-               struct tegra_csi_port *port_a = &csi->ports[port_val];
-               struct tegra_csi_port *port_b = &csi->ports[port_val+1];
-
-               cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0,
-                         BRICK_CLOCK_A_4X);
-               cil_write(port_b, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
-               cil_write(port_b, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0);
-               cil_write(port_a, TEGRA_CSI_CIL_PHY_CONTROL,
-                                       BYPASS_LP_SEQ | 0xA);
-               cil_write(port_b, TEGRA_CSI_CIL_PHY_CONTROL,
-                                       BYPASS_LP_SEQ | 0xA);
-               csi_write(csi, TEGRA_CSI_PHY_CIL_COMMAND,
-                       CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE,
-                       port_num>>1);
-       } else {
-               u32 val = csi_read(csi, TEGRA_CSI_PHY_CIL_COMMAND, port_num>>1);
-               int port_val = ((port_num >> 1) << 1);
-               struct tegra_csi_port *port_a = &csi->ports[port_val];
-
-               cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
-               val |= ((port->num & 0x1) == PORT_A) ? CSI_A_PHY_CIL_ENABLE :
-                       CSI_B_PHY_CIL_ENABLE;
-               csi_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, val, port_num>>1);
-       }
-
-       /* CSI pixel parser registers setup */
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
-                (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
-                CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_RST);
-       pp_write(port, TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0);
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL0,
-                CSI_PP_PACKET_HEADER_SENT |
-                CSI_PP_DATA_IDENTIFIER_ENABLE |
-                CSI_PP_WORD_COUNT_SELECT_HEADER |
-                CSI_PP_CRC_CHECK_ENABLE |  CSI_PP_WC_CHECK |
-                CSI_PP_OUTPUT_FORMAT_STORE | CSI_PPA_PAD_LINE_NOPAD |
-                CSI_PP_HEADER_EC_DISABLE | CSI_PPA_PAD_FRAME_NOPAD |
-                (port->num & 1));
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL1,
-                (0x1 << CSI_PP_TOP_FIELD_FRAME_OFFSET) |
-                (0x1 << CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET));
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_GAP,
-                0x14 << PP_FRAME_MIN_GAP_OFFSET);
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME, 0x0);
-       pp_write(port, TEGRA_CSI_INPUT_STREAM_CONTROL,
-                (0x3f << CSI_SKIP_PACKET_THRESHOLD_OFFSET) |
-                (port->lanes - 1));
-
-#if DEBUG
-       /* 0x454140E1 - register setting for line counter */
-       /* 0x454340E1 - tracks frame start, line starts, hpa headers */
-       pp_write(port, TEGRA_CSI_DEBUG_CONTROL, 0x454340E1);
-#endif
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
-                (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
-                CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_ENABLE);
+       csi->fops->soc_start_streaming(csi, port_num);
 }
 EXPORT_SYMBOL(tegra_csi_start_streaming);
 
 int tegra_csi_error(struct tegra_csi_device *csi,
                        enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-       u32 val;
-       int err = 0;
-
-       /*
-        * only uncorrectable header error and multi-bit
-        * transmission errors are checked as they cannot be
-        * corrected automatically
-       */
-       val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS);
-       err |= val & 0x4000;
-       pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, val);
-
-       val = cil_read(port, TEGRA_CSI_CIL_STATUS);
-       err |= val & 0x02;
-       cil_write(port, TEGRA_CSI_CIL_STATUS, val);
-
-       val = cil_read(port, TEGRA_CSI_CILX_STATUS);
-       err |= val & 0x00020020;
-       cil_write(port, TEGRA_CSI_CILX_STATUS, val);
-
-       return err;
+       return csi->fops->soc_error(csi, port_num);
 }
 
 void tegra_csi_status(struct tegra_csi_device *csi,
                        enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-       u32 val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS);
-
-       dev_dbg(csi->dev, "TEGRA_CSI_PIXEL_PARSER_STATUS 0x%08x\n",
-               val);
-
-       val = cil_read(port, TEGRA_CSI_CIL_STATUS);
-       dev_dbg(csi->dev, "TEGRA_CSI_CIL_STATUS 0x%08x\n", val);
-
-       val = cil_read(port, TEGRA_CSI_CILX_STATUS);
-       dev_dbg(csi->dev, "TEGRA_CSI_CILX_STATUS 0x%08x\n", val);
-
-#if DEBUG
-       val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_0);
-       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_0 0x%08x\n", val);
-       val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_1);
-       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_1 0x%08x\n", val);
-       val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_2);
-       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_2 0x%08x\n", val);
-#endif
+       csi->fops->soc_status(csi, port_num);
 }
 EXPORT_SYMBOL(tegra_csi_status);
 
 void tegra_csi_error_recover(struct tegra_csi_device *csi,
                                enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-
-       if (port->lanes == 4) {
-               int port_val = ((port_num >> 1) << 1);
-               struct tegra_csi_port *port_a = &csi->ports[port_val];
-               struct tegra_csi_port *port_b = &csi->ports[port_val+1];
-               tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE);
-               tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE);
-               cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
-               cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
-               csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, port_num>>1);
-               /* sleep for clock cycles to drain the Rx FIFO */
-               usleep_range(10, 20);
-               cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
-               cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
-               csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, port_num>>1);
-               tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE);
-               tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE);
-       } else {
-               tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE);
-               cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
-               csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, port_num>>1);
-               /* sleep for clock cycles to drain the Rx FIFO */
-               usleep_range(10, 20);
-               cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
-               csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, port_num>>1);
-               tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE);
-       }
+       csi->fops->soc_error_recover(csi, port_num);
 }
 
 void tegra_csi_stop_streaming(struct tegra_csi_device *csi,
                                enum tegra_csi_port_num port_num)
 {
-       struct tegra_csi_port *port = &csi->ports[port_num];
-
-       if (csi->pg_mode)
-               tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE);
-
-       pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
-                (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
-                CSI_PP_DISABLE);
+       csi->fops->soc_stop_streaming(csi, port_num);
 }
 EXPORT_SYMBOL(tegra_csi_stop_streaming);
 
index 8b1e047d6b8947e6114a0d07f9b86738c13713fc..74b6a792dffa811562bd548137ab0dbe10f01d68 100644 (file)
@@ -55,6 +55,7 @@ struct tegra_csi_port {
 
 struct tegra_csi_device {
        struct v4l2_subdev subdev;
+       struct vi *vi;
        struct device *dev;
        void __iomem *iomem[3];
        struct clk *clk;
@@ -68,6 +69,23 @@ struct tegra_csi_device {
        unsigned int clk_freq;
        int num_ports;
        int pg_mode;
+
+       struct tegra_csi_fops *fops;
+};
+
+struct tegra_csi_fops {
+       void (*soc_tpg_start_streaming)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
+       void (*soc_start_streaming)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
+       int (*soc_error)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
+       void (*soc_status)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
+       void (*soc_error_recover)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
+       void (*soc_stop_streaming)(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num);
 };
 
 static inline struct tegra_csi_device *to_csi(struct v4l2_subdev *subdev)
diff --git a/drivers/media/platform/tegra/csi/csi2_fops.c b/drivers/media/platform/tegra/csi/csi2_fops.c
new file mode 100644 (file)
index 0000000..9e13f09
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Tegra CSI2 device common APIs
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Bryan Wu <pengw@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include "csi/csi.h"
+
+#define DEBUG 0
+
+static void csi2_write(struct tegra_csi_device *csi, unsigned int addr,
+                          u32 val, u8 port)
+{
+       dev_dbg(csi->dev, "%s:port %d offset 0x%08x val:0x%08x\n",
+                               __func__, port, addr, val);
+       writel(val, (csi->iomem[port] + addr));
+}
+
+static u32 csi2_read(struct tegra_csi_device *csi, unsigned int addr,
+                        u8 port)
+{
+       dev_dbg(csi->dev, "%s:port %d offset 0x%08x\n", __func__, port, addr);
+       return readl((csi->iomem[port] + addr));
+}
+
+/* Pixel parser registers accessors */
+static void csi2_pp_write(struct tegra_csi_port *port, u32 addr, u32 val)
+{
+       pr_debug("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val);
+       writel(val, port->pixel_parser + addr);
+}
+
+static u32 csi2_pp_read(struct tegra_csi_port *port, u32 addr)
+{
+       pr_debug("%s:offset 0x%08x\n", __func__, addr);
+       return readl(port->pixel_parser + addr);
+}
+
+/* CSI CIL registers accessors */
+static void csi2_cil_write(struct tegra_csi_port *port, u32 addr, u32 val)
+{
+       pr_debug("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val);
+       writel(val, port->cil + addr);
+}
+
+static u32 csi2_cil_read(struct tegra_csi_port *port, u32 addr)
+{
+       pr_debug("%s:offset 0x%08x\n", __func__, addr);
+       return readl(port->cil + addr);
+}
+
+/* Test pattern generator registers accessor */
+static void csi2_tpg_write(struct tegra_csi_port *port,
+                              unsigned int addr, u32 val)
+{
+       writel(val, port->tpg + addr);
+}
+
+void csi2_tpg_start_streaming(struct tegra_csi_device *csi,
+                             enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+
+       csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                      ((csi->pg_mode - 1) << PG_MODE_OFFSET) |
+                      PG_ENABLE);
+       csi2_tpg_write(port, TEGRA_CSI_PG_PHASE, 0x0);
+       csi2_tpg_write(port, TEGRA_CSI_PG_RED_FREQ,
+                      (0x10 << PG_RED_VERT_INIT_FREQ_OFFSET) |
+                      (0x10 << PG_RED_HOR_INIT_FREQ_OFFSET));
+       csi2_tpg_write(port, TEGRA_CSI_PG_RED_FREQ_RATE, 0x0);
+       csi2_tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ,
+                      (0x10 << PG_GREEN_VERT_INIT_FREQ_OFFSET) |
+                      (0x10 << PG_GREEN_HOR_INIT_FREQ_OFFSET));
+       csi2_tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ_RATE, 0x0);
+       csi2_tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ,
+                      (0x10 << PG_BLUE_VERT_INIT_FREQ_OFFSET) |
+                      (0x10 << PG_BLUE_HOR_INIT_FREQ_OFFSET));
+       csi2_tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ_RATE, 0x0);
+}
+
+void csi2_start_streaming(struct tegra_csi_device *csi,
+                         enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+
+       csi2_write(csi, TEGRA_CSI_CLKEN_OVERRIDE, 0, port_num >> 1);
+
+       /* Clean up status */
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, 0xFFFFFFFF);
+       csi2_cil_write(port, TEGRA_CSI_CIL_STATUS, 0xFFFFFFFF);
+       csi2_cil_write(port, TEGRA_CSI_CILX_STATUS, 0xFFFFFFFF);
+
+       csi2_cil_write(port, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0);
+
+       /* CIL PHY registers setup */
+       csi2_cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
+       csi2_cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL,
+                      BYPASS_LP_SEQ | 0xA);
+
+       /*
+        * The CSI unit provides for connection of up to six cameras in
+        * the system and is organized as three identical instances of
+        * two MIPI support blocks, each with a separate 4-lane
+        * interface that can be configured as a single camera with 4
+        * lanes or as a dual camera with 2 lanes available for each
+        * camera.
+        */
+       if (port->lanes == 4) {
+               int port_val = ((port_num >> 1) << 1);
+               struct tegra_csi_port *port_a = &csi->ports[port_val];
+               struct tegra_csi_port *port_b = &csi->ports[port_val + 1];
+
+               csi2_cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0,
+                              BRICK_CLOCK_A_4X);
+               csi2_cil_write(port_b, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
+               csi2_cil_write(port_b, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0);
+               csi2_cil_write(port_a, TEGRA_CSI_CIL_PHY_CONTROL,
+                              BYPASS_LP_SEQ | 0xA);
+               csi2_cil_write(port_b, TEGRA_CSI_CIL_PHY_CONTROL,
+                              BYPASS_LP_SEQ | 0xA);
+               csi2_write(csi, TEGRA_CSI_PHY_CIL_COMMAND,
+                          CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE,
+                          port_num >> 1);
+       } else {
+               u32 val = csi2_read(csi, TEGRA_CSI_PHY_CIL_COMMAND,
+                                       port_num >> 1);
+               int port_val = ((port_num >> 1) << 1);
+               struct tegra_csi_port *port_a = &csi->ports[port_val];
+
+               csi2_cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0);
+               val |= ((port->num & 0x1) == PORT_A) ? CSI_A_PHY_CIL_ENABLE :
+                       CSI_B_PHY_CIL_ENABLE;
+               csi2_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, val,
+                          port_num >> 1);
+       }
+
+       /* CSI pixel parser registers setup */
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
+                     (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
+                     CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_RST);
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0);
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL0,
+                     CSI_PP_PACKET_HEADER_SENT |
+                     CSI_PP_DATA_IDENTIFIER_ENABLE |
+                     CSI_PP_WORD_COUNT_SELECT_HEADER |
+                     CSI_PP_CRC_CHECK_ENABLE |  CSI_PP_WC_CHECK |
+                     CSI_PP_OUTPUT_FORMAT_STORE | CSI_PPA_PAD_LINE_NOPAD |
+                     CSI_PP_HEADER_EC_DISABLE | CSI_PPA_PAD_FRAME_NOPAD |
+                     (port->num & 1));
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL1,
+                     (0x1 << CSI_PP_TOP_FIELD_FRAME_OFFSET) |
+                     (0x1 << CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET));
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_GAP,
+                     0x14 << PP_FRAME_MIN_GAP_OFFSET);
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME, 0x0);
+       csi2_pp_write(port, TEGRA_CSI_INPUT_STREAM_CONTROL,
+                     (0x3f << CSI_SKIP_PACKET_THRESHOLD_OFFSET) |
+                     (port->lanes - 1));
+#if DEBUG
+       /* 0x454140E1 - register setting for line counter */
+       /* 0x454340E1 - tracks frame start, line starts, hpa headers */
+       csi2_pp_write(port, TEGRA_CSI_DEBUG_CONTROL, 0x454340E1);
+#endif
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
+                     (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
+                     CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_ENABLE);
+}
+
+int csi2_error(struct tegra_csi_device *csi,
+              enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+       u32 val;
+       int err = 0;
+
+       /*
+        * only uncorrectable header error and multi-bit
+        * transmission errors are checked as they cannot be
+        * corrected automatically
+       */
+       val = csi2_pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS);
+       err |= val & 0x4000;
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, val);
+
+       val = csi2_cil_read(port, TEGRA_CSI_CIL_STATUS);
+       err |= val & 0x02;
+       csi2_cil_write(port, TEGRA_CSI_CIL_STATUS, val);
+
+       val = csi2_cil_read(port, TEGRA_CSI_CILX_STATUS);
+       err |= val & 0x00020020;
+       csi2_cil_write(port, TEGRA_CSI_CILX_STATUS, val);
+
+       return err;
+}
+
+void csi2_status(struct tegra_csi_device *csi,
+                enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+       u32 val = csi2_pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS);
+
+       dev_dbg(csi->dev, "TEGRA_CSI_PIXEL_PARSER_STATUS 0x%08x\n",
+               val);
+
+       val = csi2_cil_read(port, TEGRA_CSI_CIL_STATUS);
+       dev_dbg(csi->dev, "TEGRA_CSI_CIL_STATUS 0x%08x\n", val);
+
+       val = csi2_cil_read(port, TEGRA_CSI_CILX_STATUS);
+       dev_dbg(csi->dev, "TEGRA_CSI_CILX_STATUS 0x%08x\n", val);
+
+#if DEBUG
+       val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_0);
+       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_0 0x%08x\n", val);
+       val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_1);
+       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_1 0x%08x\n", val);
+       val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_2);
+       dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_2 0x%08x\n", val);
+#endif
+}
+
+void csi2_error_recover(struct tegra_csi_device *csi,
+                       enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+
+       if (port->lanes == 4) {
+               int port_val = ((port_num >> 1) << 1);
+               struct tegra_csi_port *port_a = &csi->ports[port_val];
+               struct tegra_csi_port *port_b = &csi->ports[port_val+1];
+               csi2_tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_ENABLE);
+               csi2_tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_ENABLE);
+               csi2_cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
+               csi2_cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
+               csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1,
+                          port_num >> 1);
+               /* sleep for clock cycles to drain the Rx FIFO */
+               usleep_range(10, 20);
+               csi2_cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
+               csi2_cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
+               csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0,
+                          port_num >> 1);
+               csi2_tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_DISABLE);
+               csi2_tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_DISABLE);
+       } else {
+               csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_ENABLE);
+               csi2_cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1);
+               csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1,
+                          port_num >> 1);
+               /* sleep for clock cycles to drain the Rx FIFO */
+               usleep_range(10, 20);
+               csi2_cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0);
+               csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0,
+                          port_num >> 1);
+               csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_DISABLE);
+       }
+}
+
+void csi2_stop_streaming(struct tegra_csi_device *csi,
+                        enum tegra_csi_port_num port_num)
+{
+       struct tegra_csi_port *port = &csi->ports[port_num];
+
+       if (csi->pg_mode)
+               csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL,
+                              PG_DISABLE);
+
+       csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND,
+                     (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) |
+                     CSI_PP_DISABLE);
+}
diff --git a/drivers/media/platform/tegra/csi/csi2_fops.h b/drivers/media/platform/tegra/csi/csi2_fops.h
new file mode 100644 (file)
index 0000000..1ef0d76
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Tegra CSI2 device common APIs
+ *
+ * Tegra Graphics Host VI
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Bryan Wu <pengw@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CSI2_H__
+#define __CSI2_H__
+
+#include "csi.h"
+
+void csi2_tpg_start_streaming(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+void csi2_start_streaming(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+int csi2_error(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+void csi2_status(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+void csi2_error_recover(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+void csi2_stop_streaming(struct tegra_csi_device *csi,
+               enum tegra_csi_port_num port_num);
+
+struct tegra_csi_fops csi2_fops = {
+       .soc_tpg_start_streaming = csi2_tpg_start_streaming,
+       .soc_start_streaming = csi2_start_streaming,
+       .soc_error = csi2_error,
+       .soc_status = csi2_status,
+       .soc_error_recover = csi2_error_recover,
+       .soc_stop_streaming = csi2_stop_streaming,
+};
+
+#endif
index 1b607b36dd72d768deece50fdf2169eef90ac3ea..30a6da5109dd76719eb3c5453f1c2f59703d8a3e 100644 (file)
@@ -40,6 +40,7 @@ static int tpg_probe(struct vi *tegra_vi)
        /* Init CSI related media controller interface */
        csi->num_ports = TPG_CHANNELS;
        csi->pg_mode = TEGRA_VI_PG_PATCH;
+       csi->vi = tegra_vi;
        ret = tegra_csi_media_controller_init(csi, pdev);
        if (ret)
                return ret;
index d0e22bdb44ee9ecf5ed328df45b55195706977ff..d59e403822700b7e88997131d33ddc7b8e7735fb 100644 (file)
@@ -41,6 +41,7 @@
 #include "vi/vi.h"
 #include "vi/vi_irq.h"
 #include "camera/vi2_fops.h"
+#include "csi/csi2_fops.h"
 
 #ifdef CONFIG_ARCH_TEGRA_18x_SOC
 #include "t186/t186.h"
@@ -61,12 +62,14 @@ struct tegra_vi_data t124_vi_data = {
        .info = (struct nvhost_device_data *)&t124_vi_info,
        .vi_fops = &vi2_fops,
        .channel_fops = &vi2_channel_fops,
+       .csi_fops = &csi2_fops,
 };
 
 struct tegra_vi_data t210_vi_data = {
        .info = (struct nvhost_device_data *)&t21_vi_info,
        .vi_fops = &vi2_fops,
        .channel_fops = &vi2_channel_fops,
+       .csi_fops = &csi2_fops,
 };
 
 #ifdef CONFIG_ARCH_TEGRA_18x_SOC
@@ -312,7 +315,7 @@ static int vi_probe(struct platform_device *dev)
        tegra_vi->dev = &dev->dev;
 
        /* If missing SoC fops, whole VI/CSI has be in bypass mode */
-       if (!data->vi_fops || !data->channel_fops)
+       if (!data->vi_fops || !data->channel_fops || !data->csi_fops)
                tegra_vi->bypass = true;
 
        err = nvhost_client_device_get_resources(dev);
@@ -400,6 +403,7 @@ static int vi_probe(struct platform_device *dev)
                goto camera_unregister;
 
        tegra_vi->csi.vi = tegra_vi;
+       tegra_vi->csi.fops = tegra_vi->data->csi_fops;
        err = tegra_csi_init(&tegra_vi->csi, dev);
        if (err)
                goto vi_mc_init_error;
index 71e0aad22ee8ad75e5e4fc547ef2b1af41db8562..1d83b7166a515927590ba219d140495a5a3d7783 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "camera/mc_common.h"
 #include "chip_support.h"
+#include "csi/csi.h"
 
 #define VI_CFG_INTERRUPT_MASK_0                                0x8c
 #define VI_CFG_INTERRUPT_STATUS_0                      0x98
@@ -111,6 +112,7 @@ struct tegra_vi_data {
        struct nvhost_device_data *info;
        struct tegra_vi_fops *vi_fops;
        struct tegra_vi_channel_fops *channel_fops;
+       struct tegra_csi_fops *csi_fops;
 };
 
 struct vi {