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

Bug 1779020

Change-Id: I29a8388f9343a1ffc895148b8ffb36c18d7f5c19
Signed-off-by: Bryan Wu <pengw@nvidia.com>
Reviewed-on: http://git-master/r/1169882
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Jihoon Bang <jbang@nvidia.com>
drivers/media/platform/tegra/camera/Makefile
drivers/media/platform/tegra/camera/channel.c
drivers/media/platform/tegra/camera/mc_common.c
drivers/media/platform/tegra/camera/mc_common.h
drivers/media/platform/tegra/camera/vi2_fops.c [new file with mode: 0644]
drivers/media/platform/tegra/camera/vi2_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 136036064f95b203807f99c6591d5354d7f2740f..bccce2af4544904a4ad22d9db3c1dbb3f08eaecf 100644 (file)
@@ -3,4 +3,4 @@ ccflags-y += -Idrivers/video/tegra/host
 ccflags-y += -Idrivers/media/platform/tegra
 ccflags-y += -Werror
 ccflags-y += -Idrivers/media/platform/tegra/mipical
-obj-y += camera_common.o mc_common.o core.o channel.o graph.o
+obj-y += camera_common.o mc_common.o core.o channel.o graph.o vi2_fops.o
index c1835d66be92d7e06d94c7e891c3a3c9d90c6b92..e218165a978bb6191a124ab1b2c840615e965c81 100644 (file)
@@ -42,7 +42,6 @@
 #include "nvhost_acm.h"
 #include "mipi_cal.h"
 
-
 #define FRAMERATE      30
 #define BPP_MEM                2
 
@@ -53,31 +52,6 @@ static void tegra_channel_stop_kthreads(struct tegra_channel *chan);
 static int tegra_channel_set_stream(struct tegra_channel *chan, bool on);
 static int tegra_channel_mipi_cal(struct tegra_channel *chan, char is_bypass);
 
-u32 tegra_channel_read(struct tegra_channel *chan,
-                       unsigned int addr)
-{
-       return readl(chan->vi->iomem + addr);
-}
-
-void tegra_channel_write(struct tegra_channel *chan,
-                       unsigned int addr, u32 val)
-{
-       writel(val, chan->vi->iomem + addr);
-}
-
-/* CSI registers */
-static void csi_write(struct tegra_channel *chan, unsigned int index,
-                       unsigned int addr, u32 val)
-{
-       writel(val, chan->csibase[index] + addr);
-}
-
-static u32 csi_read(struct tegra_channel *chan, unsigned int index,
-                                       unsigned int addr)
-{
-       return readl(chan->csibase[index] + addr);
-}
-
 static void gang_buffer_offsets(struct tegra_channel *chan)
 {
        int i;
@@ -275,37 +249,7 @@ static void tegra_channel_fmts_bitmap_init(struct tegra_channel *chan)
 
 static int tegra_channel_capture_setup(struct tegra_channel *chan)
 {
-       u32 height = chan->format.height;
-       u32 width = chan->format.width;
-       u32 format = chan->fmtinfo->img_fmt;
-       u32 data_type = chan->fmtinfo->img_dt;
-       u32 word_count = tegra_core_get_word_count(width, chan->fmtinfo);
-       u32 bypass_pixel_transform = 1;
-       int index;
-
-       if (chan->valid_ports > 1) {
-               height = chan->gang_height;
-               width = chan->gang_width;
-               word_count = tegra_core_get_word_count(width, chan->fmtinfo);
-       }
-
-       if (chan->vi->pg_mode ||
-          (chan->fmtinfo->vf_code == TEGRA_VF_YUV422) ||
-          (chan->fmtinfo->vf_code == TEGRA_VF_RGB888))
-               bypass_pixel_transform = 0;
-
-       for (index = 0; index < chan->valid_ports; index++) {
-               csi_write(chan, index, TEGRA_VI_CSI_ERROR_STATUS, 0xFFFFFFFF);
-               csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DEF,
-                 (bypass_pixel_transform << BYPASS_PXL_TRANSFORM_OFFSET) |
-                 (format << IMAGE_DEF_FORMAT_OFFSET));
-               csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DT, data_type);
-               csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE_WC, word_count);
-               csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE,
-                         (height << IMAGE_SIZE_HEIGHT_OFFSET) | width);
-       }
-
-       return 0;
+       return chan->fops->soc_channel_capture_setup(chan);
 }
 
 static int tegra_channel_enable_stream(struct tegra_channel *chan)
@@ -342,34 +286,7 @@ static int tegra_channel_enable_stream(struct tegra_channel *chan)
 
 static int tegra_channel_error_status(struct tegra_channel *chan)
 {
-       u32 val;
-       int err = 0;
-       int index = 0;
-
-       for (index = 0; index < chan->valid_ports; index++) {
-               val = csi_read(chan, index, TEGRA_VI_CSI_ERROR_STATUS);
-               csi_write(chan, index, TEGRA_VI_CSI_ERROR_STATUS, val);
-               err |= val;
-               err |= tegra_csi_error(chan->vi->csi, chan->port[index]);
-       }
-
-       if (err)
-               dev_err(chan->vi->dev, "%s:error %x frame %d\n",
-                               __func__, err, chan->sequence);
-       return err;
-}
-
-static void tegra_channel_capture_error(struct tegra_channel *chan)
-{
-       u32 val;
-       int index = 0;
-
-       for (index = 0; index < chan->valid_ports; index++) {
-               val = csi_read(chan, index, TEGRA_VI_CSI_ERROR_STATUS);
-               dev_dbg(&chan->video.dev,
-                       "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val);
-               tegra_csi_status(chan->vi->csi, chan->port[index]);
-       }
+       return chan->fops->soc_channel_error_status(chan);
 }
 
 static void tegra_channel_init_ring_buffer(struct tegra_channel *chan)
@@ -490,54 +407,19 @@ static void tegra_channel_ec_init(struct tegra_channel *chan)
         */
        chan->timeout = 20;
 
-       /*
-        * Sync point FIFO full blocks host interface
-        * Below setting enables SW to process error recovery
-        */
-       tegra_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_CNTRL, 0x100);
+       chan->fops->soc_channel_ec_init(chan);
 }
 
-static void tegra_channel_clear_singleshot(struct tegra_channel *chan,
-                                               int index)
-{
-       /* clear single shot */
-       csi_write(chan, index, TEGRA_VI_CSI_SW_RESET, 0xF);
-       csi_write(chan, index, TEGRA_VI_CSI_SW_RESET, 0x0);
-}
-
-static void tegra_channel_vi_csi_recover(struct tegra_channel *chan)
+static void tegra_channel_ec_recover(struct tegra_channel *chan)
 {
-       u32 error_val = tegra_channel_read(chan,
-                                       TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR);
-       u32 frame_start;
        int index, valid_ports = chan->valid_ports;
 
        /* Disable pad power to start recovery */
        tegra_csi_pad_control(chan->vi->csi, chan->port, DISABLE);
-       /* Disable clock gating to enable continuous clock */
-       tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE);
-       /* clear CSI state */
-       for (index = 0; index < valid_ports; index++) {
-               tegra_csi_error_recover(chan->vi->csi, chan->port[index]);
-               csi_write(chan, index,
-                               TEGRA_VI_CSI_IMAGE_DEF, 0);
-               tegra_channel_clear_singleshot(chan, index);
-       }
 
-       /* clear VI errors */
-       for (index = 0; index < valid_ports; index++) {
-               frame_start = VI_CSI_PP_FRAME_START(chan->port[index]);
-               if (error_val & frame_start)
-                       chan->syncpoint_fifo[index] = SYNCPT_FIFO_DEPTH;
-       }
-       /* clear FIFO error status */
-       tegra_channel_write(chan,
-               TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, error_val);
-
-       /* Enable clock gating so VI can be clock gated if necessary */
-       tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE);
+       chan->fops->soc_channel_ec_recover(chan);
 
-       /* re-init VI and CSI */
+       /* Re-init VI and CSI */
        tegra_channel_capture_setup(chan);
        for (index = 0; index < valid_ports; index++) {
                tegra_csi_stop_streaming(chan->vi->csi,
@@ -549,49 +431,18 @@ static void tegra_channel_vi_csi_recover(struct tegra_channel *chan)
        }
 }
 
-static void tegra_channel_ec_recover(struct tegra_channel *chan)
-{
-       tegra_channel_capture_error(chan);
-       tegra_channel_vi_csi_recover(chan);
-}
-
 static int tegra_channel_capture_frame(struct tegra_channel *chan,
                                       struct tegra_channel_buffer *buf)
 {
        struct vb2_buffer *vb = &buf->buf;
        struct timespec ts;
-       int err = 0;
-       u32 val, frame_start;
-       int bytes_per_line = chan->format.bytesperline;
-       int index = 0;
        u32 thresh[TEGRA_CSI_BLOCKS] = { 0 };
-       int valid_ports = chan->valid_ports;
+       int err = 0;
        int state = VB2_BUF_STATE_DONE;
 
-       for (index = 0; index < valid_ports; index++) {
-               /* Program buffer address by using surface 0 */
-               csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0);
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SURFACE0_OFFSET_LSB,
-                       (buf->addr + chan->buffer_offset[index]));
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
-
-               /* Program syncpoints */
-               thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev,
-                                       chan->syncpt[index], 1);
-               /* Do not arm sync points if FIFO had entries before */
-               if (!chan->syncpoint_fifo[index]) {
-                       frame_start = VI_CSI_PP_FRAME_START(chan->port[index]);
-                       val = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) |
-                               chan->syncpt[index];
-                       tegra_channel_write(chan,
-                               TEGRA_VI_CFG_VI_INCR_SYNCPT, val);
-               } else
-                       chan->syncpoint_fifo[index]--;
-       }
+       /* Init registers related to each frames */
+       chan->fops->soc_channel_capture_frame_init(chan, buf, thresh);
 
-       /* enable input stream once the VI registers are configured */
        if (!chan->bfirst_fstart) {
                err = tegra_channel_enable_stream(chan);
                if (err) {
@@ -600,36 +451,17 @@ static int tegra_channel_capture_frame(struct tegra_channel *chan,
                        tegra_channel_ring_buffer(chan, vb, &ts, state);
                        return err;
                }
-               /* Bit controls VI memory write, enable after all regs */
-               for (index = 0; index < valid_ports; index++) {
-                       val = csi_read(chan, index, TEGRA_VI_CSI_IMAGE_DEF);
-                       csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DEF,
-                                       val | IMAGE_DEF_DEST_MEM);
-               }
-       }
-
-       /* Ensure all CSI ports are ready with setup to avoid timing issue */
-       for (index = 0; index < valid_ports; index++)
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
-
-       chan->capture_state = CAPTURE_GOOD;
-       for (index = 0; index < valid_ports; index++) {
-               err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev,
-                       chan->syncpt[index], thresh[index],
-                       chan->timeout, NULL, &ts);
-               if (err) {
-                       dev_err(&chan->video.dev,
-                               "frame start syncpt timeout!%d\n", index);
-                       state = VB2_BUF_STATE_ERROR;
-                       /* perform error recovery for timeout */
-                       tegra_channel_ec_recover(chan);
-                       chan->capture_state = CAPTURE_TIMEOUT;
-                       break;
-               }
+               /* Enable input stream once the VI registers are configured */
+               chan->fops->soc_channel_capture_frame_enable(chan);
        }
 
-       if (!err && !chan->vi->pg_mode) {
+       /* Arm capture and wait for notifier or syncpoint */
+       err = chan->fops->soc_channel_capture_frame(chan, &ts, thresh);
+       if (err) {
+               state = VB2_BUF_STATE_ERROR;
+               /* perform error recovery for timeout */
+               tegra_channel_ec_recover(chan);
+       } else if (!chan->vi->pg_mode) {
                /* Marking error frames and resume capture */
                /* TODO: TPG has frame height short error always set */
                err = tegra_channel_error_status(chan);
@@ -669,12 +501,8 @@ done:
 static void tegra_channel_capture_done(struct tegra_channel *chan)
 {
        struct timespec ts;
-       int index, err;
-       int bytes_per_line = chan->format.bytesperline;
-       u32 val, mw_ack_done;
-       u32 thresh[TEGRA_CSI_BLOCKS] = { 0 };
+       int err;
        struct tegra_channel_buffer *buf;
-       int valid_ports = chan->valid_ports;
        int state = VB2_BUF_STATE_DONE;
 
        /* dequeue buffer and return if no buffer exists */
@@ -682,41 +510,14 @@ static void tegra_channel_capture_done(struct tegra_channel *chan)
        if (!buf)
                return;
 
-       for (index = 0; index < valid_ports; index++) {
-               /* Program buffer address by using surface 0 */
-               csi_write(chan, index, TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0);
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SURFACE0_OFFSET_LSB,
-                       (buf->addr + chan->buffer_offset[index]));
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
-
-               /* Program syncpoints */
-               thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev,
-                                       chan->syncpt[index], 1);
-               mw_ack_done = VI_CSI_MW_ACK_DONE(chan->port[index]);
-               val = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) |
-                               chan->syncpt[index];
-               tegra_channel_write(chan,
-                               TEGRA_VI_CFG_VI_INCR_SYNCPT, val);
-               csi_write(chan, index,
-                       TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
+       err = chan->fops->soc_channel_capture_done(chan, buf, &ts);
+       if (err) {
+               state = VB2_BUF_STATE_ERROR;
+               /* perform error recovery for timeout */
+               tegra_channel_ec_recover(chan);
+               chan->capture_state = CAPTURE_TIMEOUT;
        }
 
-       for (index = 0; index < chan->valid_ports; index++) {
-               err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev,
-                       chan->syncpt[index], thresh[index],
-                       chan->timeout, NULL, &ts);
-               if (err) {
-                       dev_err(&chan->video.dev,
-                               "MW_ACK_DONE syncpoint time out!%d\n", index);
-                       state = VB2_BUF_STATE_ERROR;
-                       /* perform error recovery for timeout */
-                       tegra_channel_ec_recover(chan);
-                       chan->capture_state = CAPTURE_TIMEOUT;
-                       break;
-               }
-       }
        /* Mark capture state to IDLE as capture is finished */
        chan->capture_state = CAPTURE_IDLE;
 
@@ -1123,7 +924,6 @@ error_pipeline_start:
 static int tegra_channel_stop_streaming(struct vb2_queue *vq)
 {
        struct tegra_channel *chan = vb2_get_drv_priv(vq);
-       int index;
        bool is_streaming = atomic_read(&chan->is_streaming);
 
        if (!chan->bypass) {
@@ -1136,17 +936,8 @@ static int tegra_channel_stop_streaming(struct vb2_queue *vq)
                /* dequeue buffers back to app which are in capture queue */
                tegra_channel_queued_buf_done(chan, VB2_BUF_STATE_ERROR);
 
-               /* Disable clock gating to enable continuous clock */
-               tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE);
-               for (index = 0; index < chan->valid_ports; index++) {
-                       tegra_csi_stop_streaming(chan->vi->csi,
-                                               chan->port[index]);
-                       /* Always clear single shot if armed at close */
-                       if (csi_read(chan, index, TEGRA_VI_CSI_SINGLE_SHOT))
-                               tegra_channel_clear_singleshot(chan, index);
-               }
-               /* Enable clock gating so VI can be clock gated if necessary */
-               tegra_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE);
+               chan->fops->soc_channel_stop_streaming(chan);
+
                tegra_csi_pad_control(chan->vi->csi, chan->port, DISABLE);
        }
 
@@ -1349,7 +1140,13 @@ static int tegra_channel_s_ctrl(struct v4l2_ctrl *ctrl)
        case V4L2_CID_VI_BYPASS_MODE:
                if (switch_ctrl_qmenu[ctrl->val] == SWITCH_ON)
                        chan->bypass = true;
-               else
+               else if (chan->vi->vi->bypass) {
+                       dev_dbg(&chan->video.dev,
+                               "can't disable bypass mode\n");
+                       dev_dbg(&chan->video.dev,
+                               "because the VI/CSI is in bypass mode\n");
+                       chan->bypass = true;
+               } else
                        chan->bypass = false;
                break;
        default:
@@ -1923,7 +1720,12 @@ static int tegra_channel_init(struct tegra_mc_vi *vi, unsigned int index)
        int ret;
        struct tegra_channel *chan = &vi->chans[index];
 
+       /* VI/CSI is in bypass mode, then channel has to be in bypass */
+       if (vi->vi->bypass)
+               chan->bypass = true;
+
        chan->vi = vi;
+       chan->fops = vi->vi->data->channel_fops;
        tegra_channel_csi_init(vi, index);
 
        chan->width_align = TEGRA_WIDTH_ALIGNMENT;
index 9a03cd396cc25868039d8494983a6807b819f949..fe49559be44d2a6465c200e8b56d1b77d8c2ea82 100644 (file)
 #include "vi/vi.h"
 #include "camera/registers.h"
 
-void vi_write(struct tegra_mc_vi *vi, unsigned int addr, u32 val)
-{
-       writel(val, vi->iomem + addr);
-}
-
 /* In TPG mode, VI only support 2 formats */
 static void vi_tpg_fmts_bitmap_init(struct tegra_mc_vi *vi)
 {
@@ -68,14 +63,9 @@ int tegra_vi_power_on(struct tegra_mc_vi *vi)
                }
        }
 
-       vi_write(vi, TEGRA_VI_CFG_CG_CTRL, 1);
-
-       /* unpowergate VE */
-       ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
-       if (ret) {
-               dev_err(vi->dev, "failed to unpower gate VI\n");
-               goto error_unpowergate;
-       }
+       ret = vi->fops->soc_power_on(vi);
+       if (ret)
+               goto error_soc_power_on;
 
        /* clock settings */
        clk_prepare_enable(vi->clk);
@@ -93,8 +83,8 @@ int tegra_vi_power_on(struct tegra_mc_vi *vi)
 err_emc_enable:
        clk_disable_unprepare(vi->clk);
 error_clk_set_rate:
-       tegra_powergate_partition(TEGRA_POWERGATE_VENC);
-error_unpowergate:
+       vi->fops->soc_power_off(vi);
+error_soc_power_on:
        regulator_disable(vi->reg);
 error_regulator_fail:
        nvhost_module_idle_ext(vi->ndev);
@@ -110,7 +100,7 @@ void tegra_vi_power_off(struct tegra_mc_vi *vi)
        tegra_channel_ec_close(vi);
        tegra_camera_emc_clk_disable();
        clk_disable_unprepare(vi->clk);
-       tegra_powergate_partition(TEGRA_POWERGATE_VENC);
+       vi->fops->soc_power_off(vi);
        regulator_disable(vi->reg);
        nvhost_module_idle_ext(vi->ndev);
 }
index 2719652a121643652bea0b9c7c4735808722624e..b52f69a5c11d3a35aa1b79caca852fa3c8ae60d4 100644 (file)
@@ -174,6 +174,8 @@ struct tegra_channel {
        int requested_kbyteps;
        unsigned long requested_hz;
        int grp_id;
+
+       struct tegra_vi_channel_fops *fops;
 };
 
 #define to_tegra_channel(vdev) \
@@ -242,6 +244,8 @@ struct tegra_mc_vi {
        struct mutex mipical_lock;
        unsigned int link_status;
        unsigned int subdevs_bound;
+
+       struct tegra_vi_fops *fops;
 };
 
 int tegra_vi_get_port_info(struct tegra_channel *chan,
diff --git a/drivers/media/platform/tegra/camera/vi2_fops.c b/drivers/media/platform/tegra/camera/vi2_fops.c
new file mode 100644 (file)
index 0000000..1333de3
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Tegra Video Input 2 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 <linux/nvhost.h>
+#include <linux/tegra-powergate.h>
+
+#include "camera/mc_common.h"
+
+/* VI2 register accessors */
+void vi2_write(struct tegra_mc_vi *vi, unsigned int addr, u32 val)
+{
+       writel(val, vi->iomem + addr);
+}
+
+u32 vi2_channel_read(struct tegra_channel *chan,
+                       unsigned int addr)
+{
+       return readl(chan->vi->iomem + addr);
+}
+
+void vi2_channel_write(struct tegra_channel *chan,
+                       unsigned int addr, u32 val)
+{
+       writel(val, chan->vi->iomem + addr);
+}
+
+static void vi2_channel_csi_write(struct tegra_channel *chan,
+                                  unsigned int index, unsigned int addr,
+                                  u32 val)
+{
+       writel(val, chan->csibase[index] + addr);
+}
+
+static u32 vi2_channel_csi_read(struct tegra_channel *chan, unsigned int index,
+                                       unsigned int addr)
+{
+       return readl(chan->csibase[index] + addr);
+}
+
+/* VI2 Media-Controller releated fops */
+int vi2_power_on(struct tegra_mc_vi *vi)
+{
+       int ret;
+
+       vi2_write(vi, TEGRA_VI_CFG_CG_CTRL, 1);
+
+       /* unpowergate VE */
+       ret = tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
+       if (ret)
+               dev_err(vi->dev, "failed to unpower gate VI\n");
+
+       return ret;
+}
+
+void vi2_power_off(struct tegra_mc_vi *vi)
+{
+       tegra_powergate_partition(TEGRA_POWERGATE_VENC);
+}
+
+/* VI2 channel operation related fops */
+void vi2_channel_ec_init(struct tegra_channel *chan)
+{
+       /*
+        * Sync point FIFO full blocks host interface
+        * Below setting enables SW to process error recovery
+        */
+       vi2_channel_write(chan, TEGRA_VI_CFG_VI_INCR_SYNCPT_CNTRL, 0x100);
+}
+
+int vi2_channel_capture_setup(struct tegra_channel *chan)
+{
+       u32 height = chan->format.height;
+       u32 width = chan->format.width;
+       u32 format = chan->fmtinfo->img_fmt;
+       u32 data_type = chan->fmtinfo->img_dt;
+       u32 word_count = tegra_core_get_word_count(width, chan->fmtinfo);
+       u32 bypass_pixel_transform = 1;
+       int index;
+
+       if (chan->valid_ports > 1) {
+               height = chan->gang_height;
+               width = chan->gang_width;
+               word_count = tegra_core_get_word_count(width, chan->fmtinfo);
+       }
+
+       if (chan->vi->pg_mode ||
+          (chan->fmtinfo->vf_code == TEGRA_VF_YUV422) ||
+          (chan->fmtinfo->vf_code == TEGRA_VF_RGB888))
+               bypass_pixel_transform = 0;
+
+       for (index = 0; index < chan->valid_ports; index++) {
+               vi2_channel_csi_write(chan, index, TEGRA_VI_CSI_ERROR_STATUS,
+                                      0xFFFFFFFF);
+               vi2_channel_csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DEF,
+                 (bypass_pixel_transform << BYPASS_PXL_TRANSFORM_OFFSET) |
+                 (format << IMAGE_DEF_FORMAT_OFFSET));
+               vi2_channel_csi_write(chan, index, TEGRA_VI_CSI_IMAGE_DT,
+                                      data_type);
+               vi2_channel_csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE_WC,
+                                      word_count);
+               vi2_channel_csi_write(chan, index, TEGRA_VI_CSI_IMAGE_SIZE,
+                         (height << IMAGE_SIZE_HEIGHT_OFFSET) | width);
+       }
+
+       return 0;
+}
+
+void vi2_channel_capture_frame_init(struct tegra_channel *chan,
+                                    struct tegra_channel_buffer *buf,
+                                    u32 *thresh)
+{
+       int index = 0;
+       int valid_ports = chan->valid_ports;
+       int bytes_per_line = chan->format.bytesperline;
+       u32 val, frame_start;
+
+       for (index = 0; index < valid_ports; index++) {
+               /* Program buffer address by using surface 0 */
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0);
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SURFACE0_OFFSET_LSB,
+                       (buf->addr + chan->buffer_offset[index]));
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
+
+               /* Program syncpoints */
+               thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev,
+                                       chan->syncpt[index], 1);
+               /* Do not arm sync points if FIFO had entries before */
+               if (!chan->syncpoint_fifo[index]) {
+                       frame_start = VI_CSI_PP_FRAME_START(chan->port[index]);
+                       val = VI_CFG_VI_INCR_SYNCPT_COND(frame_start) |
+                               chan->syncpt[index];
+                       vi2_channel_write(chan,
+                               TEGRA_VI_CFG_VI_INCR_SYNCPT, val);
+               } else
+                       chan->syncpoint_fifo[index]--;
+       }
+}
+
+void vi2_channel_capture_frame_enable(struct tegra_channel *chan)
+{
+       int index;
+       u32 val;
+       int valid_ports = chan->valid_ports;
+
+       /* Bit controls VI memory write, enable after all regs */
+       for (index = 0; index < valid_ports; index++) {
+               val = vi2_channel_csi_read(chan, index,
+                               TEGRA_VI_CSI_IMAGE_DEF);
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_IMAGE_DEF,
+                               val | IMAGE_DEF_DEST_MEM);
+       }
+}
+
+int vi2_channel_capture_frame(struct tegra_channel *chan,
+                              struct timespec *ts, u32 *thresh)
+{
+
+       int index = 0;
+       int valid_ports = chan->valid_ports;
+       int err = 0;
+
+       /* Ensure all CSI ports are ready with setup to avoid timing issue */
+       for (index = 0; index < valid_ports; index++)
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
+
+       chan->capture_state = CAPTURE_GOOD;
+       for (index = 0; index < valid_ports; index++) {
+               err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev,
+                       chan->syncpt[index], thresh[index],
+                       chan->timeout, NULL, ts);
+               if (err) {
+                       dev_err(&chan->video.dev,
+                               "frame start syncpt timeout!%d\n", index);
+                       chan->capture_state = CAPTURE_TIMEOUT;
+                       return err;
+               }
+       }
+
+       return err;
+}
+
+int vi2_channel_error_status(struct tegra_channel *chan)
+{
+       u32 val;
+       int err = 0;
+       int index = 0;
+
+       for (index = 0; index < chan->valid_ports; index++) {
+               val = vi2_channel_csi_read(chan, index,
+                               TEGRA_VI_CSI_ERROR_STATUS);
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_ERROR_STATUS, val);
+               err |= val;
+               err |= tegra_csi_error(chan->vi->csi, chan->port[index]);
+       }
+
+       if (err)
+               dev_err(chan->vi->dev, "%s:error %x frame %d\n",
+                               __func__, err, chan->sequence);
+       return err;
+}
+
+int vi2_channel_capture_done(struct tegra_channel *chan,
+                             struct tegra_channel_buffer *buf,
+                             struct timespec *ts)
+{
+       int index, err;
+       int bytes_per_line = chan->format.bytesperline;
+       u32 val, mw_ack_done;
+       u32 thresh[TEGRA_CSI_BLOCKS] = { 0 };
+
+       for (index = 0; index < chan->valid_ports; index++) {
+               /* Program buffer address by using surface 0 */
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_SURFACE0_OFFSET_MSB, 0x0);
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SURFACE0_OFFSET_LSB,
+                       (buf->addr + chan->buffer_offset[index]));
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SURFACE0_STRIDE, bytes_per_line);
+
+               /* Program syncpoints */
+               thresh[index] = nvhost_syncpt_incr_max_ext(chan->vi->ndev,
+                                       chan->syncpt[index], 1);
+               mw_ack_done = VI_CSI_MW_ACK_DONE(chan->port[index]);
+               val = VI_CFG_VI_INCR_SYNCPT_COND(mw_ack_done) |
+                               chan->syncpt[index];
+               vi2_channel_write(chan,
+                               TEGRA_VI_CFG_VI_INCR_SYNCPT, val);
+               vi2_channel_csi_write(chan, index,
+                       TEGRA_VI_CSI_SINGLE_SHOT, SINGLE_SHOT_CAPTURE);
+       }
+
+       for (index = 0; index < chan->valid_ports; index++) {
+               err = nvhost_syncpt_wait_timeout_ext(chan->vi->ndev,
+                       chan->syncpt[index], thresh[index],
+                       chan->timeout, NULL, ts);
+               if (err) {
+                       dev_err(&chan->video.dev,
+                               "MW_ACK_DONE syncpoint time out!%d\n", index);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+void vi2_channel_ec_recover(struct tegra_channel *chan)
+{
+       u32 error_val = vi2_channel_read(chan,
+                               TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR);
+       int index, valid_ports = chan->valid_ports;
+       u32 frame_start, val;
+
+       /* Get error status for debugging */
+       for (index = 0; index < valid_ports; index++) {
+               val = vi2_channel_csi_read(chan, index,
+                               TEGRA_VI_CSI_ERROR_STATUS);
+               dev_dbg(&chan->video.dev,
+                       "TEGRA_VI_CSI_ERROR_STATUS 0x%08x\n", val);
+               tegra_csi_status(chan->vi->csi, chan->port[index]);
+       }
+
+       /* Disable clock gating to enable continuous clock */
+       vi2_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE);
+
+       /* Clear CSI state */
+       for (index = 0; index < valid_ports; index++) {
+               tegra_csi_error_recover(chan->vi->csi, chan->port[index]);
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_IMAGE_DEF, 0);
+               /* Clear single shot */
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_SW_RESET, 0xF);
+               vi2_channel_csi_write(chan, index,
+                               TEGRA_VI_CSI_SW_RESET, 0x0);
+       }
+
+       /* Clear VI errors */
+       for (index = 0; index < valid_ports; index++) {
+               frame_start = VI_CSI_PP_FRAME_START(chan->port[index]);
+               if (error_val & frame_start)
+                       chan->syncpoint_fifo[index] = SYNCPT_FIFO_DEPTH;
+       }
+
+       /* Clear FIFO error status */
+       vi2_channel_write(chan,
+               TEGRA_VI_CFG_VI_INCR_SYNCPT_ERROR, error_val);
+
+       /* Enable clock gating so VI can be clock gated if necessary */
+       vi2_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE);
+}
+
+void vi2_channel_stop_streaming(struct tegra_channel *chan)
+{
+       int index;
+
+       /* Disable clock gating to enable continuous clock */
+       vi2_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, DISABLE);
+
+       for (index = 0; index < chan->valid_ports; index++) {
+               tegra_csi_stop_streaming(chan->vi->csi,
+                               chan->port[index]);
+               /* Always clear single shot if armed at close */
+               if (vi2_channel_csi_read(chan, index,
+                                       TEGRA_VI_CSI_SINGLE_SHOT)) {
+                       vi2_channel_csi_write(chan, index,
+                                       TEGRA_VI_CSI_SW_RESET, 0xF);
+                       vi2_channel_csi_write(chan, index,
+                                       TEGRA_VI_CSI_SW_RESET, 0x0);
+               }
+       }
+
+       /* Enable clock gating so VI can be clock gated if necessary */
+       vi2_channel_write(chan, TEGRA_VI_CFG_CG_CTRL, ENABLE);
+}
diff --git a/drivers/media/platform/tegra/camera/vi2_fops.h b/drivers/media/platform/tegra/camera/vi2_fops.h
new file mode 100644 (file)
index 0000000..3453569
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Tegra Video Input 2 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 __T210_VI_H__
+#define __T210_VI_H__
+
+#include "vi/vi.h"
+
+int vi2_power_on(struct tegra_mc_vi *vi);
+void vi2_power_off(struct tegra_mc_vi *vi);
+void vi2_channel_ec_init(struct tegra_channel *chan);
+void vi2_channel_ec_recover(struct tegra_channel *chan);
+int vi2_channel_capture_setup(struct tegra_channel *chan);
+void vi2_channel_capture_frame_init(struct tegra_channel *chan,
+               struct tegra_channel_buffer *buf, u32 *thresh);
+void vi2_channel_capture_frame_enable(struct tegra_channel *chan);
+int vi2_channel_capture_frame(struct tegra_channel *chan,
+               struct timespec *ts, u32 *thresh);
+int vi2_channel_capture_done(struct tegra_channel *chan,
+               struct tegra_channel_buffer *buf,
+               struct timespec *ts);
+int vi2_channel_error_status(struct tegra_channel *chan);
+int vi2_channel_stop_streaming(struct tegra_channel *chan);
+
+struct tegra_vi_fops vi2_fops = {
+       .soc_power_on = vi2_power_on,
+       .soc_power_off = vi2_power_off,
+};
+
+struct tegra_vi_channel_fops vi2_channel_fops = {
+       .soc_channel_ec_init = vi2_channel_ec_init,
+       .soc_channel_ec_recover = vi2_channel_ec_recover,
+       .soc_channel_capture_setup = vi2_channel_capture_setup,
+       .soc_channel_capture_frame_init = vi2_channel_capture_frame_init,
+       .soc_channel_capture_frame_enable = vi2_channel_capture_frame_enable,
+       .soc_channel_capture_frame = vi2_channel_capture_frame,
+       .soc_channel_capture_done = vi2_channel_capture_done,
+       .soc_channel_error_status = vi2_channel_error_status,
+       .soc_channel_stop_streaming = vi2_channel_stop_streaming,
+};
+
+
+#endif
index 9e331d354082c0e78c91d4f216d5b190f1a9322a..1b607b36dd72d768deece50fdf2169eef90ac3ea 100644 (file)
@@ -50,6 +50,7 @@ static int tpg_probe(struct vi *tegra_vi)
        mc_vi->reg = tegra_vi->reg;
        mc_vi->pg_mode = TEGRA_VI_PG_PATCH;
        mc_vi->num_channels = TPG_CHANNELS;
+       mc_vi->fops = tegra_vi->data->vi_fops;
        ret = tegra_vi_media_controller_init(mc_vi, pdev);
        if (ret)
                goto vi_mc_error;
index 6e3924c3164c20d9b6b191abaa13c273c6b3ff1e..d0e22bdb44ee9ecf5ed328df45b55195706977ff 100644 (file)
@@ -40,6 +40,7 @@
 #include "t210/t210.h"
 #include "vi/vi.h"
 #include "vi/vi_irq.h"
+#include "camera/vi2_fops.h"
 
 #ifdef CONFIG_ARCH_TEGRA_18x_SOC
 #include "t186/t186.h"
@@ -56,19 +57,38 @@ struct vi *tegra_vi_get(void)
 }
 EXPORT_SYMBOL(tegra_vi_get);
 
+struct tegra_vi_data t124_vi_data = {
+       .info = (struct nvhost_device_data *)&t124_vi_info,
+       .vi_fops = &vi2_fops,
+       .channel_fops = &vi2_channel_fops,
+};
 
-static struct of_device_id tegra_vi_of_match[] = {
-#ifdef TEGRA_12X_OR_HIGHER_CONFIG
-       { .compatible = "nvidia,tegra124-vi",
-               .data = (struct nvhost_device_data *)&t124_vi_info },
-#endif
-#ifdef TEGRA_21X_OR_HIGHER_CONFIG
-       { .compatible = "nvidia,tegra210-vi",
-               .data = (struct nvhost_device_data *)&t21_vi_info },
+struct tegra_vi_data t210_vi_data = {
+       .info = (struct nvhost_device_data *)&t21_vi_info,
+       .vi_fops = &vi2_fops,
+       .channel_fops = &vi2_channel_fops,
+};
+
+#ifdef CONFIG_ARCH_TEGRA_18x_SOC
+struct tegra_vi_data t186_vi_data = {
+       .info = (struct nvhost_device_data *)&t18_vi_info,
+};
 #endif
+
+static struct of_device_id tegra_vi_of_match[] = {
+       {
+               .compatible = "nvidia,tegra124-vi",
+               .data = &t124_vi_data
+       },
+       {
+               .compatible = "nvidia,tegra210-vi",
+               .data = &t210_vi_data
+       },
 #ifdef CONFIG_ARCH_TEGRA_18x_SOC
-       { .compatible = "nvidia,tegra186-vi",
-               .data = (struct nvhost_device_data *)&t18_vi_info },
+       {
+               .compatible = "nvidia,tegra186-vi",
+               .data = &t186_vi_data
+       },
 #endif
        { },
 };
@@ -250,29 +270,26 @@ static int nvhost_vi_slcg_handler(struct notifier_block *nb,
 static int vi_probe(struct platform_device *dev)
 {
        int err = 0;
+       struct tegra_vi_data *data = NULL;
        struct nvhost_device_data *pdata = NULL;
 
        if (dev->dev.of_node) {
                const struct of_device_id *match;
 
                match = of_match_device(tegra_vi_of_match, &dev->dev);
-               if (match) {
-                       pdata = (struct nvhost_device_data *)match->data;
-                       dev->dev.platform_data = pdata;
-               }
+               if (match)
+                       data = (struct tegra_vi_data *) match->data;
+
                /* DT initializes it to -1, use below WAR to set correct value.
                 * TODO: Once proper fix for dev-id goes in, remove it.
                 */
                dev->id = dev->dev.id;
-       } else
-               pdata = (struct nvhost_device_data *)dev->dev.platform_data;
-
-       WARN_ON(!pdata);
-       if (!pdata) {
-               dev_info(&dev->dev, "no platform data\n");
-               return -ENODATA;
        }
 
+       BUG_ON(!data || !data->info);
+       dev->dev.platform_data = data->info;
+       pdata = data->info;
+
        err = nvhost_check_bondout(pdata->bond_out_id);
        if (err) {
                dev_warn(&dev->dev, "No VI unit present. err:%d", err);
@@ -291,7 +308,13 @@ static int vi_probe(struct platform_device *dev)
                return -ENOMEM;
        }
        tegra_vi->ndev = dev;
+       tegra_vi->data = data;
        tegra_vi->dev = &dev->dev;
+
+       /* If missing SoC fops, whole VI/CSI has be in bypass mode */
+       if (!data->vi_fops || !data->channel_fops)
+               tegra_vi->bypass = true;
+
        err = nvhost_client_device_get_resources(dev);
        if (err)
                goto vi_probe_fail;
@@ -376,6 +399,7 @@ static int vi_probe(struct platform_device *dev)
        if (err)
                goto camera_unregister;
 
+       tegra_vi->csi.vi = tegra_vi;
        err = tegra_csi_init(&tegra_vi->csi, dev);
        if (err)
                goto vi_mc_init_error;
@@ -383,6 +407,7 @@ static int vi_probe(struct platform_device *dev)
        tegra_vi->mc_vi.vi = tegra_vi;
        tegra_vi->mc_vi.csi = &tegra_vi->csi;
        tegra_vi->mc_vi.reg = tegra_vi->reg;
+       tegra_vi->mc_vi.fops = tegra_vi->data->vi_fops;
        err = tegra_vi_media_controller_init(&tegra_vi->mc_vi, dev);
        if (err)
                goto vi_mc_init_error;
index f98b880a5d9aa23adb31103f496ffefcfc215dff..71e0aad22ee8ad75e5e4fc547ef2b1af41db8562 100644 (file)
@@ -86,11 +86,38 @@ struct tegra_vi_stats {
        atomic_t overflow;
 };
 
+struct tegra_vi_fops {
+       int (*soc_power_on)(struct tegra_mc_vi *vi);
+       void (*soc_power_off)(struct tegra_mc_vi *vi);
+};
+
+struct tegra_vi_channel_fops {
+       void (*soc_channel_ec_init)(struct tegra_channel *chan);
+       void (*soc_channel_ec_recover)(struct tegra_channel *chan);
+       int (*soc_channel_capture_setup)(struct tegra_channel *chan);
+       void (*soc_channel_capture_frame_init)(struct tegra_channel *chan,
+                       struct tegra_channel_buffer *buf, u32 *thresh);
+       void (*soc_channel_capture_frame_enable)(struct tegra_channel *chan);
+       int (*soc_channel_capture_frame)(struct tegra_channel *chan,
+                       struct timespec *ts, u32 *thresh);
+       int (*soc_channel_capture_done)(struct tegra_channel *chan,
+                       struct tegra_channel_buffer *buf,
+                       struct timespec *ts);
+       int (*soc_channel_error_status)(struct tegra_channel *chan);
+       int (*soc_channel_stop_streaming)(struct tegra_channel *chan);
+};
+
+struct tegra_vi_data {
+       struct nvhost_device_data *info;
+       struct tegra_vi_fops *vi_fops;
+       struct tegra_vi_channel_fops *channel_fops;
+};
+
 struct vi {
        struct tegra_camera *camera;
        struct platform_device *ndev;
        struct device *dev;
-       struct nvhost_device_data *ndata;
+       struct tegra_vi_data *data;
        struct tegra_mc_vi mc_vi;
        struct tegra_csi_device csi;
 
@@ -110,6 +137,7 @@ struct vi {
        bool master_deinitialized;
        bool tpg_opened;
        bool sensor_opened;
+       bool bypass;
 };
 
 extern const struct file_operations tegra_vi_ctrl_ops;