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
#include "nvhost_acm.h"
#include "mipi_cal.h"
-
#define FRAMERATE 30
#define BPP_MEM 2
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;
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)
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)
*/
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,
}
}
-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) {
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);
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 */
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;
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) {
/* 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);
}
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:
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;
#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)
{
}
}
- 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);
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);
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);
}
int requested_kbyteps;
unsigned long requested_hz;
int grp_id;
+
+ struct tegra_vi_channel_fops *fops;
};
#define to_tegra_channel(vdev) \
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,
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
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;
#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"
}
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
{ },
};
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);
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;
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;
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;
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;
bool master_deinitialized;
bool tpg_opened;
bool sensor_opened;
+ bool bypass;
};
extern const struct file_operations tegra_vi_ctrl_ops;