From: Bryan Wu Date: Fri, 17 Jun 2016 20:29:48 +0000 (-0700) Subject: media: tegra_camera: add SoC fops for VI X-Git-Tag: tegra-l4t-r24.2~95 X-Git-Url: http://rtime.felk.cvut.cz/gitweb/sojka/nv-tegra/linux-3.10.git/commitdiff_plain/91557cf47b4027519986d5998bfaef3dee654c34 media: tegra_camera: add SoC fops for VI 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 Reviewed-on: http://git-master/r/1169882 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jihoon Bang --- diff --git a/drivers/media/platform/tegra/camera/Makefile b/drivers/media/platform/tegra/camera/Makefile index 136036064f9..bccce2af454 100644 --- a/drivers/media/platform/tegra/camera/Makefile +++ b/drivers/media/platform/tegra/camera/Makefile @@ -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 diff --git a/drivers/media/platform/tegra/camera/channel.c b/drivers/media/platform/tegra/camera/channel.c index c1835d66be9..e218165a978 100644 --- a/drivers/media/platform/tegra/camera/channel.c +++ b/drivers/media/platform/tegra/camera/channel.c @@ -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; diff --git a/drivers/media/platform/tegra/camera/mc_common.c b/drivers/media/platform/tegra/camera/mc_common.c index 9a03cd396cc..fe49559be44 100644 --- a/drivers/media/platform/tegra/camera/mc_common.c +++ b/drivers/media/platform/tegra/camera/mc_common.c @@ -27,11 +27,6 @@ #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); } diff --git a/drivers/media/platform/tegra/camera/mc_common.h b/drivers/media/platform/tegra/camera/mc_common.h index 2719652a121..b52f69a5c11 100644 --- a/drivers/media/platform/tegra/camera/mc_common.h +++ b/drivers/media/platform/tegra/camera/mc_common.h @@ -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 index 00000000000..1333de333db --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi2_fops.c @@ -0,0 +1,331 @@ +/* + * Tegra Video Input 2 device common APIs + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Author: Bryan Wu + * + * 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 +#include +#include + +#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 index 00000000000..345356975e6 --- /dev/null +++ b/drivers/media/platform/tegra/camera/vi2_fops.h @@ -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 + * + * 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 diff --git a/drivers/media/platform/tegra/tpg/tpg.c b/drivers/media/platform/tegra/tpg/tpg.c index 9e331d35408..1b607b36dd7 100644 --- a/drivers/media/platform/tegra/tpg/tpg.c +++ b/drivers/media/platform/tegra/tpg/tpg.c @@ -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; diff --git a/drivers/media/platform/tegra/vi/vi.c b/drivers/media/platform/tegra/vi/vi.c index 6e3924c3164..d0e22bdb44e 100644 --- a/drivers/media/platform/tegra/vi/vi.c +++ b/drivers/media/platform/tegra/vi/vi.c @@ -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; diff --git a/drivers/media/platform/tegra/vi/vi.h b/drivers/media/platform/tegra/vi/vi.h index f98b880a5d9..71e0aad22ee 100644 --- a/drivers/media/platform/tegra/vi/vi.h +++ b/drivers/media/platform/tegra/vi/vi.h @@ -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;