From 9811ff09017d0382c5f0be8c7056bc0151b0d78a Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Fri, 17 Jun 2016 17:16:13 -0700 Subject: [PATCH] media: tegra_camera: add SoC fops for CSI Add an SoC abstraction layer for for CSI which can be extended to support different Tegra SoC in the future. Now start with T210. Bug 1779020 Change-Id: I7c081ccb008bb7775591300495154aaf4505aee5 Signed-off-by: Bryan Wu Reviewed-on: http://git-master/r/1169883 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jihoon Bang --- drivers/media/platform/tegra/csi/Makefile | 2 +- drivers/media/platform/tegra/csi/csi.c | 244 +--------------- drivers/media/platform/tegra/csi/csi.h | 18 ++ drivers/media/platform/tegra/csi/csi2_fops.c | 284 +++++++++++++++++++ drivers/media/platform/tegra/csi/csi2_fops.h | 42 +++ drivers/media/platform/tegra/tpg/tpg.c | 1 + drivers/media/platform/tegra/vi/vi.c | 6 +- drivers/media/platform/tegra/vi/vi.h | 2 + 8 files changed, 359 insertions(+), 240 deletions(-) create mode 100644 drivers/media/platform/tegra/csi/csi2_fops.c create mode 100644 drivers/media/platform/tegra/csi/csi2_fops.h diff --git a/drivers/media/platform/tegra/csi/Makefile b/drivers/media/platform/tegra/csi/Makefile index 33b59fad920..648d5bbd279 100644 --- a/drivers/media/platform/tegra/csi/Makefile +++ b/drivers/media/platform/tegra/csi/Makefile @@ -3,4 +3,4 @@ ccflags-y += -Idrivers/video/tegra/host ccflags-y += -Idrivers/media/platform/tegra ccflags-y += -Werror -obj-y += csi.o +obj-y += csi.o csi2_fops.o diff --git a/drivers/media/platform/tegra/csi/csi.c b/drivers/media/platform/tegra/csi/csi.c index a019e027dc4..566e73e4d19 100644 --- a/drivers/media/platform/tegra/csi/csi.c +++ b/drivers/media/platform/tegra/csi/csi.c @@ -27,67 +27,6 @@ #include "vi/vi.h" #include "csi/csi.h" -#define DEBUG 0 - -static void csi_write(struct tegra_csi_device *csi, unsigned int addr, - u32 val, u8 port) -{ -#if DEBUG - dev_info(csi->dev, "%s:port %d offset 0x%08x val:0x%08x\n", - __func__, port, addr, val); -#endif - writel(val, (csi->iomem[port] + addr)); -} - -static u32 csi_read(struct tegra_csi_device *csi, unsigned int addr, - u8 port) -{ -#if DEBUG - dev_info(csi->dev, "%s:port %d offset 0x%08x\n", __func__, port, addr); -#endif - return readl((csi->iomem[port] + addr)); -} - -/* Pixel parser registers accessors */ -static void pp_write(struct tegra_csi_port *port, u32 addr, u32 val) -{ -#if DEBUG - pr_info("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val); -#endif - writel(val, port->pixel_parser + addr); -} - -static u32 pp_read(struct tegra_csi_port *port, u32 addr) -{ -#if DEBUG - pr_info("%s:offset 0x%08x\n", __func__, addr); -#endif - return readl(port->pixel_parser + addr); -} - -/* CSI CIL registers accessors */ -static void cil_write(struct tegra_csi_port *port, u32 addr, u32 val) -{ -#if DEBUG - pr_info("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val); -#endif - writel(val, port->cil + addr); -} - -static u32 cil_read(struct tegra_csi_port *port, u32 addr) -{ -#if DEBUG - pr_info("%s:offset 0x%08x\n", __func__, addr); -#endif - return readl(port->cil + addr); -} - -/* Test pattern generator registers accessor */ -static void tpg_write(struct tegra_csi_port *port, unsigned int addr, u32 val) -{ - writel(val, port->tpg + addr); -} - static int csi_get_clks(struct tegra_csi_device *csi, struct platform_device *pdev) { @@ -305,210 +244,39 @@ EXPORT_SYMBOL(tegra_csi_power); void tegra_csi_tpg_start_streaming(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - - tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, - ((csi->pg_mode - 1) << PG_MODE_OFFSET) | - PG_ENABLE); - tpg_write(port, TEGRA_CSI_PG_PHASE, 0x0); - tpg_write(port, TEGRA_CSI_PG_RED_FREQ, - (0x10 << PG_RED_VERT_INIT_FREQ_OFFSET) | - (0x10 << PG_RED_HOR_INIT_FREQ_OFFSET)); - tpg_write(port, TEGRA_CSI_PG_RED_FREQ_RATE, 0x0); - tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ, - (0x10 << PG_GREEN_VERT_INIT_FREQ_OFFSET) | - (0x10 << PG_GREEN_HOR_INIT_FREQ_OFFSET)); - tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ_RATE, 0x0); - tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ, - (0x10 << PG_BLUE_VERT_INIT_FREQ_OFFSET) | - (0x10 << PG_BLUE_HOR_INIT_FREQ_OFFSET)); - tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ_RATE, 0x0); + csi->fops->soc_tpg_start_streaming(csi, port_num); } void tegra_csi_start_streaming(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - - csi_write(csi, TEGRA_CSI_CLKEN_OVERRIDE, 0, port_num>>1); - - /* Clean up status */ - pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, 0xFFFFFFFF); - cil_write(port, TEGRA_CSI_CIL_STATUS, 0xFFFFFFFF); - cil_write(port, TEGRA_CSI_CILX_STATUS, 0xFFFFFFFF); - - cil_write(port, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0); - - /* CIL PHY registers setup */ - cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); - cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL, BYPASS_LP_SEQ | 0xA); - - /* - * The CSI unit provides for connection of up to six cameras in - * the system and is organized as three identical instances of - * two MIPI support blocks, each with a separate 4-lane - * interface that can be configured as a single camera with 4 - * lanes or as a dual camera with 2 lanes available for each - * camera. - */ - if (port->lanes == 4) { - int port_val = ((port_num >> 1) << 1); - struct tegra_csi_port *port_a = &csi->ports[port_val]; - struct tegra_csi_port *port_b = &csi->ports[port_val+1]; - - cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, - BRICK_CLOCK_A_4X); - cil_write(port_b, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); - cil_write(port_b, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0); - cil_write(port_a, TEGRA_CSI_CIL_PHY_CONTROL, - BYPASS_LP_SEQ | 0xA); - cil_write(port_b, TEGRA_CSI_CIL_PHY_CONTROL, - BYPASS_LP_SEQ | 0xA); - csi_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, - CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE, - port_num>>1); - } else { - u32 val = csi_read(csi, TEGRA_CSI_PHY_CIL_COMMAND, port_num>>1); - int port_val = ((port_num >> 1) << 1); - struct tegra_csi_port *port_a = &csi->ports[port_val]; - - cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); - val |= ((port->num & 0x1) == PORT_A) ? CSI_A_PHY_CIL_ENABLE : - CSI_B_PHY_CIL_ENABLE; - csi_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, val, port_num>>1); - } - - /* CSI pixel parser registers setup */ - pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, - (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | - CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_RST); - pp_write(port, TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0); - pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL0, - CSI_PP_PACKET_HEADER_SENT | - CSI_PP_DATA_IDENTIFIER_ENABLE | - CSI_PP_WORD_COUNT_SELECT_HEADER | - CSI_PP_CRC_CHECK_ENABLE | CSI_PP_WC_CHECK | - CSI_PP_OUTPUT_FORMAT_STORE | CSI_PPA_PAD_LINE_NOPAD | - CSI_PP_HEADER_EC_DISABLE | CSI_PPA_PAD_FRAME_NOPAD | - (port->num & 1)); - pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL1, - (0x1 << CSI_PP_TOP_FIELD_FRAME_OFFSET) | - (0x1 << CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET)); - pp_write(port, TEGRA_CSI_PIXEL_STREAM_GAP, - 0x14 << PP_FRAME_MIN_GAP_OFFSET); - pp_write(port, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME, 0x0); - pp_write(port, TEGRA_CSI_INPUT_STREAM_CONTROL, - (0x3f << CSI_SKIP_PACKET_THRESHOLD_OFFSET) | - (port->lanes - 1)); - -#if DEBUG - /* 0x454140E1 - register setting for line counter */ - /* 0x454340E1 - tracks frame start, line starts, hpa headers */ - pp_write(port, TEGRA_CSI_DEBUG_CONTROL, 0x454340E1); -#endif - pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, - (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | - CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_ENABLE); + csi->fops->soc_start_streaming(csi, port_num); } EXPORT_SYMBOL(tegra_csi_start_streaming); int tegra_csi_error(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - u32 val; - int err = 0; - - /* - * only uncorrectable header error and multi-bit - * transmission errors are checked as they cannot be - * corrected automatically - */ - val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); - err |= val & 0x4000; - pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, val); - - val = cil_read(port, TEGRA_CSI_CIL_STATUS); - err |= val & 0x02; - cil_write(port, TEGRA_CSI_CIL_STATUS, val); - - val = cil_read(port, TEGRA_CSI_CILX_STATUS); - err |= val & 0x00020020; - cil_write(port, TEGRA_CSI_CILX_STATUS, val); - - return err; + return csi->fops->soc_error(csi, port_num); } void tegra_csi_status(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - u32 val = pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); - - dev_dbg(csi->dev, "TEGRA_CSI_PIXEL_PARSER_STATUS 0x%08x\n", - val); - - val = cil_read(port, TEGRA_CSI_CIL_STATUS); - dev_dbg(csi->dev, "TEGRA_CSI_CIL_STATUS 0x%08x\n", val); - - val = cil_read(port, TEGRA_CSI_CILX_STATUS); - dev_dbg(csi->dev, "TEGRA_CSI_CILX_STATUS 0x%08x\n", val); - -#if DEBUG - val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_0); - dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_0 0x%08x\n", val); - val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_1); - dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_1 0x%08x\n", val); - val = pp_read(port, TEGRA_CSI_DEBUG_COUNTER_2); - dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_2 0x%08x\n", val); -#endif + csi->fops->soc_status(csi, port_num); } EXPORT_SYMBOL(tegra_csi_status); void tegra_csi_error_recover(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - - if (port->lanes == 4) { - int port_val = ((port_num >> 1) << 1); - struct tegra_csi_port *port_a = &csi->ports[port_val]; - struct tegra_csi_port *port_b = &csi->ports[port_val+1]; - tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); - tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); - cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); - cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); - csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, port_num>>1); - /* sleep for clock cycles to drain the Rx FIFO */ - usleep_range(10, 20); - cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); - cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); - csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, port_num>>1); - tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); - tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); - } else { - tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_ENABLE); - cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); - csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, port_num>>1); - /* sleep for clock cycles to drain the Rx FIFO */ - usleep_range(10, 20); - cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); - csi_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, port_num>>1); - tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); - } + csi->fops->soc_error_recover(csi, port_num); } void tegra_csi_stop_streaming(struct tegra_csi_device *csi, enum tegra_csi_port_num port_num) { - struct tegra_csi_port *port = &csi->ports[port_num]; - - if (csi->pg_mode) - tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, PG_DISABLE); - - pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, - (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | - CSI_PP_DISABLE); + csi->fops->soc_stop_streaming(csi, port_num); } EXPORT_SYMBOL(tegra_csi_stop_streaming); diff --git a/drivers/media/platform/tegra/csi/csi.h b/drivers/media/platform/tegra/csi/csi.h index 8b1e047d6b8..74b6a792dff 100644 --- a/drivers/media/platform/tegra/csi/csi.h +++ b/drivers/media/platform/tegra/csi/csi.h @@ -55,6 +55,7 @@ struct tegra_csi_port { struct tegra_csi_device { struct v4l2_subdev subdev; + struct vi *vi; struct device *dev; void __iomem *iomem[3]; struct clk *clk; @@ -68,6 +69,23 @@ struct tegra_csi_device { unsigned int clk_freq; int num_ports; int pg_mode; + + struct tegra_csi_fops *fops; +}; + +struct tegra_csi_fops { + void (*soc_tpg_start_streaming)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + void (*soc_start_streaming)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + int (*soc_error)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + void (*soc_status)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + void (*soc_error_recover)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + void (*soc_stop_streaming)(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); }; static inline struct tegra_csi_device *to_csi(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/tegra/csi/csi2_fops.c b/drivers/media/platform/tegra/csi/csi2_fops.c new file mode 100644 index 00000000000..9e13f09c763 --- /dev/null +++ b/drivers/media/platform/tegra/csi/csi2_fops.c @@ -0,0 +1,284 @@ +/* + * Tegra CSI2 device common APIs + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Author: Bryan Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "csi/csi.h" + +#define DEBUG 0 + +static void csi2_write(struct tegra_csi_device *csi, unsigned int addr, + u32 val, u8 port) +{ + dev_dbg(csi->dev, "%s:port %d offset 0x%08x val:0x%08x\n", + __func__, port, addr, val); + writel(val, (csi->iomem[port] + addr)); +} + +static u32 csi2_read(struct tegra_csi_device *csi, unsigned int addr, + u8 port) +{ + dev_dbg(csi->dev, "%s:port %d offset 0x%08x\n", __func__, port, addr); + return readl((csi->iomem[port] + addr)); +} + +/* Pixel parser registers accessors */ +static void csi2_pp_write(struct tegra_csi_port *port, u32 addr, u32 val) +{ + pr_debug("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val); + writel(val, port->pixel_parser + addr); +} + +static u32 csi2_pp_read(struct tegra_csi_port *port, u32 addr) +{ + pr_debug("%s:offset 0x%08x\n", __func__, addr); + return readl(port->pixel_parser + addr); +} + +/* CSI CIL registers accessors */ +static void csi2_cil_write(struct tegra_csi_port *port, u32 addr, u32 val) +{ + pr_debug("%s:offset 0x%08x val:0x%08x\n", __func__, addr, val); + writel(val, port->cil + addr); +} + +static u32 csi2_cil_read(struct tegra_csi_port *port, u32 addr) +{ + pr_debug("%s:offset 0x%08x\n", __func__, addr); + return readl(port->cil + addr); +} + +/* Test pattern generator registers accessor */ +static void csi2_tpg_write(struct tegra_csi_port *port, + unsigned int addr, u32 val) +{ + writel(val, port->tpg + addr); +} + +void csi2_tpg_start_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + + csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + ((csi->pg_mode - 1) << PG_MODE_OFFSET) | + PG_ENABLE); + csi2_tpg_write(port, TEGRA_CSI_PG_PHASE, 0x0); + csi2_tpg_write(port, TEGRA_CSI_PG_RED_FREQ, + (0x10 << PG_RED_VERT_INIT_FREQ_OFFSET) | + (0x10 << PG_RED_HOR_INIT_FREQ_OFFSET)); + csi2_tpg_write(port, TEGRA_CSI_PG_RED_FREQ_RATE, 0x0); + csi2_tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ, + (0x10 << PG_GREEN_VERT_INIT_FREQ_OFFSET) | + (0x10 << PG_GREEN_HOR_INIT_FREQ_OFFSET)); + csi2_tpg_write(port, TEGRA_CSI_PG_GREEN_FREQ_RATE, 0x0); + csi2_tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ, + (0x10 << PG_BLUE_VERT_INIT_FREQ_OFFSET) | + (0x10 << PG_BLUE_HOR_INIT_FREQ_OFFSET)); + csi2_tpg_write(port, TEGRA_CSI_PG_BLUE_FREQ_RATE, 0x0); +} + +void csi2_start_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + + csi2_write(csi, TEGRA_CSI_CLKEN_OVERRIDE, 0, port_num >> 1); + + /* Clean up status */ + csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, 0xFFFFFFFF); + csi2_cil_write(port, TEGRA_CSI_CIL_STATUS, 0xFFFFFFFF); + csi2_cil_write(port, TEGRA_CSI_CILX_STATUS, 0xFFFFFFFF); + + csi2_cil_write(port, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0); + + /* CIL PHY registers setup */ + csi2_cil_write(port, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); + csi2_cil_write(port, TEGRA_CSI_CIL_PHY_CONTROL, + BYPASS_LP_SEQ | 0xA); + + /* + * The CSI unit provides for connection of up to six cameras in + * the system and is organized as three identical instances of + * two MIPI support blocks, each with a separate 4-lane + * interface that can be configured as a single camera with 4 + * lanes or as a dual camera with 2 lanes available for each + * camera. + */ + if (port->lanes == 4) { + int port_val = ((port_num >> 1) << 1); + struct tegra_csi_port *port_a = &csi->ports[port_val]; + struct tegra_csi_port *port_b = &csi->ports[port_val + 1]; + + csi2_cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, + BRICK_CLOCK_A_4X); + csi2_cil_write(port_b, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); + csi2_cil_write(port_b, TEGRA_CSI_CIL_INTERRUPT_MASK, 0x0); + csi2_cil_write(port_a, TEGRA_CSI_CIL_PHY_CONTROL, + BYPASS_LP_SEQ | 0xA); + csi2_cil_write(port_b, TEGRA_CSI_CIL_PHY_CONTROL, + BYPASS_LP_SEQ | 0xA); + csi2_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, + CSI_A_PHY_CIL_ENABLE | CSI_B_PHY_CIL_ENABLE, + port_num >> 1); + } else { + u32 val = csi2_read(csi, TEGRA_CSI_PHY_CIL_COMMAND, + port_num >> 1); + int port_val = ((port_num >> 1) << 1); + struct tegra_csi_port *port_a = &csi->ports[port_val]; + + csi2_cil_write(port_a, TEGRA_CSI_CIL_PAD_CONFIG0, 0x0); + val |= ((port->num & 0x1) == PORT_A) ? CSI_A_PHY_CIL_ENABLE : + CSI_B_PHY_CIL_ENABLE; + csi2_write(csi, TEGRA_CSI_PHY_CIL_COMMAND, val, + port_num >> 1); + } + + /* CSI pixel parser registers setup */ + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, + (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | + CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_RST); + csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_INTERRUPT_MASK, 0x0); + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL0, + CSI_PP_PACKET_HEADER_SENT | + CSI_PP_DATA_IDENTIFIER_ENABLE | + CSI_PP_WORD_COUNT_SELECT_HEADER | + CSI_PP_CRC_CHECK_ENABLE | CSI_PP_WC_CHECK | + CSI_PP_OUTPUT_FORMAT_STORE | CSI_PPA_PAD_LINE_NOPAD | + CSI_PP_HEADER_EC_DISABLE | CSI_PPA_PAD_FRAME_NOPAD | + (port->num & 1)); + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_CONTROL1, + (0x1 << CSI_PP_TOP_FIELD_FRAME_OFFSET) | + (0x1 << CSI_PP_TOP_FIELD_FRAME_MASK_OFFSET)); + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_GAP, + 0x14 << PP_FRAME_MIN_GAP_OFFSET); + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_EXPECTED_FRAME, 0x0); + csi2_pp_write(port, TEGRA_CSI_INPUT_STREAM_CONTROL, + (0x3f << CSI_SKIP_PACKET_THRESHOLD_OFFSET) | + (port->lanes - 1)); +#if DEBUG + /* 0x454140E1 - register setting for line counter */ + /* 0x454340E1 - tracks frame start, line starts, hpa headers */ + csi2_pp_write(port, TEGRA_CSI_DEBUG_CONTROL, 0x454340E1); +#endif + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, + (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | + CSI_PP_SINGLE_SHOT_ENABLE | CSI_PP_ENABLE); +} + +int csi2_error(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + u32 val; + int err = 0; + + /* + * only uncorrectable header error and multi-bit + * transmission errors are checked as they cannot be + * corrected automatically + */ + val = csi2_pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); + err |= val & 0x4000; + csi2_pp_write(port, TEGRA_CSI_PIXEL_PARSER_STATUS, val); + + val = csi2_cil_read(port, TEGRA_CSI_CIL_STATUS); + err |= val & 0x02; + csi2_cil_write(port, TEGRA_CSI_CIL_STATUS, val); + + val = csi2_cil_read(port, TEGRA_CSI_CILX_STATUS); + err |= val & 0x00020020; + csi2_cil_write(port, TEGRA_CSI_CILX_STATUS, val); + + return err; +} + +void csi2_status(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + u32 val = csi2_pp_read(port, TEGRA_CSI_PIXEL_PARSER_STATUS); + + dev_dbg(csi->dev, "TEGRA_CSI_PIXEL_PARSER_STATUS 0x%08x\n", + val); + + val = csi2_cil_read(port, TEGRA_CSI_CIL_STATUS); + dev_dbg(csi->dev, "TEGRA_CSI_CIL_STATUS 0x%08x\n", val); + + val = csi2_cil_read(port, TEGRA_CSI_CILX_STATUS); + dev_dbg(csi->dev, "TEGRA_CSI_CILX_STATUS 0x%08x\n", val); + +#if DEBUG + val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_0); + dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_0 0x%08x\n", val); + val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_1); + dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_1 0x%08x\n", val); + val = csi2_pp_read(port, TEGRA_CSI_DEBUG_COUNTER_2); + dev_dbg(csi->dev, "TEGRA_CSI_DEBUG_COUNTER_2 0x%08x\n", val); +#endif +} + +void csi2_error_recover(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + + if (port->lanes == 4) { + int port_val = ((port_num >> 1) << 1); + struct tegra_csi_port *port_a = &csi->ports[port_val]; + struct tegra_csi_port *port_b = &csi->ports[port_val+1]; + csi2_tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_ENABLE); + csi2_tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_ENABLE); + csi2_cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); + csi2_cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); + csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, + port_num >> 1); + /* sleep for clock cycles to drain the Rx FIFO */ + usleep_range(10, 20); + csi2_cil_write(port_a, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); + csi2_cil_write(port_b, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); + csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, + port_num >> 1); + csi2_tpg_write(port_a, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_DISABLE); + csi2_tpg_write(port_b, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_DISABLE); + } else { + csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_ENABLE); + csi2_cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x1); + csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x1, + port_num >> 1); + /* sleep for clock cycles to drain the Rx FIFO */ + usleep_range(10, 20); + csi2_cil_write(port, TEGRA_CSI_CIL_SW_SENSOR_RESET, 0x0); + csi2_write(csi, TEGRA_CSI_CSI_SW_STATUS_RESET, 0x0, + port_num >> 1); + csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_DISABLE); + } +} + +void csi2_stop_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num) +{ + struct tegra_csi_port *port = &csi->ports[port_num]; + + if (csi->pg_mode) + csi2_tpg_write(port, TEGRA_CSI_PATTERN_GENERATOR_CTRL, + PG_DISABLE); + + csi2_pp_write(port, TEGRA_CSI_PIXEL_STREAM_PP_COMMAND, + (0xF << CSI_PP_START_MARKER_FRAME_MAX_OFFSET) | + CSI_PP_DISABLE); +} diff --git a/drivers/media/platform/tegra/csi/csi2_fops.h b/drivers/media/platform/tegra/csi/csi2_fops.h new file mode 100644 index 00000000000..1ef0d76f223 --- /dev/null +++ b/drivers/media/platform/tegra/csi/csi2_fops.h @@ -0,0 +1,42 @@ +/* + * Tegra CSI2 device common APIs + * + * Tegra Graphics Host VI + * + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + * + * Author: Bryan Wu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CSI2_H__ +#define __CSI2_H__ + +#include "csi.h" + +void csi2_tpg_start_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); +void csi2_start_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); +int csi2_error(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); +void csi2_status(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); +void csi2_error_recover(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); +void csi2_stop_streaming(struct tegra_csi_device *csi, + enum tegra_csi_port_num port_num); + +struct tegra_csi_fops csi2_fops = { + .soc_tpg_start_streaming = csi2_tpg_start_streaming, + .soc_start_streaming = csi2_start_streaming, + .soc_error = csi2_error, + .soc_status = csi2_status, + .soc_error_recover = csi2_error_recover, + .soc_stop_streaming = csi2_stop_streaming, +}; + +#endif diff --git a/drivers/media/platform/tegra/tpg/tpg.c b/drivers/media/platform/tegra/tpg/tpg.c index 1b607b36dd7..30a6da5109d 100644 --- a/drivers/media/platform/tegra/tpg/tpg.c +++ b/drivers/media/platform/tegra/tpg/tpg.c @@ -40,6 +40,7 @@ static int tpg_probe(struct vi *tegra_vi) /* Init CSI related media controller interface */ csi->num_ports = TPG_CHANNELS; csi->pg_mode = TEGRA_VI_PG_PATCH; + csi->vi = tegra_vi; ret = tegra_csi_media_controller_init(csi, pdev); if (ret) return ret; diff --git a/drivers/media/platform/tegra/vi/vi.c b/drivers/media/platform/tegra/vi/vi.c index d0e22bdb44e..d59e4038227 100644 --- a/drivers/media/platform/tegra/vi/vi.c +++ b/drivers/media/platform/tegra/vi/vi.c @@ -41,6 +41,7 @@ #include "vi/vi.h" #include "vi/vi_irq.h" #include "camera/vi2_fops.h" +#include "csi/csi2_fops.h" #ifdef CONFIG_ARCH_TEGRA_18x_SOC #include "t186/t186.h" @@ -61,12 +62,14 @@ struct tegra_vi_data t124_vi_data = { .info = (struct nvhost_device_data *)&t124_vi_info, .vi_fops = &vi2_fops, .channel_fops = &vi2_channel_fops, + .csi_fops = &csi2_fops, }; struct tegra_vi_data t210_vi_data = { .info = (struct nvhost_device_data *)&t21_vi_info, .vi_fops = &vi2_fops, .channel_fops = &vi2_channel_fops, + .csi_fops = &csi2_fops, }; #ifdef CONFIG_ARCH_TEGRA_18x_SOC @@ -312,7 +315,7 @@ static int vi_probe(struct platform_device *dev) tegra_vi->dev = &dev->dev; /* If missing SoC fops, whole VI/CSI has be in bypass mode */ - if (!data->vi_fops || !data->channel_fops) + if (!data->vi_fops || !data->channel_fops || !data->csi_fops) tegra_vi->bypass = true; err = nvhost_client_device_get_resources(dev); @@ -400,6 +403,7 @@ static int vi_probe(struct platform_device *dev) goto camera_unregister; tegra_vi->csi.vi = tegra_vi; + tegra_vi->csi.fops = tegra_vi->data->csi_fops; err = tegra_csi_init(&tegra_vi->csi, dev); if (err) goto vi_mc_init_error; diff --git a/drivers/media/platform/tegra/vi/vi.h b/drivers/media/platform/tegra/vi/vi.h index 71e0aad22ee..1d83b7166a5 100644 --- a/drivers/media/platform/tegra/vi/vi.h +++ b/drivers/media/platform/tegra/vi/vi.h @@ -27,6 +27,7 @@ #include "camera/mc_common.h" #include "chip_support.h" +#include "csi/csi.h" #define VI_CFG_INTERRUPT_MASK_0 0x8c #define VI_CFG_INTERRUPT_STATUS_0 0x98 @@ -111,6 +112,7 @@ struct tegra_vi_data { struct nvhost_device_data *info; struct tegra_vi_fops *vi_fops; struct tegra_vi_channel_fops *channel_fops; + struct tegra_csi_fops *csi_fops; }; struct vi { -- 2.39.2