diff options
Diffstat (limited to 'drivers/media/video/tegra_v4l2_camera.c')
-rw-r--r-- | drivers/media/video/tegra_v4l2_camera.c | 1198 |
1 files changed, 519 insertions, 679 deletions
diff --git a/drivers/media/video/tegra_v4l2_camera.c b/drivers/media/video/tegra_v4l2_camera.c index 8f71d1702537..14a74cd6b7cc 100644 --- a/drivers/media/video/tegra_v4l2_camera.c +++ b/drivers/media/video/tegra_v4l2_camera.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -14,7 +14,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/delay.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -23,13 +22,14 @@ #include <linux/nvhost.h> #include <mach/iomap.h> -#include <mach/powergate.h> #include <media/soc_camera.h> #include <media/soc_mediabus.h> #include <media/videobuf2-dma-nvmap.h> #include <media/tegra_v4l2_camera.h> +#include <mach/powergate.h> + #include "dev.h" #include "bus_client.h" #include "host1x/host1x_syncpt.h" @@ -37,18 +37,22 @@ #define TEGRA_CAM_DRV_NAME "vi" #define TEGRA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5) -#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 200 +static unsigned int internal_sync = 0; +module_param(internal_sync, int, 0644); +MODULE_PARM_DESC(internal_sync, "enable internal vsync and hsync decoded " \ + "from data"); + +#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 25 #define TEGRA_SYNCPT_CSI_WAIT_TIMEOUT 200 #define TEGRA_SYNCPT_RETRY_COUNT 10 -#define TEGRA_VIP_H_ACTIVE_START 0x98 -#define TEGRA_VIP_V_ACTIVE_START 0x10 +#define TEGRA_VIP_H_ACTIVE_START 0x8F //0x98 +#define TEGRA_VIP_V_ACTIVE_START 0x12 //0x10 /* SYNCPTs 12-17 are reserved for VI. */ #define TEGRA_VI_SYNCPT_VI NVSYNCPT_VI_ISP_2 -#define TEGRA_VI_SYNCPT_CSI_A NVSYNCPT_VI_ISP_3 -#define TEGRA_VI_SYNCPT_CSI_B NVSYNCPT_VI_ISP_4 +#define TEGRA_VI_SYNCPT_CSI NVSYNCPT_VI_ISP_3 /* Tegra CSI-MIPI registers. */ #define TEGRA_VI_OUT_1_INCR_SYNCPT 0x0000 @@ -235,7 +239,10 @@ #define TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME 0x08c8 #define TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME 0x08cc #define TEGRA_CSI_DSI_MIPI_CAL_CONFIG 0x08d0 -#define TEGRA_CSI_MIPIBIAS_PAD_CONFIG0 0x08d4 + +#define IS_INTERLACED ((pcdev->field == V4L2_FIELD_INTERLACED)\ + || (pcdev->field == V4L2_FIELD_INTERLACED_BT)\ + || (pcdev->field == V4L2_FIELD_INTERLACED_TB)) #define TC_VI_REG_RD(DEV, REG) readl(DEV->vi_base + REG) #define TC_VI_REG_WT(DEV, REG, VAL) writel(VAL, DEV->vi_base + REG) @@ -256,8 +263,6 @@ struct tegra_buffer { struct vb2_buffer vb; /* v4l buffer must be first */ struct list_head queue; - struct soc_camera_device *icd; - int output_channel; /* * Various buffer addresses shadowed so we don't have to recalculate @@ -269,21 +274,20 @@ struct tegra_buffer { dma_addr_t start_addr; dma_addr_t start_addr_u; dma_addr_t start_addr_v; + void* virtual_addr; }; struct tegra_camera_dev { struct soc_camera_host ici; + struct soc_camera_device *icd; struct nvhost_device *ndev; + struct tegra_camera_platform_data *pdata; struct clk *clk_vi; struct clk *clk_vi_sensor; struct clk *clk_csi; struct clk *clk_isp; struct clk *clk_csus; - struct clk *clk_sclk; - struct clk *clk_emc; - - struct regulator *reg; void __iomem *vi_base; spinlock_t videobuf_queue_lock; @@ -291,22 +295,16 @@ struct tegra_camera_dev { struct vb2_buffer *active; struct vb2_alloc_ctx *alloc_ctx; enum v4l2_field field; - int sequence_a; - int sequence_b; + int sequence; struct work_struct work; struct mutex work_mutex; u32 syncpt_vi; - u32 syncpt_csi_a; - u32 syncpt_csi_b; + u32 syncpt_csi; /* Debug */ int num_frames; - int enable_refcnt; - - /* CSI pad calibration flag */ - int cal_done; }; static const struct soc_mbus_pixelfmt tegra_camera_formats[] = { @@ -352,23 +350,6 @@ static const struct soc_mbus_pixelfmt tegra_camera_formats[] = { .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, }, - - /* For RAW8 and RAW10 output, we always output 16-bit (2 bytes). */ - { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .name = "Bayer 8 BGBG.. GRGR..", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - }, - { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .name = "Bayer 10 BGBG.. GRGR..", - .bits_per_sample = 16, - .packing = SOC_MBUS_PACKING_EXTEND16, - .order = SOC_MBUS_ORDER_LE, - }, - }; static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb) @@ -378,13 +359,9 @@ static struct tegra_buffer *to_tegra_vb(struct vb2_buffer *vb) static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev) { - pcdev->syncpt_csi_a = + pcdev->syncpt_csi = nvhost_syncpt_read_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_A); - - pcdev->syncpt_csi_b = - nvhost_syncpt_read_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_B); + TEGRA_VI_SYNCPT_CSI); pcdev->syncpt_vi = nvhost_syncpt_read_ext(pcdev->ndev, @@ -394,65 +371,60 @@ static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev) static void tegra_camera_incr_syncpts(struct tegra_camera_dev *pcdev) { nvhost_syncpt_cpu_incr_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_A); - - nvhost_syncpt_cpu_incr_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_B); + TEGRA_VI_SYNCPT_CSI); nvhost_syncpt_cpu_incr_ext(pcdev->ndev, TEGRA_VI_SYNCPT_VI); } -static void tegra_camera_capture_clean(struct tegra_camera_dev *pcdev) +static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev, + int input_format, + int yuv_input_format) { - TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000); + struct soc_camera_device *icd = pcdev->icd; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_INTERRUPT_MASK, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_READONLY_STATUS, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_ESCAPE_MODE_COMMAND, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_ESCAPE_MODE_DATA, 0x0); + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x02000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, + (yuv_input_format << 8) | + (input_format << 2)); - TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_CONTROL, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_0, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_1, 0x0); - TC_VI_REG_WT(pcdev, TEGRA_CSI_DEBUG_COUNTER_2, 0x0); -} + TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000004); + TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000004); -static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev, - struct soc_camera_device *icd, - u32 hdr) -{ - struct tegra_camera_platform_data *pdata = icd->link->priv; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); + /* CSI-A H_ACTIVE and V_ACTIVE */ + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_H_ACTIVE, + (icd->user_width << 16)); + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_V_ACTIVE, + (icd->user_height << 16)); + /* CSI A */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_GAP, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG0, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_PAD_CONFIG1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME, 0x0); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x02000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000); - /* CSI-A H_ACTIVE and V_ACTIVE */ - TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_H_ACTIVE, - (icd->user_width << 16)); - TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPA_V_ACTIVE, - (icd->user_height << 16)); + /* pad1s enabled, virtual channel ID 00 */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, + (0x1 << 16) | /* Output 1 pixel per clock */ + (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */ + (0x1 << 7) | /* Check header CRC */ + (0x1 << 6) | /* Use word count field in the header */ + (0x1 << 5) | /* Look at data identifier byte in hdr */ + (0x1 << 4)); /* Expect packet header */ TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1, 0x1); /* Frame # for top field detect for interlaced */ @@ -466,27 +438,22 @@ static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev, (0x100 << 4) | /* Wait 0x100 vi clks for timeout */ 0x1); /* Enable line timeout */ - /* pad 0s enabled, virtual channel ID 00 */ - TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL0, - (0x1 << 16) | /* Output 1 pixel per clock */ - (hdr << 8) | /* If hdr shows wrong fmt, use right value */ - (0x1 << 7) | /* Check header CRC */ - (0x1 << 6) | /* Use word count field in the header */ - (0x1 << 5) | /* Look at data identifier byte in hdr */ - (0x1 << 4)); /* Expect packet header */ - TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_A_CONTROL, (0x3f << 16) | /* Skip packet threshold */ - (pdata->lanes - 1)); + (pcdev->pdata->lanes - 1)); /* Use 0x00000022 for continuous clock mode. */ TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0, - (pdata->continuous_clk << 5) | + (pcdev->pdata->continuous_clk << 5) | 0x5); /* Clock settle time */ TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_END, (0x1 << 8) | /* Enable continuous syncpt */ - TEGRA_VI_SYNCPT_CSI_A); + TEGRA_VI_SYNCPT_CSI); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_VI); TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00020001); @@ -494,37 +461,49 @@ static void tegra_camera_capture_setup_csi_a(struct tegra_camera_dev *pcdev, } static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev, - struct soc_camera_device *icd, - u32 hdr) + int input_format, + int yuv_input_format) { - struct tegra_camera_platform_data *pdata = icd->link->priv; + struct soc_camera_device *icd = pcdev->icd; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x04000000); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, + (yuv_input_format << 8) | + (input_format << 2)); + + TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000008); + TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000008); + + /* CSI-B H_ACTIVE and V_ACTIVE */ + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_H_ACTIVE, + (icd->user_width << 16)); + TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_V_ACTIVE, + (icd->user_height << 16)); + + /* CSI B */ + TC_VI_REG_WT(pcdev, TEGRA_CSI_VI_INPUT_STREAM_CONTROL, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_HOST_INPUT_STREAM_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL1, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_GAP, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG0, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_PAD_CONFIG1, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_PAD_CONFIG, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME, 0x0); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x04000000); - - /* CSI-B H_ACTIVE and V_ACTIVE */ - TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_H_ACTIVE, - (icd->user_width << 16)); - TC_VI_REG_WT(pcdev, TEGRA_VI_CSI_PPB_V_ACTIVE, - (icd->user_height << 16)); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CIL_MIPI_CAL_STATUS, 0x00000000); + TC_VI_REG_WT(pcdev, TEGRA_CSI_CLKEN_OVERRIDE, 0x00000000); - /* pad 0s enabled, virtual channel ID 00 */ + /* pad1s enabled, virtual channel ID 00 */ TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL0, (0x1 << 16) | /* Output 1 pixel per clock */ - (hdr << 8) | /* If hdr shows wrong fmt, use right value */ + (0x1e << 8) | /* If hdr shows wrong fmt, use YUV422 */ (0x1 << 7) | /* Check header CRC */ (0x1 << 6) | /* Use word count field in the header */ (0x1 << 5) | /* Look at data identifier byte in hdr */ @@ -545,16 +524,20 @@ static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev, TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL, (0x3f << 16) | /* Skip packet threshold */ - (pdata->lanes - 1)); + (pcdev->pdata->lanes - 1)); /* Use 0x00000022 for continuous clock mode. */ TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0, - (pdata->continuous_clk << 5) | + (pcdev->pdata->continuous_clk << 5) | 0x5); /* Clock settle time */ TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_END, (0x1 << 8) | /* Enable continuous syncpt */ - TEGRA_VI_SYNCPT_CSI_B); + TEGRA_VI_SYNCPT_CSI); + + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, + (0x1 << 8) | /* Enable continuous syncpt */ + TEGRA_VI_SYNCPT_VI); TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00010002); @@ -562,17 +545,22 @@ static void tegra_camera_capture_setup_csi_b(struct tegra_camera_dev *pcdev, } static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev, - struct soc_camera_device *icd, - u32 input_control) + int input_format, + int yuv_input_format) { + struct soc_camera_device *icd = pcdev->icd; TC_VI_REG_WT(pcdev, TEGRA_VI_VI_CORE_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_VI_VI_INPUT_CONTROL, - (1 << 27) | /* field detect */ - (1 << 25) | /* hsync/vsync decoded from data (BT.656) */ +// (1 << 27) | /* field detect */ + (0 << 28) | /* 1 == top field is even field, 00 == odd */ + ((internal_sync == 1) << 25) | /* 1 == hsync/vsync decoded + internally from data + (BT.656) */ + (yuv_input_format << 8) | (1 << 1) | /* VIP_INPUT_ENABLE */ - input_control); + (input_format << 2)); TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000000); TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000000); @@ -580,180 +568,80 @@ static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev, /* VIP H_ACTIVE and V_ACTIVE */ TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_H_ACTIVE, (icd->user_width << 16) | - TEGRA_VIP_H_ACTIVE_START); + (TEGRA_VIP_H_ACTIVE_START - ((internal_sync == 1)?1:0))); TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_V_ACTIVE, - (icd->user_height << 16) | + ( ( IS_INTERLACED ? (icd->user_height/2) : (icd->user_height) ) << 16) | TEGRA_VIP_V_ACTIVE_START); /* * For VIP, D9..D2 is mapped to the video decoder's P7..P0. * Disable/mask out the other Dn wires. */ - TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INPUT_ENABLE, 0x000003fc); + TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INPUT_ENABLE, 0x000003fc | 0x6000); // D2..D9 + VSYNC + HSYNC TC_VI_REG_WT(pcdev, TEGRA_VI_VI_DATA_INPUT_CONTROL, 0x000003fc); TC_VI_REG_WT(pcdev, TEGRA_VI_PIN_INVERSION, 0x00000000); - TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_VIP_VSYNC, + TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, (0x1 << 8) | /* Enable continuous syncpt */ TEGRA_VI_SYNCPT_VI); - TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004); +// TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, 0x00000004); } -static int tegra_camera_capture_output_channel_setup( - struct tegra_camera_dev *pcdev, - struct soc_camera_device *icd) +static void tegra_camera_capture_setup(struct tegra_camera_dev *pcdev) { - struct tegra_camera_platform_data *pdata = icd->link->priv; - int port = pdata->port; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); + struct soc_camera_device *icd = pcdev->icd; const struct soc_camera_format_xlate *current_fmt = icd->current_fmt; + enum v4l2_mbus_pixelcode input_code = current_fmt->code; u32 output_fourcc = current_fmt->host_fmt->fourcc; - u32 output_format, output_control; - struct tegra_buffer *buf = to_tegra_vb(pcdev->active); + int yuv_input_format = 0x0; + int input_format = 0x0; /* Default to YUV422 */ + int yuv_output_format = 0x0; + int output_format = 0x3; /* Default to YUV422 */ + int port = pcdev->pdata->port; + int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + int frame_count = 1; + + switch (input_code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + yuv_input_format = 0x2; + break; + case V4L2_MBUS_FMT_VYUY8_2X8: + yuv_input_format = 0x3; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + yuv_input_format = 0x0; + break; + case V4L2_MBUS_FMT_YVYU8_2X8: + yuv_input_format = 0x1; + break; + default: + BUG_ON(1); + } switch (output_fourcc) { case V4L2_PIX_FMT_UYVY: - output_format = 0x3; /* Default to YUV422 */ + yuv_output_format = 0x0; break; case V4L2_PIX_FMT_VYUY: - output_format = (0x1 << 17) | 0x3; + yuv_output_format = 0x1; break; case V4L2_PIX_FMT_YUYV: - output_format = (0x2 << 17) | 0x3; + yuv_output_format = 0x2; break; case V4L2_PIX_FMT_YVYU: - output_format = (0x3 << 17) | 0x3; + yuv_output_format = 0x3; break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: output_format = 0x6; /* YUV420 planar */ break; - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR10: - /* Use second output channel for RAW8/RAW10 */ - buf->output_channel = 1; - - if (port == TEGRA_CAMERA_PORT_CSI_A) - output_format = 0x7; - else if (port == TEGRA_CAMERA_PORT_CSI_B) - output_format = 0x8; - else - output_format = 0x9; - break; default: - dev_err(&pcdev->ndev->dev, "Wrong output format %d\n", - output_fourcc); - return -EINVAL; + BUG_ON(1); } - output_control = (pdata->flip_v ? (0x1 << 20) : 0) | - (pdata->flip_h ? (0x1 << 19) : 0) | - output_format; - - if (buf->output_channel == 0) { - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL, - output_control); - /* - * Set up frame size. Bits 31:16 are the number of lines, and - * bits 15:0 are the number of pixels per line. - */ - TC_VI_REG_WT(pcdev, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE, - (icd->user_height << 16) | icd->user_width); - - /* First output memory enabled */ - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000); - - /* Set the number of frames in the buffer. */ - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_FIRST, 0x00000001); - - /* Set up buffer frame size. */ - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_FIRST, - (icd->user_height << 16) | icd->user_width); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, - (icd->user_height * bytes_per_line)); - - TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_1, - (0x1 << 8) | /* Enable continuous syncpt */ - TEGRA_VI_SYNCPT_VI); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000); - } else if (buf->output_channel == 1) { - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_SECOND_OUTPUT_CONTROL, - output_control); - - TC_VI_REG_WT(pcdev, TEGRA_VI_SECOND_OUTPUT_FRAME_SIZE, - (icd->user_height << 16) | icd->user_width); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE_2, 0x00000000); - - /* Set the number of frames in the buffer. */ - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_SECOND, 0x00000001); - - /* Set up buffer frame size. */ - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_SECOND, - (icd->user_height << 16) | icd->user_width); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_SECOND, - (icd->user_height * bytes_per_line)); - - TC_VI_REG_WT(pcdev, TEGRA_VI_CONT_SYNCPT_OUT_2, - (0x1 << 8) | /* Enable continuous syncpt */ - TEGRA_VI_SYNCPT_VI); - - TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE_2, 0x00000000); - } else { - dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n", - buf->output_channel); - return -EINVAL; - } - - return 0; -} - -static int tegra_camera_capture_setup(struct tegra_camera_dev *pcdev) -{ - struct vb2_buffer *vb = pcdev->active; - struct tegra_buffer *buf = to_tegra_vb(vb); - struct soc_camera_device *icd = buf->icd; - struct tegra_camera_platform_data *pdata = icd->link->priv; - int port = pdata->port; - const struct soc_camera_format_xlate *current_fmt = icd->current_fmt; - enum v4l2_mbus_pixelcode input_code = current_fmt->code; - u32 hdr, input_control = 0x0; - - switch (input_code) { - case V4L2_MBUS_FMT_UYVY8_2X8: - input_control |= 0x2 << 8; - hdr = 30; - break; - case V4L2_MBUS_FMT_VYUY8_2X8: - input_control |= 0x3 << 8; - hdr = 30; - break; - case V4L2_MBUS_FMT_YUYV8_2X8: - input_control |= 0x0; - hdr = 30; - break; - case V4L2_MBUS_FMT_YVYU8_2X8: - input_control |= 0x1 << 8; - hdr = 30; - break; - case V4L2_MBUS_FMT_SBGGR8_1X8: - input_control |= 0x2 << 2; /* Input Format = Bayer */ - hdr = 42; - break; - case V4L2_MBUS_FMT_SBGGR10_1X10: - input_control |= 0x2 << 2; /* Input Format = Bayer */ - hdr = 43; - break; - default: - dev_err(&pcdev->ndev->dev, "Input format %d is not supported\n", - input_code); - return -EINVAL; - } + BUG_ON(!tegra_camera_port_is_valid(port)); /* * Set up low pass filter. Use 0x240 for chromaticity and 0x240 @@ -765,25 +653,98 @@ static int tegra_camera_capture_setup(struct tegra_camera_dev *pcdev) /* Set up raise-on-edge, so we get an interrupt on end of frame. */ TC_VI_REG_WT(pcdev, TEGRA_VI_VI_RAISE, 0x00000001); - /* Cleanup registers */ - tegra_camera_capture_clean(pcdev); - - /* Setup registers for CSI-A, CSI-B and VIP inputs */ if (port == TEGRA_CAMERA_PORT_CSI_A) - tegra_camera_capture_setup_csi_a(pcdev, icd, hdr); + tegra_camera_capture_setup_csi_a(pcdev, input_format, + yuv_input_format); else if (port == TEGRA_CAMERA_PORT_CSI_B) - tegra_camera_capture_setup_csi_b(pcdev, icd, hdr); + tegra_camera_capture_setup_csi_b(pcdev, input_format, + yuv_input_format); else - tegra_camera_capture_setup_vip(pcdev, icd, input_control); + tegra_camera_capture_setup_vip(pcdev, input_format, + yuv_input_format); + + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_FIRST_OUTPUT_CONTROL, + (pcdev->pdata->flip_v ? (0x1 << 20) : 0) | + (pcdev->pdata->flip_h ? (0x1 << 19) : 0) | + (yuv_output_format << 17) | + output_format); + + // if the video is interlaced, then take two frames + frame_count = IS_INTERLACED ? 2 : 1; + + /* + * Set up frame size. Bits 31:16 are the number of lines, and + * bits 15:0 are the number of pixels per line. + */ + TC_VI_REG_WT(pcdev, TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE, + ((icd->user_height/frame_count) << 16) | icd->user_width); + + /* Set the number of frames in the buffer. */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_COUNT_FIRST, frame_count); + + /* Set up buffer frame size. */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_SIZE_FIRST, + ((icd->user_height/frame_count) << 16) | icd->user_width); + if(output_fourcc == V4L2_PIX_FMT_YUV420 || output_fourcc == V4L2_PIX_FMT_YVU420) { + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, + ((icd->user_height/frame_count) * icd->user_width) | (2<<30)); + } + else { + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BUFFER_STRIDE_FIRST, + ((icd->user_height/frame_count) * bytes_per_line) | (2<<30)); + } + + /* First output memory enabled */ + TC_VI_REG_WT(pcdev, TEGRA_VI_VI_ENABLE, 0x00000000); - /* Setup registers for output channels */ - return tegra_camera_capture_output_channel_setup(pcdev, icd); } -static int tegra_camera_capture_buffer_setup(struct tegra_camera_dev *pcdev, - struct tegra_buffer *buf) +// +// make_interlaced +// +// make one interlaced frame out of two frames in the buffer @ addr +// this function must be issued for Y, U and V buffers. +// +static void make_interlaced(uint8_t * addr, int width, int height) { + uint8_t starting_line_buf[width]; + bool lines[height]; + int i; + int destination_line, starting_line; + for (i = 1; i < height-1; i++) lines[i] = false; + lines[0] = true; + lines[height-1] = true; + + #define real_line(i) ( (i % 2) ? ((i / 2) + (height/2)) : (i / 2) ) + while (1) { + starting_line = -1; + for (i = 1; i < ((height)-1); i++) if (!lines[i]) { + starting_line = i; + break; + } + if (starting_line == -1) break; // all is done + + memcpy(starting_line_buf, (void*) ((unsigned int)(addr) + (width*starting_line)), width); + destination_line = starting_line; + while (1) { + lines[destination_line] = true; + if (real_line(destination_line) == starting_line) { + memcpy(addr + width * destination_line, starting_line_buf, width); + break; + } + memcpy(addr + (width * destination_line), (void*) ((unsigned int)(addr) + (width * real_line(destination_line))), width); + destination_line = real_line(destination_line); + } + } +} + +static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, + struct tegra_buffer *buf) { - struct soc_camera_device *icd = buf->icd; + struct soc_camera_device *icd = pcdev->icd; + int port = pcdev->pdata->port; + int err; + int bytes_per_line; + uint8_t *src; switch (icd->current_fmt->host_fmt->fourcc) { case V4L2_PIX_FMT_YUV420: @@ -802,90 +763,87 @@ static int tegra_camera_capture_buffer_setup(struct tegra_camera_dev *pcdev, case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR10: - /* output 1 */ - if (buf->output_channel == 0) { - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, - buf->buffer_addr); - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_FIRST, - buf->start_addr); - /* output 2 */ - } else if (buf->output_channel == 1) { - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_SECOND, - buf->buffer_addr); - TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_SECOND, - buf->start_addr); - } else { - dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n", - buf->output_channel); - return -EINVAL; - } - break; + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST, + buf->buffer_addr); + TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_FIRST, + buf->start_addr); + + break; default: - dev_err(&pcdev->ndev->dev, "Wrong host format %d\n", - icd->current_fmt->host_fmt->fourcc); - return -EINVAL; + BUG_ON(1); } - return 0; -} + BUG_ON(!tegra_camera_port_is_valid(port)); -static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, - struct tegra_buffer *buf) -{ - struct soc_camera_device *icd = buf->icd; - struct tegra_camera_platform_data *pdata = icd->link->priv; - int port = pdata->port; - int err; + if (port == TEGRA_CAMERA_PORT_CSI_A) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, + 0x0000f005); + else if (port == TEGRA_CAMERA_PORT_CSI_B) + TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, + 0x0000f005); + else + TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, + 0x00000005); // start & stop - err = tegra_camera_capture_buffer_setup(pcdev, buf); - if (err < 0) - return err; /* * Only wait on CSI frame end syncpt if we're using CSI. Otherwise, * wait on VIP VSYNC syncpt. */ - if (port == TEGRA_CAMERA_PORT_CSI_A) { - pcdev->syncpt_csi_a++; - TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, - 0x0000f005); - err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_A, - pcdev->syncpt_csi_a, - TEGRA_SYNCPT_CSI_WAIT_TIMEOUT, - NULL); - } else if (port == TEGRA_CAMERA_PORT_CSI_B) { - pcdev->syncpt_csi_b++; - TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, - 0x0000f005); + + pcdev->syncpt_vi++; + pcdev->syncpt_csi++; + + if (tegra_camera_port_is_csi(port)) err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_CSI_B, - pcdev->syncpt_csi_b, - TEGRA_SYNCPT_CSI_WAIT_TIMEOUT, - NULL); - } else { - pcdev->syncpt_vi++; - TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, - 0x00000001); + TEGRA_VI_SYNCPT_CSI, + pcdev->syncpt_csi, + TEGRA_SYNCPT_CSI_WAIT_TIMEOUT, + NULL); + else err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, - TEGRA_VI_SYNCPT_VI, - pcdev->syncpt_csi_a, - TEGRA_SYNCPT_VI_WAIT_TIMEOUT, - NULL); - } + TEGRA_VI_SYNCPT_VI, + pcdev->syncpt_vi, + TEGRA_SYNCPT_VI_WAIT_TIMEOUT, + NULL); - if (!err) - return 0; + if (IS_INTERLACED) { + + TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, + 0x00000005); // start & stop + + pcdev->syncpt_vi++; + pcdev->syncpt_csi++; + + err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, + TEGRA_VI_SYNCPT_VI, + pcdev->syncpt_vi, + TEGRA_SYNCPT_VI_WAIT_TIMEOUT, + NULL); + + src = (uint8_t*)(buf->virtual_addr); + if(icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YUV420 || icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420 ) { + make_interlaced(src, icd->user_width, icd->user_height); // Y + make_interlaced(src + icd->user_width * icd->user_height, icd->user_width, icd->user_height/4); // U + make_interlaced(src + icd->user_width * icd->user_height + (icd->user_width * icd->user_height)/4, icd->user_width, icd->user_height/4); // V + } + else { + bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, + icd->current_fmt->host_fmt); + make_interlaced(src, bytes_per_line, icd->user_height); // Y + } + } + + if (!err) + return 0; if (tegra_camera_port_is_csi(port)) { u32 ppstatus; u32 cilstatus; u32 rostatus; - dev_warn(&icd->vdev->dev, "Timeout on CSI syncpt\n"); - dev_warn(&icd->vdev->dev, "buffer_addr = 0x%08x\n", + dev_err(&pcdev->ndev->dev, "Timeout on CSI syncpt\n"); + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", buf->buffer_addr); ppstatus = TC_VI_REG_RD(pcdev, @@ -895,7 +853,7 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, rostatus = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_READONLY_STATUS); - dev_warn(&icd->vdev->dev, + dev_err(&pcdev->ndev->dev, "PPSTATUS = 0x%08x, " "CILSTATUS = 0x%08x, " "ROSTATUS = 0x%08x\n", @@ -903,14 +861,14 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, } else { u32 vip_input_status; - dev_warn(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); - dev_warn(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", + dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", buf->buffer_addr); vip_input_status = TC_VI_REG_RD(pcdev, TEGRA_VI_VIP_INPUT_STATUS); - dev_warn(&pcdev->ndev->dev, + dev_err(&pcdev->ndev->dev, "VIP_INPUT_STATUS = 0x%08x\n", vip_input_status); } @@ -918,10 +876,12 @@ static int tegra_camera_capture_start(struct tegra_camera_dev *pcdev, return err; } -static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port) +static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev) { + int port = pcdev->pdata->port; int err; - struct tegra_buffer *buf = to_tegra_vb(pcdev->active); + + BUG_ON(!tegra_camera_port_is_valid(port)); if (port == TEGRA_CAMERA_PORT_CSI_A) TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, @@ -929,9 +889,9 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port) else if (port == TEGRA_CAMERA_PORT_CSI_B) TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x0000f002); - else - TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, - 0x00000005); +// else +// TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL, +// 0x00000005); if (tegra_camera_port_is_csi(port)) err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev, @@ -947,28 +907,17 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port) u32 ppstatus; u32 cilstatus; - dev_warn(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); - - if (buf->output_channel == 0) - buffer_addr = TC_VI_REG_RD(pcdev, + dev_err(&pcdev->ndev->dev, "Timeout on VI syncpt\n"); + buffer_addr = TC_VI_REG_RD(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_FIRST); - else if (buf->output_channel == 1) - buffer_addr = TC_VI_REG_RD(pcdev, - TEGRA_VI_VB0_BASE_ADDRESS_SECOND); - else { - dev_err(&pcdev->ndev->dev, "Wrong output channel %d\n", - buf->output_channel); - return -EINVAL; - } - - dev_warn(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", + dev_err(&pcdev->ndev->dev, "buffer_addr = 0x%08x\n", buffer_addr); ppstatus = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); cilstatus = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS); - dev_warn(&pcdev->ndev->dev, + dev_err(&pcdev->ndev->dev, "PPSTATUS = 0x%08x, CILSTATUS = 0x%08x\n", ppstatus, cilstatus); } @@ -976,132 +925,81 @@ static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port) return err; } -static void tegra_camera_activate(struct tegra_camera_dev *pcdev) -{ - nvhost_module_busy_ext(pcdev->ndev); - - /* Enable external power */ - regulator_enable(pcdev->reg); - - /* Unpowergate VE */ - tegra_unpowergate_partition(TEGRA_POWERGATE_VENC); - - /* Turn on relevant clocks. */ - clk_set_rate(pcdev->clk_vi, 150000000); - clk_enable(pcdev->clk_vi); - clk_set_rate(pcdev->clk_vi_sensor, 24000000); - clk_enable(pcdev->clk_vi_sensor); - clk_enable(pcdev->clk_csi); - clk_enable(pcdev->clk_isp); - clk_enable(pcdev->clk_csus); - clk_set_rate(pcdev->clk_sclk, 80000000); - clk_enable(pcdev->clk_sclk); - clk_set_rate(pcdev->clk_sclk, 375000000); - clk_enable(pcdev->clk_emc); - - /* Save current syncpt values. */ - tegra_camera_save_syncpts(pcdev); -} - -static void tegra_camera_deactivate(struct tegra_camera_dev *pcdev) -{ - /* Turn off relevant clocks. */ - clk_disable(pcdev->clk_vi); - clk_disable(pcdev->clk_vi_sensor); - clk_disable(pcdev->clk_csi); - clk_disable(pcdev->clk_isp); - clk_disable(pcdev->clk_csus); - clk_disable(pcdev->clk_sclk); - clk_disable(pcdev->clk_emc); - - /* Powergate VE */ - tegra_powergate_partition(TEGRA_POWERGATE_VENC); - - /* Disable external power */ - regulator_disable(pcdev->reg); - - nvhost_module_idle_ext(pcdev->ndev); - - pcdev->cal_done = 0; -} - static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev) { - struct vb2_buffer *vb = pcdev->active; - struct tegra_buffer *buf = to_tegra_vb(vb); - struct soc_camera_device *icd = buf->icd; - struct tegra_camera_platform_data *pdata = icd->link->priv; - int port = pdata->port; + struct vb2_buffer *vb; + struct tegra_buffer *buf; int retry = TEGRA_SYNCPT_RETRY_COUNT; - int err; + int port = pcdev->pdata->port; + int err = 0; - while (retry) { - err = tegra_camera_capture_start(pcdev, buf); - /* Capturing succeed, stop capturing */ - if (!err) - err = tegra_camera_capture_stop(pcdev, port); - /* Capturing failed, stop and retry */ - else { - retry--; - - /* Stop streaming. */ - if (port == TEGRA_CAMERA_PORT_CSI_A) { - TC_VI_REG_WT(pcdev, - TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, - 0x0000f002); - /* Clear status registers. */ - TC_VI_REG_WT(pcdev, - TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, - 0xffffffff); - TC_VI_REG_WT(pcdev, - TEGRA_CSI_CSI_CIL_STATUS, - 0xffffffff); - } else if (port == TEGRA_CAMERA_PORT_CSI_B) { - TC_VI_REG_WT(pcdev, - TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, - 0x0000f002); - /* Clear status registers. */ - TC_VI_REG_WT(pcdev, - TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, - 0xffffffff); - TC_VI_REG_WT(pcdev, - TEGRA_CSI_CSI_CIL_STATUS, - 0xffffffff); - } else { - TC_VI_REG_WT(pcdev, - TEGRA_VI_CAMERA_CONTROL, - 0x00000005); - } + if (!pcdev->active) + return 0; - tegra_camera_incr_syncpts(pcdev); - tegra_camera_save_syncpts(pcdev); + vb = pcdev->active; + buf = to_tegra_vb(vb); - continue; + while (retry--) { + err = tegra_camera_capture_start(pcdev, buf); + if (err == 0) { + err = tegra_camera_capture_stop(pcdev); + if (err == 0) break; } - break; - } + /* Stop streaming. */ + if (port == TEGRA_CAMERA_PORT_CSI_A) { + TC_VI_REG_WT(pcdev, + TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, + 0x0000f002); + /* Clear status registers. */ + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, + 0xffffffff); + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_CIL_STATUS, + 0xffffffff); + } else if (port == TEGRA_CAMERA_PORT_CSI_B) { + TC_VI_REG_WT(pcdev, + TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, + 0x0000f002); + /* Clear status registers. */ + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_PIXEL_PARSER_STATUS, + 0xffffffff); + TC_VI_REG_WT(pcdev, + TEGRA_CSI_CSI_CIL_STATUS, + 0xffffffff); + } else { + TC_VI_REG_WT(pcdev, + TEGRA_VI_CAMERA_CONTROL, + 0x00000005); + } - /* Reset hardware for too many errors */ - if (!retry) { - tegra_camera_deactivate(pcdev); - mdelay(5); - tegra_camera_activate(pcdev); - if (pcdev->active) - tegra_camera_capture_setup(pcdev); + tegra_camera_incr_syncpts(pcdev); + tegra_camera_save_syncpts(pcdev); + continue; } spin_lock_irq(&pcdev->videobuf_queue_lock); + /* + * If vb->state is VB2_BUF_STATE_ERROR, then the vb has already been + * removed, so we shouldn't remove it again. + */ + if (vb->state != VB2_BUF_STATE_ERROR) + list_del_init(&buf->queue); + + if (!list_empty(&pcdev->capture)) + pcdev->active = &list_entry(pcdev->capture.next, + struct tegra_buffer, queue)->vb; + else + pcdev->active = NULL; + do_gettimeofday(&vb->v4l2_buf.timestamp); vb->v4l2_buf.field = pcdev->field; - if (port == TEGRA_CAMERA_PORT_CSI_A) - vb->v4l2_buf.sequence = pcdev->sequence_a++; - else if (port == TEGRA_CAMERA_PORT_CSI_B) - vb->v4l2_buf.sequence = pcdev->sequence_b++; + vb->v4l2_buf.sequence = pcdev->sequence++; - vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - list_del_init(&buf->queue); + vb2_buffer_done(vb, (err != 0) ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); pcdev->num_frames++; @@ -1110,145 +1008,101 @@ static int tegra_camera_capture_frame(struct tegra_camera_dev *pcdev) return err; } -static int tegra_camera_csi_pad_calibration(struct tegra_camera_dev *pcdev) +static void tegra_camera_work(struct work_struct *work) { - struct vb2_buffer *vb = pcdev->active; - struct tegra_buffer *buf = to_tegra_vb(vb); - struct soc_camera_device *icd = buf->icd; - struct tegra_camera_platform_data *pdata = icd->link->priv; - int port = pdata->port; - int retry = 500; - u32 data; - int err = -EINVAL; - - TC_VI_REG_WT(pcdev, TEGRA_CSI_DSI_MIPI_CAL_CONFIG, 0x40300); - TC_VI_REG_WT(pcdev, TEGRA_CSI_MIPIBIAS_PAD_CONFIG0, 0x50700); - - if (port == TEGRA_CAMERA_PORT_CSI_B || pdata->lanes == 4) - TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x200004); - - data = 0x2a000004; - if (port == TEGRA_CAMERA_PORT_CSI_A) - data |= 0x200000; /* MIPI_CAL_SELA */ - TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, data); - - /* Start calibration */ - data |= 0x80000000; - TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, data); + struct tegra_camera_dev *pcdev = + container_of(work, struct tegra_camera_dev, work); - while (retry--) { - data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS); - if ((data & 0x8000) == 0x8000) - break; - udelay(20); - } - if (!retry) { - dev_warn(&pcdev->ndev->dev, "MIPI calibration timeout!\n"); - goto cal_out; - } + mutex_lock(&pcdev->work_mutex); - /* Clear status */ - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, data); - retry = 500; - while (retry--) { - data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS); - if ((data & 0x8000) == 0x0) - break; - udelay(20); - } + while (pcdev->active) + tegra_camera_capture_frame(pcdev); - if (!retry) { - dev_warn(&pcdev->ndev->dev, - "Clear MIPI calibration status timeout!\n"); - goto cal_out; - } + mutex_unlock(&pcdev->work_mutex); +} - data = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_PIXEL_PARSER_STATUS); - err = TC_VI_REG_RD(pcdev, TEGRA_CSI_CSI_CIL_STATUS); - if (data | err) { - dev_warn(&pcdev->ndev->dev, - "Calibration status not be cleared!\n"); - err = -EINVAL; - goto cal_out; - } +static void tegra_camera_activate(struct tegra_camera_dev *pcdev) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + u32 val; + void __iomem *apb_misc; +#endif - /* Calibration succeed */ - err = 0; + nvhost_module_busy_ext(pcdev->ndev); -cal_out: - TC_VI_REG_WT(pcdev, TEGRA_CSI_CSI_CIL_STATUS, data); +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + if(!tegra_powergate_is_powered(TEGRA_POWERGATE_VENC)) + tegra_unpowergate_partition(TEGRA_POWERGATE_VENC); +#endif - /* un-select to avoid interference with DSI */ - if (port == TEGRA_CAMERA_PORT_CSI_B || pdata->lanes == 4) - TC_VI_REG_WT(pcdev, TEGRA_CSI_CILB_MIPI_CAL_CONFIG, 0x4); + /* Turn on relevant clocks. */ + clk_enable(pcdev->clk_vi); + clk_enable(pcdev->clk_vi_sensor); + clk_enable(pcdev->clk_csi); + clk_enable(pcdev->clk_isp); + clk_enable(pcdev->clk_csus); - TC_VI_REG_WT(pcdev, TEGRA_CSI_CILA_MIPI_CAL_CONFIG, 0x2a000004); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE); + val = readl(apb_misc + 0x42c); + writel(val | 0x1, apb_misc + 0x42c); +#endif - return err; + /* Save current syncpt values. */ + tegra_camera_save_syncpts(pcdev); } -static void tegra_camera_work(struct work_struct *work) +static void tegra_camera_deactivate(struct tegra_camera_dev *pcdev) { - struct tegra_camera_dev *pcdev = - container_of(work, struct tegra_camera_dev, work); - struct tegra_buffer *buf; + mutex_lock(&pcdev->work_mutex); - while (1) { - mutex_lock(&pcdev->work_mutex); - - spin_lock_irq(&pcdev->videobuf_queue_lock); - if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - spin_unlock_irq(&pcdev->videobuf_queue_lock); - mutex_unlock(&pcdev->work_mutex); - return; - } + /* Cancel active buffer. */ + if (pcdev->active) { + list_del_init(&to_tegra_vb(pcdev->active)->queue); + vb2_buffer_done(pcdev->active, VB2_BUF_STATE_ERROR); + pcdev->active = NULL; + } - buf = list_entry(pcdev->capture.next, struct tegra_buffer, - queue); - pcdev->active = &buf->vb; - spin_unlock_irq(&pcdev->videobuf_queue_lock); + mutex_unlock(&pcdev->work_mutex); - tegra_camera_capture_setup(pcdev); - if (!pcdev->cal_done) { - tegra_camera_csi_pad_calibration(pcdev); - pcdev->cal_done = 1; - } - tegra_camera_capture_frame(pcdev); + /* Turn off relevant clocks. */ + clk_disable(pcdev->clk_vi); + clk_disable(pcdev->clk_vi_sensor); + clk_disable(pcdev->clk_csi); + clk_disable(pcdev->clk_isp); + clk_disable(pcdev->clk_csus); + + nvhost_module_idle_ext(pcdev->ndev); - mutex_unlock(&pcdev->work_mutex); - } } -static int tegra_camera_init_buffer(struct tegra_buffer *buf) +static void tegra_camera_init_buffer(struct tegra_camera_dev *pcdev, + struct tegra_buffer *buf) { - struct soc_camera_device *icd = buf->icd; + struct soc_camera_device *icd = pcdev->icd; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - struct tegra_camera_platform_data *pdata = icd->link->priv; + buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); // physical addr + buf->virtual_addr = vb2_plane_vaddr(&buf->vb, 0); // save virtual addr for later interlace handling switch (icd->current_fmt->host_fmt->fourcc) { case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_VYUY: case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YVYU: - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR10: - buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); buf->start_addr = buf->buffer_addr; - if (pdata->flip_v) + if (pcdev->pdata->flip_v) buf->start_addr += bytes_per_line * (icd->user_height-1); - if (pdata->flip_h) + if (pcdev->pdata->flip_h) buf->start_addr += bytes_per_line - 1; break; case V4L2_PIX_FMT_YUV420: case V4L2_PIX_FMT_YVU420: - buf->buffer_addr = vb2_dma_nvmap_plane_paddr(&buf->vb, 0); buf->buffer_addr_u = buf->buffer_addr + icd->user_width * icd->user_height; buf->buffer_addr_v = buf->buffer_addr_u + @@ -1265,7 +1119,7 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf) buf->start_addr_u = buf->buffer_addr_u; buf->start_addr_v = buf->buffer_addr_v; - if (pdata->flip_v) { + if (pcdev->pdata->flip_v) { buf->start_addr += icd->user_width * (icd->user_height - 1); @@ -1276,7 +1130,7 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf) ((icd->user_height/2) - 1)); } - if (pdata->flip_h) { + if (pcdev->pdata->flip_h) { buf->start_addr += icd->user_width - 1; buf->start_addr_u += (icd->user_width/2) - 1; @@ -1287,12 +1141,8 @@ static int tegra_camera_init_buffer(struct tegra_buffer *buf) break; default: - dev_err(icd->parent, "Wrong host format %d\n", - icd->current_fmt->host_fmt->fourcc); - return -EINVAL; + BUG_ON(1); } - - return 0; } /* @@ -1307,7 +1157,6 @@ static int tegra_camera_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); - struct tegra_camera_platform_data *pdata = icd->link->priv; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct tegra_camera_dev *pcdev = ici->priv; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, @@ -1320,16 +1169,20 @@ static int tegra_camera_videobuf_setup(struct vb2_queue *vq, *num_planes = 1; - if (pdata->port == TEGRA_CAMERA_PORT_CSI_A) - pcdev->sequence_a = 0; - else if (pdata->port == TEGRA_CAMERA_PORT_CSI_B) - pcdev->sequence_b = 0; + pcdev->sequence = 0; sizes[0] = bytes_per_line * icd->user_height; alloc_ctxs[0] = pcdev->alloc_ctx; if (!*num_buffers) *num_buffers = 2; + dev_dbg(icd->parent, "num_buffers=%u, size=%lu\n", + *num_buffers, sizes[0]); + + tegra_camera_capture_setup(pcdev); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_setup()\n"); + return 0; } @@ -1338,8 +1191,9 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb) struct soc_camera_device *icd = container_of(vb->vb2_queue, struct soc_camera_device, vb2_vidq); - struct tegra_buffer *buf = to_tegra_vb(vb); - struct tegra_camera_platform_data *pdata = icd->link->priv; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct tegra_camera_dev *pcdev = ici->priv; + struct tegra_buffer *buf; int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); unsigned long size; @@ -1349,19 +1203,7 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb) if (bytes_per_line < 0) return bytes_per_line; - buf->icd = icd; - - if (!pdata) { - dev_err(icd->parent, "No platform data for this device!\n"); - return -EINVAL; - } - - if (!tegra_camera_port_is_valid(pdata->port)) { - dev_err(icd->parent, - "Invalid camera port %d in platform data\n", - pdata->port); - return -EINVAL; - } + buf = to_tegra_vb(vb); dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_plane_size(vb, 0)); @@ -1375,10 +1217,7 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb) memset(vb2_plane_vaddr(vb, 0), 0xbd, vb2_plane_size(vb, 0)); #endif - if (!icd->current_fmt) { - dev_err(icd->parent, "%s NULL format point\n", __func__); - return -EINVAL; - } + BUG_ON(NULL == icd->current_fmt); size = icd->user_height * bytes_per_line; @@ -1390,7 +1229,11 @@ static int tegra_camera_videobuf_prepare(struct vb2_buffer *vb) vb2_set_plane_payload(vb, 0, size); - return tegra_camera_init_buffer(buf); + tegra_camera_init_buffer(pcdev, buf); + + dev_dbg(icd->parent, "Finished tegra_camera_videobuf_prepare()\n"); + + return 0; } static void tegra_camera_videobuf_queue(struct vb2_buffer *vb) @@ -1402,12 +1245,18 @@ static void tegra_camera_videobuf_queue(struct vb2_buffer *vb) struct tegra_camera_dev *pcdev = ici->priv; struct tegra_buffer *buf = to_tegra_vb(vb); + dev_dbg(icd->parent, "In tegra_camera_videobuf_queue()\n"); + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); spin_lock_irq(&pcdev->videobuf_queue_lock); list_add_tail(&buf->queue, &pcdev->capture); - schedule_work(&pcdev->work); + + if (!pcdev->active) { + pcdev->active = vb; + schedule_work(&pcdev->work); + } spin_unlock_irq(&pcdev->videobuf_queue_lock); dev_dbg(icd->parent, "Finished tegra_camera_videobuf_queue()\n"); @@ -1462,24 +1311,16 @@ static int tegra_camera_stop_streaming(struct vb2_queue *q) struct tegra_camera_dev *pcdev = ici->priv; struct list_head *buf_head, *tmp; - mutex_lock(&pcdev->work_mutex); spin_lock_irq(&pcdev->videobuf_queue_lock); - list_for_each_safe(buf_head, tmp, &pcdev->capture) { - struct tegra_buffer *buf = container_of(buf_head, - struct tegra_buffer, - queue); - if (buf->icd == icd) - list_del_init(buf_head); - } - spin_unlock_irq(&pcdev->videobuf_queue_lock); - if (pcdev->active) { - struct tegra_buffer *buf = to_tegra_vb(pcdev->active); - if (buf->icd == icd) - pcdev->active = NULL; - } + pcdev->active = NULL; + + list_for_each_safe(buf_head, tmp, &pcdev->capture) + list_del_init(buf_head); + + spin_unlock_irq(&pcdev->videobuf_queue_lock); mutex_unlock(&pcdev->work_mutex); @@ -1524,13 +1365,24 @@ static int tegra_camera_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct tegra_camera_dev *pcdev = ici->priv; + int err; - if (!pcdev->enable_refcnt) { - pm_runtime_get_sync(ici->v4l2_dev.dev); - tegra_camera_activate(pcdev); - pcdev->num_frames = 0; + if (pcdev->icd) + return -EBUSY; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + if (pcdev->pdata->enable_camera) { + err = pcdev->pdata->enable_camera(pcdev->ndev); + if (IS_ERR_VALUE(err)) + return err; } - pcdev->enable_refcnt++; + + tegra_camera_activate(pcdev); + + pcdev->icd = icd; + + pcdev->num_frames = 0; dev_dbg(icd->parent, "TEGRA Camera host attached to camera %d\n", icd->devnum); @@ -1544,12 +1396,14 @@ static void tegra_camera_remove_device(struct soc_camera_device *icd) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct tegra_camera_dev *pcdev = ici->priv; - pcdev->enable_refcnt--; - if (!pcdev->enable_refcnt) { - cancel_work_sync(&pcdev->work); - tegra_camera_deactivate(pcdev); - pm_runtime_put_sync(ici->v4l2_dev.dev); - } + tegra_camera_deactivate(pcdev); + + pcdev->icd = NULL; + + if (pcdev->pdata->disable_camera) + pcdev->pdata->disable_camera(pcdev->ndev); + + pm_runtime_put_sync(ici->v4l2_dev.dev); dev_dbg(icd->parent, "Frames captured: %d\n", pcdev->num_frames); @@ -1591,8 +1445,6 @@ static int tegra_camera_get_formats(struct soc_camera_device *icd, case V4L2_MBUS_FMT_VYUY8_2X8: case V4L2_MBUS_FMT_YUYV8_2X8: case V4L2_MBUS_FMT_YVYU8_2X8: - case V4L2_MBUS_FMT_SBGGR8_1X8: - case V4L2_MBUS_FMT_SBGGR10_1X10: formats += ARRAY_SIZE(tegra_camera_formats); for (k = 0; xlate && (k < ARRAY_SIZE(tegra_camera_formats)); @@ -1721,6 +1573,15 @@ static int tegra_camera_try_fmt(struct soc_camera_device *icd, case V4L2_FIELD_NONE: pix->field = V4L2_FIELD_NONE; break; + case V4L2_FIELD_INTERLACED_BT: + pix->field = V4L2_FIELD_INTERLACED_BT; + break; + case V4L2_FIELD_INTERLACED_TB: + pix->field = V4L2_FIELD_INTERLACED_TB; + break; + case V4L2_FIELD_INTERLACED: + pix->field = V4L2_FIELD_INTERLACED; + break; default: /* TODO: support interlaced at least in pass-through mode */ dev_err(icd->parent, "Field type %d unsupported.\n", @@ -1785,7 +1646,8 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev, goto exit; } - pcdev->ndev = ndev; + pcdev->pdata = ndev->dev.platform_data; + pcdev->ndev = ndev; pcdev->ici.priv = pcdev; pcdev->ici.v4l2_dev.dev = &ndev->dev; @@ -1798,6 +1660,14 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev, spin_lock_init(&pcdev->videobuf_queue_lock); mutex_init(&pcdev->work_mutex); + nvhost_set_drvdata(ndev, pcdev); + + if (!tegra_camera_port_is_valid(pcdev->pdata->port)) { + dev_err(&ndev->dev, "Invalid camera port %d in platform data\n", + pcdev->pdata->port); + goto exit_free_pcdev; + } + pcdev->clk_vi = clk_get_sys("tegra_camera", "vi"); if (IS_ERR_OR_NULL(pcdev->clk_vi)) { dev_err(&ndev->dev, "Failed to get vi clock.\n"); @@ -1827,48 +1697,16 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev, dev_err(&ndev->dev, "Failed to get csus clock.\n"); goto exit_put_clk_isp; } - - pcdev->clk_sclk = clk_get_sys("tegra_camera", "sclk"); - if (IS_ERR_OR_NULL(pcdev->clk_sclk)) { - dev_err(&ndev->dev, "Failed to get sclk clock.\n"); - goto exit_put_clk_csus; - } - - pcdev->clk_emc = clk_get_sys("tegra_camera", "emc"); - if (IS_ERR_OR_NULL(pcdev->clk_emc)) { - dev_err(&ndev->dev, "Failed to get emc clock.\n"); - goto exit_put_clk_sclk; - } - clk_set_rate(pcdev->clk_vi, 150000000); clk_set_rate(pcdev->clk_vi_sensor, 24000000); - /* Get regulator pointer */ -#ifdef CONFIG_ARCH_TEGRA_2x_SOC - pcdev->reg = regulator_get(&ndev->dev, "vcsi"); -#else - pcdev->reg = regulator_get(&ndev->dev, "avdd_dsi_csi"); -#endif - if (IS_ERR_OR_NULL(pcdev->reg)) { - dev_err(&ndev->dev, "%s: couldn't get regulator\n", - __func__); - goto exit_put_clk_emc; - } - - nvhost_set_drvdata(ndev, pcdev); err = nvhost_client_device_get_resources(ndev); - if (err) { - dev_err(&ndev->dev, "%s: nvhost get resources failed %d\n", - __func__, err); - goto exit_put_regulator; - } + if (err) + goto exit_put_clk_csus; - err = nvhost_client_device_init(ndev); - if (err) { - dev_err(&ndev->dev, "%s: nvhost init failed %d\n", - __func__, err); - goto exit_put_regulator; - } + // initialize nvhost client device only the first time + if (ndev->power_attrib == NULL) + nvhost_client_device_init(ndev); pcdev->vi_base = ndev->aperture; @@ -1876,10 +1714,10 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev, pm_runtime_enable(&ndev->dev); pm_runtime_resume(&ndev->dev); - pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(&ndev->dev); + pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(pcdev->ici.v4l2_dev.dev); if (IS_ERR(pcdev->alloc_ctx)) { err = PTR_ERR(pcdev->alloc_ctx); - goto exit_pm_disable; + goto exit_put_resources; } err = soc_camera_host_register(&pcdev->ici); @@ -1892,15 +1730,9 @@ static int __devinit tegra_camera_probe(struct nvhost_device *ndev, exit_cleanup_alloc_ctx: vb2_dma_nvmap_cleanup_ctx(pcdev->alloc_ctx); -exit_pm_disable: +exit_put_resources: pm_runtime_disable(&ndev->dev); nvhost_client_device_put_resources(ndev); -exit_put_regulator: - regulator_put(pcdev->reg); -exit_put_clk_emc: - clk_put(pcdev->clk_emc); -exit_put_clk_sclk: - clk_put(pcdev->clk_sclk); exit_put_clk_csus: clk_put(pcdev->clk_csus); exit_put_clk_isp: @@ -1963,6 +1795,11 @@ static int tegra_camera_suspend(struct nvhost_device *ndev, pm_message_t state) /* Suspend the camera sensor. */ WARN_ON(!pcdev->icd->ops->suspend); pcdev->icd->ops->suspend(pcdev->icd, state); + + /* Power off the camera subsystem. */ + pcdev->pdata->disable_camera(pcdev->ndev); + + nvhost_module_idle_ext(nvhost_get_parent(ndev)); } return 0; @@ -1976,10 +1813,14 @@ static int tegra_camera_resume(struct nvhost_device *ndev) /* We only need to do something if a camera sensor is attached. */ if (pcdev->icd) { + nvhost_module_busy_ext(nvhost_get_parent(ndev)); + + /* Power on the camera subsystem. */ + pcdev->pdata->enable_camera(pcdev->ndev); + /* Resume the camera host. */ tegra_camera_save_syncpts(pcdev); - if (pcdev->active) - tegra_camera_capture_setup(pcdev); + tegra_camera_capture_setup(pcdev); /* Resume the camera sensor. */ WARN_ON(!pcdev->icd->ops->resume); @@ -2021,6 +1862,5 @@ module_exit(tegra_camera_exit); MODULE_DESCRIPTION("TEGRA SoC Camera Host driver"); MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>"); -MODULE_AUTHOR("Bryan Wu <pengw@nvidia.com>"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("nvhost:" TEGRA_CAM_DRV_NAME); |