blob: 20df0bbe4a82c8c00052bd49aa0f2d2f31e929ac [file] [log] [blame]
/*
* Copyright (c) 2012-2013, 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,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* 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>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/nvhost.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <mach/powergate.h>
#include <mach/pm_domains.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <media/videobuf2-dma-nvmap.h>
#include <media/tegra_v4l2_camera.h>
#include "dev.h"
#include "bus_client.h"
#include "nvhost_syncpt.h"
#include "t20/t20.h"
#include "t30/t30.h"
#include "t114/t114.h"
#define TEGRA_CAM_DRV_NAME "vi"
#define TEGRA_CAM_VERSION_CODE KERNEL_VERSION(0, 0, 5)
#define TEGRA_SYNCPT_VI_WAIT_TIMEOUT 200
#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
/* 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
/* Tegra CSI-MIPI registers. */
#define TEGRA_VI_OUT_1_INCR_SYNCPT 0x0000
#define TEGRA_VI_OUT_1_INCR_SYNCPT_CNTRL 0x0004
#define TEGRA_VI_OUT_1_INCR_SYNCPT_ERROR 0x0008
#define TEGRA_VI_OUT_2_INCR_SYNCPT 0x0020
#define TEGRA_VI_OUT_2_INCR_SYNCPT_CNTRL 0x0024
#define TEGRA_VI_OUT_2_INCR_SYNCPT_ERROR 0x0028
#define TEGRA_VI_MISC_INCR_SYNCPT 0x0040
#define TEGRA_VI_MISC_INCR_SYNCPT_CNTRL 0x0044
#define TEGRA_VI_MISC_INCR_SYNCPT_ERROR 0x0048
#define TEGRA_VI_CONT_SYNCPT_OUT_1 0x0060
#define TEGRA_VI_CONT_SYNCPT_OUT_2 0x0064
#define TEGRA_VI_CONT_SYNCPT_VIP_VSYNC 0x0068
#define TEGRA_VI_CONT_SYNCPT_VI2EPP 0x006c
#define TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_START 0x0070
#define TEGRA_VI_CONT_SYNCPT_CSI_PPA_FRAME_END 0x0074
#define TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_START 0x0078
#define TEGRA_VI_CONT_SYNCPT_CSI_PPB_FRAME_END 0x007c
#define TEGRA_VI_CTXSW 0x0080
#define TEGRA_VI_INTSTATUS 0x0084
#define TEGRA_VI_VI_INPUT_CONTROL 0x0088
#define TEGRA_VI_VI_CORE_CONTROL 0x008c
#define TEGRA_VI_VI_FIRST_OUTPUT_CONTROL 0x0090
#define TEGRA_VI_VI_SECOND_OUTPUT_CONTROL 0x0094
#define TEGRA_VI_HOST_INPUT_FRAME_SIZE 0x0098
#define TEGRA_VI_HOST_H_ACTIVE 0x009c
#define TEGRA_VI_HOST_V_ACTIVE 0x00a0
#define TEGRA_VI_VIP_H_ACTIVE 0x00a4
#define TEGRA_VI_VIP_V_ACTIVE 0x00a8
#define TEGRA_VI_VI_PEER_CONTROL 0x00ac
#define TEGRA_VI_VI_DMA_SELECT 0x00b0
#define TEGRA_VI_HOST_DMA_WRITE_BUFFER 0x00b4
#define TEGRA_VI_HOST_DMA_BASE_ADDRESS 0x00b8
#define TEGRA_VI_HOST_DMA_WRITE_BUFFER_STATUS 0x00bc
#define TEGRA_VI_HOST_DMA_WRITE_PEND_BUFCOUNT 0x00c0
#define TEGRA_VI_VB0_START_ADDRESS_FIRST 0x00c4
#define TEGRA_VI_VB0_BASE_ADDRESS_FIRST 0x00c8
#define TEGRA_VI_VB0_START_ADDRESS_U 0x00cc
#define TEGRA_VI_VB0_BASE_ADDRESS_U 0x00d0
#define TEGRA_VI_VB0_START_ADDRESS_V 0x00d4
#define TEGRA_VI_VB0_BASE_ADDRESS_V 0x00d8
#define TEGRA_VI_VB_SCRATCH_ADDRESS_UV 0x00dc
#define TEGRA_VI_FIRST_OUTPUT_FRAME_SIZE 0x00e0
#define TEGRA_VI_VB0_COUNT_FIRST 0x00e4
#define TEGRA_VI_VB0_SIZE_FIRST 0x00e8
#define TEGRA_VI_VB0_BUFFER_STRIDE_FIRST 0x00ec
#define TEGRA_VI_VB0_START_ADDRESS_SECOND 0x00f0
#define TEGRA_VI_VB0_BASE_ADDRESS_SECOND 0x00f4
#define TEGRA_VI_SECOND_OUTPUT_FRAME_SIZE 0x00f8
#define TEGRA_VI_VB0_COUNT_SECOND 0x00fc
#define TEGRA_VI_VB0_SIZE_SECOND 0x0100
#define TEGRA_VI_VB0_BUFFER_STRIDE_SECOND 0x0104
#define TEGRA_VI_H_LPF_CONTROL 0x0108
#define TEGRA_VI_H_DOWNSCALE_CONTROL 0x010c
#define TEGRA_VI_V_DOWNSCALE_CONTROL 0x0110
#define TEGRA_VI_CSC_Y 0x0114
#define TEGRA_VI_CSC_UV_R 0x0118
#define TEGRA_VI_CSC_UV_G 0x011c
#define TEGRA_VI_CSC_UV_B 0x0120
#define TEGRA_VI_CSC_ALPHA 0x0124
#define TEGRA_VI_HOST_VSYNC 0x0128
#define TEGRA_VI_COMMAND 0x012c
#define TEGRA_VI_HOST_FIFO_STATUS 0x0130
#define TEGRA_VI_INTERRUPT_MASK 0x0134
#define TEGRA_VI_INTERRUPT_TYPE_SELECT 0x0138
#define TEGRA_VI_INTERRUPT_POLARITY_SELECT 0x013c
#define TEGRA_VI_INTERRUPT_STATUS 0x0140
#define TEGRA_VI_VIP_INPUT_STATUS 0x0144
#define TEGRA_VI_VIDEO_BUFFER_STATUS 0x0148
#define TEGRA_VI_SYNC_OUTPUT 0x014c
#define TEGRA_VI_VVS_OUTPUT_DELAY 0x0150
#define TEGRA_VI_PWM_CONTROL 0x0154
#define TEGRA_VI_PWM_SELECT_PULSE_A 0x0158
#define TEGRA_VI_PWM_SELECT_PULSE_B 0x015c
#define TEGRA_VI_PWM_SELECT_PULSE_C 0x0160
#define TEGRA_VI_PWM_SELECT_PULSE_D 0x0164
#define TEGRA_VI_VI_DATA_INPUT_CONTROL 0x0168
#define TEGRA_VI_PIN_INPUT_ENABLE 0x016c
#define TEGRA_VI_PIN_OUTPUT_ENABLE 0x0170
#define TEGRA_VI_PIN_INVERSION 0x0174
#define TEGRA_VI_PIN_INPUT_DATA 0x0178
#define TEGRA_VI_PIN_OUTPUT_DATA 0x017c
#define TEGRA_VI_PIN_OUTPUT_SELECT 0x0180
#define TEGRA_VI_RAISE_VIP_BUFFER_FIRST_OUTPUT 0x0184
#define TEGRA_VI_RAISE_VIP_FRAME_FIRST_OUTPUT 0x0188
#define TEGRA_VI_RAISE_VIP_BUFFER_SECOND_OUTPUT 0x018c
#define TEGRA_VI_RAISE_VIP_FRAME_SECOND_OUTPUT 0x0190
#define TEGRA_VI_RAISE_HOST_FIRST_OUTPUT 0x0194
#define TEGRA_VI_RAISE_HOST_SECOND_OUTPUT 0x0198
#define TEGRA_VI_RAISE_EPP 0x019c
#define TEGRA_VI_CAMERA_CONTROL 0x01a0
#define TEGRA_VI_VI_ENABLE 0x01a4
#define TEGRA_VI_VI_ENABLE_2 0x01a8
#define TEGRA_VI_VI_RAISE 0x01ac
#define TEGRA_VI_Y_FIFO_WRITE 0x01b0
#define TEGRA_VI_U_FIFO_WRITE 0x01b4
#define TEGRA_VI_V_FIFO_WRITE 0x01b8
#define TEGRA_VI_VI_MCCIF_FIFOCTRL 0x01bc
#define TEGRA_VI_TIMEOUT_WCOAL_VI 0x01c0
#define TEGRA_VI_MCCIF_VIRUV_HP 0x01c4
#define TEGRA_VI_MCCIF_VIWSB_HP 0x01c8
#define TEGRA_VI_MCCIF_VIWU_HP 0x01cc
#define TEGRA_VI_MCCIF_VIWV_HP 0x01d0
#define TEGRA_VI_MCCIF_VIWY_HP 0x01d4
#define TEGRA_VI_CSI_PPA_RAISE_FRAME_START 0x01d8
#define TEGRA_VI_CSI_PPA_RAISE_FRAME_END 0x01dc
#define TEGRA_VI_CSI_PPB_RAISE_FRAME_START 0x01e0
#define TEGRA_VI_CSI_PBB_RAISE_FRAME_END 0x01e4
#define TEGRA_VI_CSI_PPA_H_ACTIVE 0x01e8
#define TEGRA_VI_CSI_PPA_V_ACTIVE 0x01ec
#define TEGRA_VI_CSI_PPB_H_ACTIVE 0x01f0
#define TEGRA_VI_CSI_PPB_V_ACTIVE 0x01f4
#define TEGRA_VI_ISP_H_ACTIVE 0x01f8
#define TEGRA_VI_ISP_V_ACTIVE 0x01fc
#define TEGRA_VI_STREAM_1_RESOURCE_DEFINE 0x0200
#define TEGRA_VI_STREAM_2_RESOURCE_DEFINE 0x0204
#define TEGRA_VI_RAISE_STREAM_1_DONE 0x0208
#define TEGRA_VI_RAISE_STREAM_2_DONE 0x020c
#define TEGRA_VI_TS_MODE 0x0210
#define TEGRA_VI_TS_CONTROL 0x0214
#define TEGRA_VI_TS_PACKET_COUNT 0x0218
#define TEGRA_VI_TS_ERROR_COUNT 0x021c
#define TEGRA_VI_TS_CPU_FLOW_CTL 0x0220
#define TEGRA_VI_VB0_CHROMA_BUFFER_STRIDE_FIRST 0x0224
#define TEGRA_VI_VB0_CHROMA_LINE_STRIDE_FIRST 0x0228
#define TEGRA_VI_EPP_LINES_PER_BUFFER 0x022c
#define TEGRA_VI_BUFFER_RELEASE_OUTPUT1 0x0230
#define TEGRA_VI_BUFFER_RELEASE_OUTPUT2 0x0234
#define TEGRA_VI_DEBUG_FLOW_CONTROL_COUNTER_OUTPUT1 0x0238
#define TEGRA_VI_DEBUG_FLOW_CONTROL_COUNTER_OUTPUT2 0x023c
#define TEGRA_VI_TERMINATE_BW_FIRST 0x0240
#define TEGRA_VI_TERMINATE_BW_SECOND 0x0244
#define TEGRA_VI_VB0_FIRST_BUFFER_ADDR_MODE 0x0248
#define TEGRA_VI_VB0_SECOND_BUFFER_ADDR_MODE 0x024c
#define TEGRA_VI_RESERVE_0 0x0250
#define TEGRA_VI_RESERVE_1 0x0254
#define TEGRA_VI_RESERVE_2 0x0258
#define TEGRA_VI_RESERVE_3 0x025c
#define TEGRA_VI_RESERVE_4 0x0260
#define TEGRA_VI_MCCIF_VIRUV_HYST 0x0264
#define TEGRA_VI_MCCIF_VIWSB_HYST 0x0268
#define TEGRA_VI_MCCIF_VIWU_HYST 0x026c
#define TEGRA_VI_MCCIF_VIWV_HYST 0x0270
#define TEGRA_VI_MCCIF_VIWY_HYST 0x0274
#define TEGRA_CSI_VI_INPUT_STREAM_CONTROL 0x0800
#define TEGRA_CSI_HOST_INPUT_STREAM_CONTROL 0x0808
#define TEGRA_CSI_INPUT_STREAM_A_CONTROL 0x0810
#define TEGRA_CSI_PIXEL_STREAM_A_CONTROL0 0x0818
#define TEGRA_CSI_PIXEL_STREAM_A_CONTROL1 0x081c
#define TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT 0x0820
#define TEGRA_CSI_PIXEL_STREAM_A_GAP 0x0824
#define TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND 0x0828
#define TEGRA_CSI_INPUT_STREAM_B_CONTROL 0x083c
#define TEGRA_CSI_PIXEL_STREAM_B_CONTROL0 0x0844
#define TEGRA_CSI_PIXEL_STREAM_B_CONTROL1 0x0848
#define TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT 0x084c
#define TEGRA_CSI_PIXEL_STREAM_B_GAP 0x0850
#define TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND 0x0854
#define TEGRA_CSI_PHY_CIL_COMMAND 0x0868
#define TEGRA_CSI_PHY_CILA_CONTROL0 0x086c
#define TEGRA_CSI_PHY_CILB_CONTROL0 0x0870
#define TEGRA_CSI_CSI_PIXEL_PARSER_STATUS 0x0878
#define TEGRA_CSI_CSI_CIL_STATUS 0x087c
#define TEGRA_CSI_CSI_PIXEL_PARSER_INTERRUPT_MASK 0x0880
#define TEGRA_CSI_CSI_CIL_INTERRUPT_MASK 0x0884
#define TEGRA_CSI_CSI_READONLY_STATUS 0x0888
#define TEGRA_CSI_ESCAPE_MODE_COMMAND 0x088c
#define TEGRA_CSI_ESCAPE_MODE_DATA 0x0890
#define TEGRA_CSI_CILA_PAD_CONFIG0 0x0894
#define TEGRA_CSI_CILA_PAD_CONFIG1 0x0898
#define TEGRA_CSI_CILB_PAD_CONFIG0 0x089c
#define TEGRA_CSI_CILB_PAD_CONFIG1 0x08a0
#define TEGRA_CSI_CIL_PAD_CONFIG 0x08a4
#define TEGRA_CSI_CILA_MIPI_CAL_CONFIG 0x08a8
#define TEGRA_CSI_CILB_MIPI_CAL_CONFIG 0x08ac
#define TEGRA_CSI_CIL_MIPI_CAL_STATUS 0x08b0
#define TEGRA_CSI_CLKEN_OVERRIDE 0x08b4
#define TEGRA_CSI_DEBUG_CONTROL 0x08b8
#define TEGRA_CSI_DEBUG_COUNTER_0 0x08bc
#define TEGRA_CSI_DEBUG_COUNTER_1 0x08c0
#define TEGRA_CSI_DEBUG_COUNTER_2 0x08c4
#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 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)
#define tegra_camera_port_is_valid(port) \
(((port) >= TEGRA_CAMERA_PORT_CSI_A) && \
((port) <= TEGRA_CAMERA_PORT_VIP))
#define tegra_camera_port_is_csi(port) \
(((port) == TEGRA_CAMERA_PORT_CSI_A) || \
((port) == TEGRA_CAMERA_PORT_CSI_B))
/*
* Structures
*/
/* buffer for one video frame */
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
* per frame. These are calculated during videobuf_prepare.
*/
dma_addr_t buffer_addr;
dma_addr_t buffer_addr_u;
dma_addr_t buffer_addr_v;
dma_addr_t start_addr;
dma_addr_t start_addr_u;
dma_addr_t start_addr_v;
};
struct tegra_camera_dev {
struct soc_camera_host ici;
struct platform_device *ndev;
struct nvhost_device_data *ndata;
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;
struct list_head capture;
struct vb2_buffer *active;
struct vb2_alloc_ctx *alloc_ctx;
enum v4l2_field field;
int sequence_a;
int sequence_b;
struct work_struct work;
struct mutex work_mutex;
u32 syncpt_vi;
u32 syncpt_csi_a;
u32 syncpt_csi_b;
/* Debug */
int num_frames;
int enable_refcnt;
};
static const struct soc_mbus_pixelfmt tegra_camera_formats[] = {
{
.fourcc = V4L2_PIX_FMT_UYVY,
.name = "YUV422 (UYVY) packed",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
{
.fourcc = V4L2_PIX_FMT_VYUY,
.name = "YUV422 (VYUY) packed",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.name = "YUV422 (YUYV) packed",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
{
.fourcc = V4L2_PIX_FMT_YVYU,
.name = "YUV422 (YVYU) packed",
.bits_per_sample = 16,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
{
.fourcc = V4L2_PIX_FMT_YUV420,
.name = "YUV420 (YU12) planar",
.bits_per_sample = 12,
.packing = SOC_MBUS_PACKING_NONE,
.order = SOC_MBUS_ORDER_LE,
},
{
.fourcc = V4L2_PIX_FMT_YVU420,
.name = "YVU420 (YV12) planar",
.bits_per_sample = 12,
.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)
{
return container_of(vb, struct tegra_buffer, vb);
}
static void tegra_camera_save_syncpts(struct tegra_camera_dev *pcdev)
{
pcdev->syncpt_csi_a =
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);
pcdev->syncpt_vi =
nvhost_syncpt_read_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_VI);
}
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);
nvhost_syncpt_cpu_incr_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_VI);
}
static void tegra_camera_capture_clean(struct tegra_camera_dev *pcdev)
{
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_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_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_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);
}
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);
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_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);
/* 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));
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_CONTROL1,
0x1); /* Frame # for top field detect for interlaced */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_WORD_COUNT,
bytes_per_line);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_GAP, 0x00140000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_A_EXPECTED_FRAME,
(icd->user_height << 16) |
(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));
/* Use 0x00000022 for continuous clock mode. */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILA_CONTROL0,
(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);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00020001);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND, 0x0000f002);
}
static void tegra_camera_capture_setup_csi_b(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);
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_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));
/* pad 0s 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 */
(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 */
0x1); /* Set PPB stream source to CSI B */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_CONTROL1,
0x1); /* Frame # for top field detect for interlaced */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_WORD_COUNT,
bytes_per_line);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_GAP, 0x00140000);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_B_EXPECTED_FRAME,
(icd->user_height << 16) |
(0x100 << 4) | /* Wait 0x100 vi clks for timeout */
0x1); /* Enable line timeout */
TC_VI_REG_WT(pcdev, TEGRA_CSI_INPUT_STREAM_B_CONTROL,
(0x3f << 16) | /* Skip packet threshold */
(pdata->lanes - 1));
/* Use 0x00000022 for continuous clock mode. */
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CILB_CONTROL0,
(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);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PHY_CIL_COMMAND, 0x00010002);
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPB_COMMAND, 0x0000f002);
}
static void tegra_camera_capture_setup_vip(struct tegra_camera_dev *pcdev,
struct soc_camera_device *icd,
u32 input_control)
{
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 << 1) | /* VIP_INPUT_ENABLE */
input_control);
TC_VI_REG_WT(pcdev, TEGRA_VI_H_DOWNSCALE_CONTROL, 0x00000000);
TC_VI_REG_WT(pcdev, TEGRA_VI_V_DOWNSCALE_CONTROL, 0x00000000);
/* 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);
TC_VI_REG_WT(pcdev, TEGRA_VI_VIP_V_ACTIVE,
(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_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,
(0x1 << 8) | /* Enable continuous syncpt */
TEGRA_VI_SYNCPT_VI);
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)
{
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);
const struct soc_camera_format_xlate *current_fmt = icd->current_fmt;
u32 output_fourcc = current_fmt->host_fmt->fourcc;
u32 output_format, output_control;
struct tegra_buffer *buf = to_tegra_vb(pcdev->active);
switch (output_fourcc) {
case V4L2_PIX_FMT_UYVY:
output_format = 0x3; /* Default to YUV422 */
break;
case V4L2_PIX_FMT_VYUY:
output_format = (0x1 << 17) | 0x3;
break;
case V4L2_PIX_FMT_YUYV:
output_format = (0x2 << 17) | 0x3;
break;
case V4L2_PIX_FMT_YVYU:
output_format = (0x3 << 17) | 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;
}
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;
}
/*
* Set up low pass filter. Use 0x240 for chromaticity and 0x240
* for luminance, which is the default and means not to touch
* anything.
*/
TC_VI_REG_WT(pcdev, TEGRA_VI_H_LPF_CONTROL, 0x02400240);
/* 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);
else if (port == TEGRA_CAMERA_PORT_CSI_B)
tegra_camera_capture_setup_csi_b(pcdev, icd, hdr);
else
tegra_camera_capture_setup_vip(pcdev, icd, input_control);
/* 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)
{
struct soc_camera_device *icd = buf->icd;
switch (icd->current_fmt->host_fmt->fourcc) {
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_U,
buf->buffer_addr_u);
TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_U,
buf->start_addr_u);
TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_BASE_ADDRESS_V,
buf->buffer_addr_v);
TC_VI_REG_WT(pcdev, TEGRA_VI_VB0_START_ADDRESS_V,
buf->start_addr_v);
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:
/* 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;
default:
dev_err(&pcdev->ndev->dev, "Wrong host format %d\n",
icd->current_fmt->host_fmt->fourcc);
return -EINVAL;
}
return 0;
}
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;
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,
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);
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_CSI_B,
pcdev->syncpt_csi_b,
TEGRA_SYNCPT_CSI_WAIT_TIMEOUT,
NULL,
NULL);
} else {
pcdev->syncpt_vi++;
TC_VI_REG_WT(pcdev, TEGRA_VI_CAMERA_CONTROL,
0x00000001);
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_VI,
pcdev->syncpt_csi_a,
TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
NULL,
NULL);
}
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",
buf->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);
rostatus = TC_VI_REG_RD(pcdev,
TEGRA_CSI_CSI_READONLY_STATUS);
dev_warn(&icd->vdev->dev,
"PPSTATUS = 0x%08x, "
"CILSTATUS = 0x%08x, "
"ROSTATUS = 0x%08x\n",
ppstatus, cilstatus, rostatus);
} 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",
buf->buffer_addr);
vip_input_status = TC_VI_REG_RD(pcdev,
TEGRA_VI_VIP_INPUT_STATUS);
dev_warn(&pcdev->ndev->dev,
"VIP_INPUT_STATUS = 0x%08x\n",
vip_input_status);
}
return err;
}
static int tegra_camera_capture_stop(struct tegra_camera_dev *pcdev, int port)
{
int err;
struct tegra_buffer *buf = to_tegra_vb(pcdev->active);
if (port == TEGRA_CAMERA_PORT_CSI_A)
TC_VI_REG_WT(pcdev, TEGRA_CSI_PIXEL_STREAM_PPA_COMMAND,
0x0000f002);
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);
if (tegra_camera_port_is_csi(port))
err = nvhost_syncpt_wait_timeout_ext(pcdev->ndev,
TEGRA_VI_SYNCPT_VI,
pcdev->syncpt_vi,
TEGRA_SYNCPT_VI_WAIT_TIMEOUT,
NULL,
NULL);
else
err = 0;
if (err) {
u32 buffer_addr;
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,
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",
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,
"PPSTATUS = 0x%08x, CILSTATUS = 0x%08x\n",
ppstatus, cilstatus);
}
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);
/*
* Powergating DIS must powergate VE partition. Camera
* module needs to increase the ref-count of disa to
* avoid itself powergated by DIS inadvertently.
*/
#if defined(CONFIG_ARCH_TEGRA_11x_SOC) || defined(CONFIG_ARCH_TEGRA_14x_SOC)
tegra_unpowergate_partition(TEGRA_POWERGATE_DISA);
#endif
/* Unpowergate VE */
tegra_unpowergate_partition(TEGRA_POWERGATE_VENC);
/* Turn on relevant clocks. */
clk_set_rate(pcdev->clk_vi, 150000000);
clk_prepare_enable(pcdev->clk_vi);
clk_set_rate(pcdev->clk_vi_sensor, 24000000);
clk_prepare_enable(pcdev->clk_vi_sensor);
clk_prepare_enable(pcdev->clk_csi);
clk_prepare_enable(pcdev->clk_isp);
clk_prepare_enable(pcdev->clk_csus);
clk_set_rate(pcdev->clk_sclk, 80000000);
clk_prepare_enable(pcdev->clk_sclk);
clk_set_rate(pcdev->clk_sclk, 375000000);
clk_prepare_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_unprepare(pcdev->clk_vi);
clk_disable_unprepare(pcdev->clk_vi_sensor);
clk_disable_unprepare(pcdev->clk_csi);
clk_disable_unprepare(pcdev->clk_isp);
clk_disable_unprepare(pcdev->clk_csus);
clk_disable_unprepare(pcdev->clk_sclk);
clk_disable_unprepare(pcdev->clk_emc);
/* Powergate VE */
tegra_powergate_partition(TEGRA_POWERGATE_VENC);
#if defined(CONFIG_ARCH_TEGRA_11x_SOC) || defined(CONFIG_ARCH_TEGRA_14x_SOC)
tegra_powergate_partition(TEGRA_POWERGATE_DISA);
#endif
/* Disable external power */
regulator_disable(pcdev->reg);
nvhost_module_idle_ext(pcdev->ndev);
}
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;
int retry = TEGRA_SYNCPT_RETRY_COUNT;
int err;
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);
}
tegra_camera_incr_syncpts(pcdev);
tegra_camera_save_syncpts(pcdev);
continue;
}
break;
}
/* 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);
}
spin_lock_irq(&pcdev->videobuf_queue_lock);
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++;
vb2_buffer_done(vb, err < 0 ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE);
list_del_init(&buf->queue);
pcdev->num_frames++;
spin_unlock_irq(&pcdev->videobuf_queue_lock);
return err;
}
static void tegra_camera_work(struct work_struct *work)
{
struct tegra_camera_dev *pcdev =
container_of(work, struct tegra_camera_dev, work);
struct tegra_buffer *buf;
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;
}
buf = list_entry(pcdev->capture.next, struct tegra_buffer,
queue);
pcdev->active = &buf->vb;
spin_unlock_irq(&pcdev->videobuf_queue_lock);
tegra_camera_capture_setup(pcdev);
tegra_camera_capture_frame(pcdev);
mutex_unlock(&pcdev->work_mutex);
}
}
static int tegra_camera_init_buffer(struct tegra_buffer *buf)
{
struct soc_camera_device *icd = buf->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;
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)
buf->start_addr += bytes_per_line *
(icd->user_height-1);
if (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 +
(icd->user_width * icd->user_height) / 4;
/* For YVU420, we swap the locations of the U and V planes. */
if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_YVU420) {
dma_addr_t temp = buf->buffer_addr_u;
buf->buffer_addr_u = buf->buffer_addr_v;
buf->buffer_addr_v = temp;
}
buf->start_addr = buf->buffer_addr;
buf->start_addr_u = buf->buffer_addr_u;
buf->start_addr_v = buf->buffer_addr_v;
if (pdata->flip_v) {
buf->start_addr += icd->user_width *
(icd->user_height - 1);
buf->start_addr_u += ((icd->user_width/2) *
((icd->user_height/2) - 1));
buf->start_addr_v += ((icd->user_width/2) *
((icd->user_height/2) - 1));
}
if (pdata->flip_h) {
buf->start_addr += icd->user_width - 1;
buf->start_addr_u += (icd->user_width/2) - 1;
buf->start_addr_v += (icd->user_width/2) - 1;
}
break;
default:
dev_err(icd->parent, "Wrong host format %d\n",
icd->current_fmt->host_fmt->fourcc);
return -EINVAL;
}
return 0;
}
/*
* Videobuf operations
*/
static int tegra_camera_videobuf_setup(struct vb2_queue *vq,
const struct v4l2_format *fmt,
unsigned int *num_buffers,
unsigned int *num_planes,
unsigned int sizes[],
void *alloc_ctxs[])
{
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,
icd->current_fmt->host_fmt);
if (bytes_per_line < 0)
return bytes_per_line;
*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;
sizes[0] = bytes_per_line * icd->user_height;
alloc_ctxs[0] = pcdev->alloc_ctx;
if (!*num_buffers)
*num_buffers = 2;
return 0;
}
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;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
unsigned long size;
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;
}
dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
vb, vb2_plane_vaddr(vb, 0), vb2_plane_size(vb, 0));
#ifdef PREFILL_BUFFER
/*
* This can be useful if you want to see if we actually fill
* the buffer with something
*/
if (vb2_plane_vaddr(vb, 0))
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;
}
size = icd->user_height * bytes_per_line;
if (vb2_plane_size(vb, 0) < size) {
dev_err(icd->parent, "Buffer too small (%lu < %lu)\n",
vb2_plane_size(vb, 0), size);
return -ENOBUFS;
}
vb2_set_plane_payload(vb, 0, size);
return tegra_camera_init_buffer(buf);
}
static void tegra_camera_videobuf_queue(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = container_of(vb->vb2_queue,
struct soc_camera_device,
vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct tegra_camera_dev *pcdev = ici->priv;
struct tegra_buffer *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_get_plane_payload(vb, 0));
spin_lock_irq(&pcdev->videobuf_queue_lock);
list_add_tail(&buf->queue, &pcdev->capture);
schedule_work(&pcdev->work);
spin_unlock_irq(&pcdev->videobuf_queue_lock);
dev_dbg(icd->parent, "Finished tegra_camera_videobuf_queue()\n");
}
static void tegra_camera_videobuf_release(struct vb2_buffer *vb)
{
struct soc_camera_device *icd = container_of(vb->vb2_queue,
struct soc_camera_device,
vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct tegra_buffer *buf = to_tegra_vb(vb);
struct tegra_camera_dev *pcdev = ici->priv;
dev_dbg(icd->parent, "In tegra_camera_videobuf_release()\n");
mutex_lock(&pcdev->work_mutex);
spin_lock_irq(&pcdev->videobuf_queue_lock);
if (pcdev->active == vb)
pcdev->active = NULL;
/*
* Doesn't hurt also if the list is empty, but it hurts, if queuing the
* buffer failed, and .buf_init() hasn't been called
*/
if (buf->queue.next)
list_del_init(&buf->queue);
spin_unlock_irq(&pcdev->videobuf_queue_lock);
mutex_unlock(&pcdev->work_mutex);
dev_dbg(icd->parent, "Finished tegra_camera_videobuf_release()\n");
}
static int tegra_camera_videobuf_init(struct vb2_buffer *vb)
{
/* This is for locking debugging only */
INIT_LIST_HEAD(&to_tegra_vb(vb)->queue);
return 0;
}
static int tegra_camera_stop_streaming(struct vb2_queue *q)
{
struct soc_camera_device *icd = container_of(q,
struct soc_camera_device,
vb2_vidq);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
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;
}
mutex_unlock(&pcdev->work_mutex);
return 0;
}
static struct vb2_ops tegra_camera_videobuf_ops = {
.queue_setup = tegra_camera_videobuf_setup,
.buf_prepare = tegra_camera_videobuf_prepare,
.buf_queue = tegra_camera_videobuf_queue,
.buf_cleanup = tegra_camera_videobuf_release,
.buf_init = tegra_camera_videobuf_init,
.wait_prepare = soc_camera_unlock,
.wait_finish = soc_camera_lock,
.stop_streaming = tegra_camera_stop_streaming,
};
/*
* SOC camera host operations
*/
static int tegra_camera_init_videobuf(struct vb2_queue *q,
struct soc_camera_device *icd)
{
dev_dbg(icd->parent, "In tegra_camera_init_videobuf()\n");
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->drv_priv = icd;
q->ops = &tegra_camera_videobuf_ops;
q->mem_ops = &vb2_dma_nvmap_memops;
q->buf_struct_size = sizeof(struct tegra_buffer);
dev_dbg(icd->parent, "Finished tegra_camera_init_videobuf()\n");
return vb2_queue_init(q);
}
/*
* Called with .video_lock held
*/
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;
if (!pcdev->enable_refcnt) {
pm_runtime_get_sync(ici->v4l2_dev.dev);
tegra_camera_activate(pcdev);
pcdev->num_frames = 0;
}
pcdev->enable_refcnt++;
dev_dbg(icd->parent, "TEGRA Camera host attached to camera %d\n",
icd->devnum);
return 0;
}
/* Called with .video_lock held */
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);
}
dev_dbg(icd->parent, "Frames captured: %d\n", pcdev->num_frames);
dev_dbg(icd->parent, "TEGRA camera host detached from camera %d\n",
icd->devnum);
}
static int tegra_camera_set_bus_param(struct soc_camera_device *icd)
{
return 0;
}
static int tegra_camera_get_formats(struct soc_camera_device *icd,
unsigned int idx,
struct soc_camera_format_xlate *xlate)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
int formats = 0;
int ret;
enum v4l2_mbus_pixelcode code;
const struct soc_mbus_pixelfmt *fmt;
int k;
ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
if (ret != 0)
/* No more formats */
return 0;
fmt = soc_mbus_get_fmtdesc(code);
if (!fmt) {
dev_err(dev, "Invalid format code #%u: %d\n", idx, code);
return 0;
}
switch (code) {
case V4L2_MBUS_FMT_UYVY8_2X8:
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));
k++) {
xlate->host_fmt = &tegra_camera_formats[k];
xlate->code = code;
xlate++;
dev_info(dev, "Providing format %s using code %d\n",
tegra_camera_formats[k].name, code);
}
break;
default:
dev_info(dev, "Not supporting %s\n", fmt->name);
return 0;
}
return formats;
}
static void tegra_camera_put_formats(struct soc_camera_device *icd)
{
kfree(icd->host_priv);
icd->host_priv = NULL;
}
static int tegra_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct device *dev = icd->parent;
struct soc_camera_host *ici = to_soc_camera_host(dev);
struct tegra_camera_dev *pcdev = ici->priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate = NULL;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
int ret;
dev_dbg(dev, "In tegra_camera_set_fmt()\n");
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
dev_warn(dev, "Format %x not found\n", pix->pixelformat);
return -EINVAL;
}
mf.width = pix->width;
mf.height = pix->height;
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
ret = v4l2_subdev_call(sd, video, s_mbus_fmt, &mf);
if (IS_ERR_VALUE(ret)) {
dev_warn(dev, "Failed to configure for format %x\n",
pix->pixelformat);
return ret;
}
if (mf.code != xlate->code) {
dev_warn(dev, "mf.code = %d, xlate->code = %d, mismatch\n",
mf.code, xlate->code);
return -EINVAL;
}
icd->user_width = mf.width;
icd->user_height = mf.height;
icd->current_fmt = xlate;
pcdev->field = pix->field;
dev_dbg(dev, "Finished tegra_camera_set_fmt(), returning %d\n", ret);
return ret;
}
static int tegra_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
int ret;
dev_dbg(icd->parent, "In tegra_camera_try_fmt()\n");
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (!xlate) {
dev_warn(icd->parent, "Format %x not found\n", pixfmt);
return -EINVAL;
}
pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
xlate->host_fmt);
if (pix->bytesperline < 0)
return pix->bytesperline;
pix->sizeimage = pix->height * pix->bytesperline;
/* limit to sensor capabilities */
mf.width = pix->width;
mf.height = pix->height;
mf.field = pix->field;
mf.colorspace = pix->colorspace;
mf.code = xlate->code;
ret = v4l2_subdev_call(sd, video, try_mbus_fmt, &mf);
if (IS_ERR_VALUE(ret))
return ret;
pix->width = mf.width;
pix->height = mf.height;
pix->colorspace = mf.colorspace;
/*
* width and height could have been changed, therefore update the
* bytesperline and sizeimage here.
*/
pix->bytesperline = soc_mbus_bytes_per_line(pix->width,
xlate->host_fmt);
pix->sizeimage = pix->height * pix->bytesperline;
switch (mf.field) {
case V4L2_FIELD_ANY:
case V4L2_FIELD_NONE:
pix->field = V4L2_FIELD_NONE;
break;
default:
/* TODO: support interlaced at least in pass-through mode */
dev_err(icd->parent, "Field type %d unsupported.\n",
mf.field);
return -EINVAL;
}
dev_dbg(icd->parent,
"Finished tegra_camera_try_fmt(), returning %d\n", ret);
return ret;
}
static int tegra_camera_reqbufs(struct soc_camera_device *icd,
struct v4l2_requestbuffers *p)
{
return 0;
}
static unsigned int tegra_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
return vb2_poll(&icd->vb2_vidq, file, pt);
}
static int tegra_camera_querycap(struct soc_camera_host *ici,
struct v4l2_capability *cap)
{
strlcpy(cap->card, TEGRA_CAM_DRV_NAME, sizeof(cap->card));
cap->version = TEGRA_CAM_VERSION_CODE;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
return 0;
}
static struct soc_camera_host_ops tegra_soc_camera_host_ops = {
.owner = THIS_MODULE,
.init_videobuf2 = tegra_camera_init_videobuf,
.add = tegra_camera_add_device,
.remove = tegra_camera_remove_device,
.set_bus_param = tegra_camera_set_bus_param,
.get_formats = tegra_camera_get_formats,
.put_formats = tegra_camera_put_formats,
.set_fmt = tegra_camera_set_fmt,
.try_fmt = tegra_camera_try_fmt,
.reqbufs = tegra_camera_reqbufs,
.poll = tegra_camera_poll,
.querycap = tegra_camera_querycap,
};
static struct of_device_id tegra_vi_of_match[] = {
#ifdef TEGRA_2X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra20-vi",
.data = (struct nvhost_device_data *)&t20_vi_info },
#endif
#ifdef TEGRA_3X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra30-vi",
.data = (struct nvhost_device_data *)&t30_vi_info },
#endif
#ifdef TEGRA_11X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra114-vi",
.data = (struct nvhost_device_data *)&t11_vi_info },
#endif
#ifdef TEGRA_14X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra148-vi",
.data = (struct nvhost_device_data *)&t14_vi_info },
#endif
{ },
};
static int tegra_camera_probe(struct platform_device *pdev)
{
struct tegra_camera_dev *pcdev;
struct nvhost_device_data *ndata = NULL;
int err = 0;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_device(tegra_vi_of_match, &pdev->dev);
if (match)
ndata = match->data;
} else
ndata = pdev->dev.platform_data;
if (!ndata) {
dev_err(&pdev->dev, "No nvhost device data!\n");
err = -EINVAL;
goto exit;
}
pcdev = kzalloc(sizeof(struct tegra_camera_dev), GFP_KERNEL);
if (!pcdev) {
dev_err(&pdev->dev, "Could not allocate pcdev\n");
err = -ENOMEM;
goto exit;
}
pcdev->ndata = ndata;
pcdev->ndev = pdev;
pcdev->ici.priv = pcdev;
pcdev->ici.v4l2_dev.dev = &pdev->dev;
pcdev->ici.nr = pdev->id;
pcdev->ici.drv_name = dev_name(&pdev->dev);
pcdev->ici.ops = &tegra_soc_camera_host_ops;
INIT_LIST_HEAD(&pcdev->capture);
INIT_WORK(&pcdev->work, tegra_camera_work);
spin_lock_init(&pcdev->videobuf_queue_lock);
mutex_init(&pcdev->work_mutex);
pcdev->clk_vi = clk_get(&pdev->dev, "vi");
if (IS_ERR_OR_NULL(pcdev->clk_vi)) {
dev_err(&pdev->dev, "Failed to get vi clock.\n");
goto exit_free_pcdev;
}
pcdev->clk_vi_sensor = clk_get(&pdev->dev, "vi_sensor");
if (IS_ERR_OR_NULL(pcdev->clk_vi_sensor)) {
dev_err(&pdev->dev, "Failed to get vi_sensor clock.\n");
goto exit_put_clk_vi;
}
pcdev->clk_csi = clk_get(&pdev->dev, "csi");
if (IS_ERR_OR_NULL(pcdev->clk_csi)) {
dev_err(&pdev->dev, "Failed to get csi clock.\n");
goto exit_put_clk_vi_sensor;
}
pcdev->clk_isp = clk_get(&pdev->dev, "isp");
if (IS_ERR_OR_NULL(pcdev->clk_isp)) {
dev_err(&pdev->dev, "Failed to get isp clock.\n");
goto exit_put_clk_csi;
}
pcdev->clk_csus = clk_get(&pdev->dev, "csus");
if (IS_ERR_OR_NULL(pcdev->clk_csus)) {
dev_err(&pdev->dev, "Failed to get csus clock.\n");
goto exit_put_clk_isp;
}
pcdev->clk_sclk = clk_get(&pdev->dev, "sclk");
if (IS_ERR_OR_NULL(pcdev->clk_sclk)) {
dev_err(&pdev->dev, "Failed to get sclk clock.\n");
goto exit_put_clk_csus;
}
pcdev->clk_emc = clk_get(&pdev->dev, "emc");
if (IS_ERR_OR_NULL(pcdev->clk_emc)) {
dev_err(&pdev->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(&pdev->dev, "vcsi");
#else
pcdev->reg = regulator_get(&pdev->dev, "avdd_dsi_csi");
#endif
if (IS_ERR_OR_NULL(pcdev->reg)) {
dev_err(&pdev->dev, "%s: couldn't get regulator\n",
__func__);
goto exit_put_clk_emc;
}
platform_set_drvdata(pdev, ndata);
err = nvhost_client_device_get_resources(pdev);
if (err) {
dev_err(&pdev->dev, "%s: nvhost get resources failed %d\n",
__func__, err);
goto exit_put_regulator;
}
err = nvhost_client_device_init(pdev);
if (err) {
dev_err(&pdev->dev, "%s: nvhost init failed %d\n",
__func__, err);
goto exit_put_regulator;
}
pcdev->vi_base = ndata->aperture[0];
tegra_pd_add_device(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, ndata->clockgate_delay);
pm_runtime_enable(&pdev->dev);
pcdev->alloc_ctx = vb2_dma_nvmap_init_ctx(&pdev->dev);
if (IS_ERR(pcdev->alloc_ctx)) {
err = PTR_ERR(pcdev->alloc_ctx);
goto exit_pm_disable;
}
platform_set_drvdata(pdev, pcdev);
err = soc_camera_host_register(&pcdev->ici);
if (IS_ERR_VALUE(err))
goto exit_cleanup_alloc_ctx;
dev_notice(&pdev->dev, "Tegra camera driver loaded.\n");
return err;
exit_cleanup_alloc_ctx:
platform_set_drvdata(pdev, pcdev->ndata);
vb2_dma_nvmap_cleanup_ctx(pcdev->alloc_ctx);
exit_pm_disable:
pm_runtime_disable(&pdev->dev);
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:
clk_put(pcdev->clk_isp);
exit_put_clk_csi:
clk_put(pcdev->clk_csi);
exit_put_clk_vi_sensor:
clk_put(pcdev->clk_vi_sensor);
exit_put_clk_vi:
clk_put(pcdev->clk_vi);
exit_free_pcdev:
kfree(pcdev);
exit:
return err;
}
static int tegra_camera_remove(struct platform_device *pdev)
{
struct soc_camera_host *ici = to_soc_camera_host(&pdev->dev);
struct tegra_camera_dev *pcdev = container_of(ici,
struct tegra_camera_dev, ici);
soc_camera_host_unregister(ici);
platform_set_drvdata(pdev, pcdev->ndata);
nvhost_client_device_release(pdev);
vb2_dma_nvmap_cleanup_ctx(pcdev->alloc_ctx);
pm_runtime_disable(&pdev->dev);
regulator_put(pcdev->reg);
clk_put(pcdev->clk_emc);
clk_put(pcdev->clk_sclk);
clk_put(pcdev->clk_csus);
clk_put(pcdev->clk_isp);
clk_put(pcdev->clk_csi);
clk_put(pcdev->clk_vi_sensor);
clk_put(pcdev->clk_vi);
kfree(pcdev);
dev_notice(&pdev->dev, "Tegra camera host driver unloaded\n");
return 0;
}
#ifdef CONFIG_PM_FISH
static int tegra_camera_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct soc_camera_host *ici = to_soc_camera_host(&pdev->dev);
struct tegra_camera_dev *pcdev = container_of(ici,
struct tegra_camera_dev, ici);
mutex_lock(&pcdev->work_mutex);
/* We only need to do something if a camera sensor is attached. */
if (pcdev->icd) {
/* Suspend the camera sensor. */
WARN_ON(!pcdev->icd->ops->suspend);
pcdev->icd->ops->suspend(pcdev->icd, state);
}
return 0;
}
static int tegra_camera_resume(struct platform_device *pdev)
{
struct soc_camera_host *ici = to_soc_camera_host(&pdev->dev);
struct tegra_camera_dev *pcdev = container_of(ici,
struct tegra_camera_dev, ici);
/* We only need to do something if a camera sensor is attached. */
if (pcdev->icd) {
/* Resume the camera host. */
tegra_camera_save_syncpts(pcdev);
if (pcdev->active)
tegra_camera_capture_setup(pcdev);
/* Resume the camera sensor. */
WARN_ON(!pcdev->icd->ops->resume);
pcdev->icd->ops->resume(pcdev->icd);
}
mutex_unlock(&pcdev->work_mutex);
return 0;
}
#endif
static struct platform_driver tegra_camera_driver = {
.driver = {
.name = TEGRA_CAM_DRV_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = tegra_vi_of_match,
#endif
},
.probe = tegra_camera_probe,
.remove = tegra_camera_remove,
#ifdef CONFIG_PM_FISH
.suspend = tegra_camera_suspend,
.resume = tegra_camera_resume,
#endif
};
static int __init tegra_camera_init(void)
{
return platform_driver_register(&tegra_camera_driver);
}
static void __exit tegra_camera_exit(void)
{
platform_driver_unregister(&tegra_camera_driver);
}
module_init(tegra_camera_init);
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);