lkml.org 
[lkml]   [2022]   [Apr]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH 3/5] media: platform: visconti: Add Toshiba Visconti Video Input Interface driver body
Date
Add support to Video Input Interface on Toshiba Visconti Video Input Interface driver.
Functions in this commit are drivers for CSI2 receiver and frame grabber.

Signed-off-by: Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>
Reviewed-by: Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
---
drivers/media/platform/Kconfig | 2 +
drivers/media/platform/Makefile | 4 +
drivers/media/platform/visconti/Kconfig | 9 +
drivers/media/platform/visconti/Makefile | 9 +
drivers/media/platform/visconti/hwd_viif.c | 2233 +++++++++++++++++
.../media/platform/visconti/hwd_viif_csi2rx.c | 767 ++++++
drivers/media/platform/visconti/viif.c | 1830 ++++++++++++++
7 files changed, 4854 insertions(+)
create mode 100644 drivers/media/platform/visconti/Kconfig
create mode 100644 drivers/media/platform/visconti/Makefile
create mode 100644 drivers/media/platform/visconti/hwd_viif.c
create mode 100644 drivers/media/platform/visconti/hwd_viif_csi2rx.c
create mode 100644 drivers/media/platform/visconti/viif.c

diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index cf4adc64c..0eb1dfc05 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -31,6 +31,8 @@ source "drivers/media/platform/davinci/Kconfig"

source "drivers/media/platform/omap/Kconfig"

+source "drivers/media/platform/visconti/Kconfig"
+
config VIDEO_ASPEED
tristate "Aspeed AST2400 and AST2500 Video Engine driver"
depends on VIDEO_V4L2
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index a148553ba..4af489811 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -84,6 +84,10 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/

obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/

+obj-$(CONFIG_ARCH_VISCONTI) += visconti/
+
obj-y += sunxi/

obj-$(CONFIG_VIDEO_MESON_GE2D) += meson/ge2d/
+
+
diff --git a/drivers/media/platform/visconti/Kconfig b/drivers/media/platform/visconti/Kconfig
new file mode 100644
index 000000000..446c809d4
--- /dev/null
+++ b/drivers/media/platform/visconti/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_VISCONTI_VIIF
+ bool "Visconti Camera Interface driver"
+ depends on VIDEO_V4L2 && MEDIA_CONTROLLER
+ depends on ARCH_VISCONTI
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ This is V4L2 driver for Toshiba Visconti Camera Interface driver
+
diff --git a/drivers/media/platform/visconti/Makefile b/drivers/media/platform/visconti/Makefile
new file mode 100644
index 000000000..6463f33f0
--- /dev/null
+++ b/drivers/media/platform/visconti/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Visconti video input device driver
+#
+
+visconti-viif-objs = viif.o
+visconti-viif-objs += hwd_viif_csi2rx.o hwd_viif.o
+
+obj-$(CONFIG_VIDEO_VISCONTI_VIIF) += visconti-viif.o
diff --git a/drivers/media/platform/visconti/hwd_viif.c b/drivers/media/platform/visconti/hwd_viif.c
new file mode 100644
index 000000000..518a86d56
--- /dev/null
+++ b/drivers/media/platform/visconti/hwd_viif.c
@@ -0,0 +1,2233 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "hwd_viif.h"
+#include "hwd_viif_internal.h"
+
+/* MIPI CSI2 DataType definition */
+#define CSI2_DT_YUV4208 0x18
+#define CSI2_DT_YUV42010 0x19
+#define CSI2_DT_YUV4208L 0x1A
+#define CSI2_DT_YUV4208C 0x1C
+#define CSI2_DT_YUV42010C 0x1D
+#define CSI2_DT_YUV4228B VISCONTI_CSI2_DT_YUV4228B
+#define CSI2_DT_YUV42210B VISCONTI_CSI2_DT_YUV42210B
+#define CSI2_DT_RGB444 0x20
+#define CSI2_DT_RGB555 0x21
+#define CSI2_DT_RGB565 VISCONTI_CSI2_DT_RGB565
+#define CSI2_DT_RGB666 0x23
+#define CSI2_DT_RGB888 VISCONTI_CSI2_DT_RGB888
+#define CSI2_DT_RAW8 VISCONTI_CSI2_DT_RAW8
+#define CSI2_DT_RAW10 VISCONTI_CSI2_DT_RAW10
+#define CSI2_DT_RAW12 VISCONTI_CSI2_DT_RAW12
+#define CSI2_DT_RAW14 VISCONTI_CSI2_DT_RAW14
+
+static struct hwd_viif_res hwd_VIIF0_res = {
+ .ch = 0,
+ .csi2rx_type = HWD_VIIF_CSI2_TYPE_4_LANES,
+ .sram_size_w_port = 0x200,
+ .sram_size_r_port = 0x200,
+ .sram_start_addr_gamma = 0x640,
+ .interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE,
+ .input_num = 1U,
+ .hobc_size = 0U,
+ .rawpack = HWD_VIIF_RAWPACK_DISABLE,
+ .l2_input = 0U,
+ .color_type = 0U,
+ .run_flag_main = (bool)false,
+};
+
+static struct hwd_viif_res hwd_VIIF1_res = {
+ .ch = 1,
+ .csi2rx_type = HWD_VIIF_CSI2_TYPE_4_LANES,
+ .sram_size_w_port = 0x200,
+ .sram_size_r_port = 0x200,
+ .sram_start_addr_gamma = 0x640,
+ .interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE,
+ .input_num = 1U,
+ .hobc_size = 0U,
+ .rawpack = HWD_VIIF_RAWPACK_DISABLE,
+ .l2_input = 0U,
+ .color_type = 0U,
+ .run_flag_main = (bool)false,
+};
+
+/**
+ * viif_id2res() - resource data for specified module ID
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: Pointer to resource access structure
+ */
+struct hwd_viif_res *viif_id2res(uint32_t module_id)
+{
+ return (module_id == 0) ? &hwd_VIIF0_res : &hwd_VIIF1_res;
+}
+
+/**
+ * hwd_VIIF_initialize() - Initialize VIIF HWD layer.
+ *
+ * @csi2host_vaddr: VIIF CSI-2 RX register base address(virtual address)
+ * @capture_vaddr: VIIF capture(system, vdmac, l1isp, l2isp) register base address(virtual address)
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ */
+int32_t hwd_VIIF_initialize(uint32_t module_id, void *csi2host_vaddr, void *capture_vaddr)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ /* Store virtual address of the register base */
+ res->csi2host_reg = csi2host_vaddr;
+ res->capture_reg = capture_vaddr;
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_uninitialize() - De-initialize VIIF HWD layer.
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ */
+int32_t hwd_VIIF_uninitialize(uint32_t module_id)
+{
+ return 0;
+}
+
+/**
+ * hwd_VIIF_force_stop() - Stop memory input or CSI2 input
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -ETIMEDOUT Driver timeout error
+ */
+int32_t hwd_VIIF_force_stop(uint32_t module_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ int32_t ret = 0;
+ uint32_t val;
+
+ if ((res->ch != 0U) && (res->ch != 1U))
+ return -EINVAL;
+
+ /* Disable auto transmission of register buffer */
+ writel(0, &(res->capture_reg->l1isp.L1_CRGBF_TRN_A_CONF));
+ writel(0, &(res->capture_reg->l2isp.L2_CRGBF_TRN_A_CONF));
+
+ /* Wait for completion of register buffer transmission */
+ udelay(HWD_VIIF_WAIT_ISP_REGBF_TRNS_COMPLETE_TIME);
+
+ if (res->ch == 1U) {
+ /* Disable VOIF through input */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.CSI2THROUGHEN));
+ }
+
+ /* Stop internal input */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTI_M_SYNCEN));
+
+ /* Stop all VCs, long packet input and emb data input of MAIN unit */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.VCPORTEN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTM_OTHEREN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTM_EMBEN));
+
+ /* Stop image data input, long packet input and emb data input of SUB unit */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_OTHEREN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_EMBEN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_IMGEN));
+
+ /* Stop VDMAC for all table ports, input ports and write ports */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_T_ENABLE));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_R_ENABLE));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_W_ENABLE));
+
+ /* abort all groups of VDMAC */
+ /* g00, g01 and g01 */
+ val = 0x7U;
+ writel(val, &(res->capture_reg->vdm.VDM_ABORTSET));
+
+ /* Clear run flag of MAIN unit */
+ res->run_flag_main = false;
+
+ return ret;
+}
+
+#define VIIF_M_STATUS_DELAY_INT_FLAG BIT(24)
+#define VIIF_S_STATUS_DELAY_INT_FLAG BIT(24)
+
+/**
+ * hwd_VIIF_get_failure_status() - Get failure information of delayed vsync generating circuit and ecc circuit
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * @is_vsync_failure_main: vsync failure information of MAIN unit
+ * @is_vsync_failure_sub: vsync failure information of SUB unit(CH0/CH1) or VOIF loopback unit(CH2)
+ * @ecc_failure: failure information of ECC(CH0/CH1)
+ * Return: -EINVAL Parameter error
+ * - "is_vsync_failure_main", "is_vsync_failure_sub" or "ecc_failure" is NULL
+ */
+int32_t hwd_VIIF_get_failure_status(uint32_t module_id, bool *is_vsync_failure_main,
+ bool *is_vsync_failure_sub, uint32_t *ecc_failure)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if ((is_vsync_failure_main == NULL) || (is_vsync_failure_sub == NULL) ||
+ (ecc_failure == NULL)) {
+ return -EINVAL;
+ }
+
+ *is_vsync_failure_main = false;
+ *is_vsync_failure_sub = false;
+ *ecc_failure = 0U;
+
+ if ((readl(&res->capture_reg->sys.INT_M_STATUS) & VIIF_M_STATUS_DELAY_INT_FLAG))
+ *is_vsync_failure_main = true;
+
+ if ((readl(&res->capture_reg->sys.INT_S_STATUS) & VIIF_S_STATUS_DELAY_INT_FLAG))
+ *is_vsync_failure_sub = true;
+
+ if (res->ch != 2U)
+ *ecc_failure = readl(&res->capture_reg->sys.MEM_ECC_DCLS_ALARM);
+
+ return 0;
+}
+
+/* Convert the unit of time-period (from sysclk, to num lines in the image) */
+#define SYSCLK_TO_NUMLINES(time_in_sysclk, img) \
+ ((uint32_t)(((time_in_sysclk) * (uint64_t)(img)->pixel_clock) / \
+ ((uint64_t)(img)->htotal_size * (HWD_VIIF_SYS_CLK))))
+
+#define LINEPERIOD_IN_SYSCLK(hsize, pixel_clock) \
+ ((uint32_t)((uint64_t)(hsize)*HWD_VIIF_SYS_CLK / (uint64_t)(pixel_clock)))
+
+/**
+ * hwd_VIIF_main_set_unit_w_isp() - Set static configuration of MAIN unit(CH0 or CH1)
+ *
+ * @dt_image: DT of image [0x0, 0x10-0x17, 0x1B, 0x1E, 0x1F, 0x22, 0x24-0x27, 0x2A-0x3F])
+ * @dt_long_packet: DT of long packet data [0x0, 0x10-0x3F]
+ * @in_img: Pointer to input image information
+ * @color_type: Color type of image [0x0, 0x1E, 0x1F, 0x22, 0x24, 0x2A-0x2D]
+ * @rawpack: RAW pack mode. For more refer @ref hwd_VIIF_raw_pack_mode
+ * @yuv_conv: YUV422 to YUV444 conversion mode. For more refer @ref hwd_VIIF_yuv_conversion_mode
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "dt_image" is out of range
+ * - [2] "dt_long_packet" is out of range
+ * - [3] Both "dt_image" and "dt_long_packet" are 0x0
+ * - [4] "in_img" is NULL
+ * - [5] member of "in_img" is invalid
+ * - [6] "color_type" is out of range
+ * - [7] "color_type" doesn't meet the condition shown in the below note
+ * - [8] "rawpack" is out of range
+ * - [9] "rawpack" is not HWD_VIIF_RAWPACK_DISABLE when color_type is other than RAW(0x2A-0x2C)
+ * - [10] "yuv_conv" is out of range
+ * - [11] "yuv_conv" is not HWD_VIIF_YUV_CONV_REPEAT when color_type is other than YUV422(0x1E or 0x1F)
+ *
+ * Note: valid combination between "dt_image" and "color_type" is
+ * - when "dt_image" is [0x10-0x17, 0x1B, 0x25-0x27, 0x2E-0x3F], "color_type" must be [0x2A-0x2D].
+ * - when "dt_image" is valid value and other than [0x10-0x17, 0x1B, 0x25-0x27, 0x2E-0x3F], "color_type" must be "dt_image"
+ */
+int32_t hwd_VIIF_main_set_unit_w_isp(uint32_t module_id, uint32_t dt_image, uint32_t dt_long_packet,
+ const struct hwd_viif_input_img *in_img, uint32_t color_type,
+ uint32_t rawpack, uint32_t yuv_conv)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val, color, sysclk_num;
+ uint32_t sw_delay0, sw_delay1, hw_delay;
+ uint32_t total_hact_size = 0U, total_vact_size = 0U;
+
+ /* 0x00-0x09: ShortPacket/Undefined */
+ if ((dt_image > 0U) && (dt_image < 0x10U))
+ return -EINVAL;
+
+ /* 0x18-0x1A: YUV420 */
+ if ((dt_image > 0x17U) && (dt_image < 0x1bU))
+ return -EINVAL;
+
+ /* 0x1C-0x1D: YUV420 CSPS */
+ if ((dt_image > 0x1bU) && (dt_image < 0x1eU))
+ return -EINVAL;
+
+ /* 0x20,0x21,0x23: RGB444, RGB555, RGB666 */
+ if ((dt_image > 0x1fU) && (dt_image < 0x22U))
+ return -EINVAL;
+
+ if (dt_image == 0x23U)
+ return -EINVAL;
+
+ /* 0x28-0x29: RAW6, RAW7 */
+ if ((dt_image > 0x27U) && (dt_image < 0x2aU))
+ return -EINVAL;
+
+ /* 0x2E-: not supported */
+ if (dt_image > HWD_VIIF_CSI2_MAX_DT)
+ return -EINVAL;
+
+ if (
+ /*Case: Generic Long Packet, Reserved, User-Defined*/
+ ((dt_image >= 0x10U) && (dt_image <= 0x17U)) || (dt_image == 0x1bU) ||
+ ((dt_image >= 0x25U) && (dt_image <= 0x27U)) || (dt_image >= 0x2eU)) {
+ if ((color_type != CSI2_DT_RAW8) && (color_type != CSI2_DT_RAW10) &&
+ (color_type != CSI2_DT_RAW12) && (color_type != CSI2_DT_RAW14)) {
+ return -EINVAL;
+ }
+ } else {
+ /*Case: Otherwise: YUV, RGB, RAW*/
+ /*Constraint: color_type must be dt_image*/
+ if (color_type != dt_image)
+ return -EINVAL;
+ }
+ if (((dt_long_packet > 0x0U) && (dt_long_packet < 0x10U)) ||
+ (dt_long_packet > HWD_VIIF_CSI2_MAX_DT)) {
+ return -EINVAL;
+ }
+ if ((dt_image == 0U) && (dt_long_packet == 0U))
+ return -EINVAL;
+
+ if (in_img == NULL)
+ return -EINVAL;
+ if ((rawpack != HWD_VIIF_RAWPACK_DISABLE) && (rawpack != HWD_VIIF_RAWPACK_MSBFIRST) &&
+ (rawpack != HWD_VIIF_RAWPACK_LSBFIRST)) {
+ return -EINVAL;
+ }
+ if ((color_type != CSI2_DT_RAW8) && (color_type != CSI2_DT_RAW10) &&
+ (color_type != CSI2_DT_RAW12) && (rawpack != HWD_VIIF_RAWPACK_DISABLE)) {
+ return -EINVAL;
+ }
+
+ if (dt_image == 0U) {
+ /* only long packet data */
+ if ((in_img->pixel_clock != 0U) ||
+ (in_img->htotal_size < HWD_VIIF_MIN_HTOTAL_NSEC) ||
+ (in_img->htotal_size > HWD_VIIF_MAX_HTOTAL_NSEC) ||
+ (in_img->vbp_size < HWD_VIIF_MIN_VBP_PACKET) ||
+ (in_img->vbp_size > HWD_VIIF_MAX_VBP_PACKET) || (in_img->hactive_size != 0U) ||
+ (in_img->vtotal_size != 0U) || (in_img->vactive_size != 0U) ||
+ (in_img->interpolation_mode != HWD_VIIF_L1_INPUT_INTERPOLATION_LINE) ||
+ (in_img->input_num != HWD_VIIF_L1_INPUT_NUM_MIN) ||
+ (in_img->hobc_width != 0U) || (in_img->hobc_margin != 0U)) {
+ return -EINVAL;
+ }
+ } else {
+ /* image data will be input */
+ if ((in_img->pixel_clock < HWD_VIIF_MIN_PIXEL_CLOCK) ||
+ (in_img->pixel_clock > HWD_VIIF_MAX_PIXEL_CLOCK) ||
+ (in_img->htotal_size < HWD_VIIF_MIN_HTOTAL_PIXEL) ||
+ (in_img->htotal_size > HWD_VIIF_MAX_HTOTAL_PIXEL) ||
+ (in_img->vtotal_size < HWD_VIIF_MIN_VTOTAL_LINE) ||
+ (in_img->vtotal_size > HWD_VIIF_MAX_VTOTAL_LINE) ||
+ (in_img->vbp_size < HWD_VIIF_MIN_VBP_LINE) ||
+ (in_img->vbp_size > HWD_VIIF_MAX_VBP_LINE) ||
+ ((in_img->hactive_size % 2U) != 0U) || ((in_img->vactive_size % 2U) != 0U)) {
+ return -EINVAL;
+ }
+
+ if ((in_img->interpolation_mode != HWD_VIIF_L1_INPUT_INTERPOLATION_LINE) &&
+ (in_img->interpolation_mode != HWD_VIIF_L1_INPUT_INTERPOLATION_PIXEL)) {
+ return -EINVAL;
+ }
+
+ if ((in_img->input_num < HWD_VIIF_L1_INPUT_NUM_MIN) ||
+ (in_img->input_num > HWD_VIIF_L1_INPUT_NUM_MAX)) {
+ return -EINVAL;
+ }
+
+ if ((in_img->hobc_width != 0U) && (in_img->hobc_width != 16U) &&
+ (in_img->hobc_width != 32U) && (in_img->hobc_width != 64U) &&
+ (in_img->hobc_width != 128U)) {
+ return -EINVAL;
+ }
+
+ if ((in_img->hobc_margin > 30U) || ((in_img->hobc_margin % 2U) != 0U))
+ return -EINVAL;
+
+ if ((in_img->hobc_width == 0U) && (in_img->hobc_margin != 0U))
+ return -EINVAL;
+
+ if ((in_img->hobc_width != 0U) && (in_img->hobc_margin == 0U))
+ return -EINVAL;
+
+ if ((color_type == CSI2_DT_RAW8) || (color_type == CSI2_DT_RAW10) ||
+ (color_type == CSI2_DT_RAW12) || (color_type == CSI2_DT_RAW14)) {
+ /* parameter check in case of L1ISP(in case of RAW) */
+ if ((in_img->hactive_size < HWD_VIIF_MIN_HACTIVE_PIXEL_W_L1ISP) ||
+ (in_img->hactive_size > HWD_VIIF_MAX_HACTIVE_PIXEL_W_L1ISP) ||
+ (in_img->vactive_size < HWD_VIIF_MIN_VACTIVE_LINE_W_L1ISP) ||
+ (in_img->vactive_size > HWD_VIIF_MAX_VACTIVE_LINE_W_L1ISP) ||
+ ((in_img->hactive_size % 8U) != 0U)) {
+ return -EINVAL;
+ }
+
+ /* check vbp range in case of L1ISP on */
+ /* the constant value "7" is configuration margin */
+ val = SYSCLK_TO_NUMLINES(HWD_VIIF_TABLE_LOAD_TIME +
+ HWD_VIIF_REGBUF_ACCESS_TIME * 2U,
+ in_img) +
+ HWD_VIIF_L1_DELAY_W_HDRC + 7U;
+ if (in_img->vbp_size < val)
+ return -EINVAL;
+
+ /* calculate total of horizontal active size and vertical active size */
+ if (rawpack != HWD_VIIF_RAWPACK_DISABLE) {
+ val = (in_img->hactive_size + in_img->hobc_width +
+ in_img->hobc_margin) *
+ 2U;
+ } else {
+ val = in_img->hactive_size + in_img->hobc_width +
+ in_img->hobc_margin;
+ }
+ if (in_img->interpolation_mode == HWD_VIIF_L1_INPUT_INTERPOLATION_LINE) {
+ total_hact_size = val;
+ total_vact_size = in_img->vactive_size * in_img->input_num;
+ } else {
+ total_hact_size = val * in_img->input_num;
+ total_vact_size = in_img->vactive_size;
+ }
+ } else {
+ /* OTHER input than RAW(L1ISP is off) */
+ if ((in_img->hactive_size < HWD_VIIF_MIN_HACTIVE_PIXEL_WO_L1ISP) ||
+ (in_img->hactive_size > HWD_VIIF_MAX_HACTIVE_PIXEL_WO_L1ISP) ||
+ (in_img->vactive_size < HWD_VIIF_MIN_VACTIVE_LINE_WO_L1ISP) ||
+ (in_img->vactive_size > HWD_VIIF_MAX_VACTIVE_LINE_WO_L1ISP) ||
+ (in_img->interpolation_mode != HWD_VIIF_L1_INPUT_INTERPOLATION_LINE) ||
+ (in_img->input_num != HWD_VIIF_L1_INPUT_NUM_MIN) ||
+ (in_img->hobc_width != 0U)) {
+ return -EINVAL;
+ }
+
+ /* check vbp range in case of L1ISP off */
+ /* the constant value "16" is configuration margin */
+ val = SYSCLK_TO_NUMLINES(HWD_VIIF_TABLE_LOAD_TIME +
+ HWD_VIIF_REGBUF_ACCESS_TIME,
+ in_img) +
+ 16U;
+ if (in_img->vbp_size < val)
+ return -EINVAL;
+
+ total_hact_size = in_img->hactive_size;
+ total_vact_size = in_img->vactive_size;
+ }
+
+ if ((in_img->htotal_size <= total_hact_size) ||
+ (in_img->vtotal_size <= (in_img->vbp_size + total_vact_size))) {
+ return -EINVAL;
+ }
+ }
+ if ((yuv_conv != HWD_VIIF_YUV_CONV_REPEAT) &&
+ (yuv_conv != HWD_VIIF_YUV_CONV_INTERPOLATION)) {
+ return -EINVAL;
+ }
+
+ if ((color_type != CSI2_DT_YUV4228B) && (color_type != CSI2_DT_YUV42210B) &&
+ (yuv_conv != HWD_VIIF_YUV_CONV_REPEAT)) {
+ return -EINVAL;
+ }
+
+ /* Set DT and color type of image data and DT of longpacket data */
+ writel((color_type << 8U) | dt_image, &(res->capture_reg->sys.IPORTM_MAIN_DT));
+ writel(dt_long_packet, &(res->capture_reg->sys.IPORTM_OTHER));
+ res->color_type = color_type;
+ res->dt_image_main_w_isp = dt_image;
+
+ /* Set back porch*/
+ writel((in_img->vbp_size << 16U) | HWD_VIIF_HBP_SYSCLK,
+ &(res->capture_reg->sys.BACK_PORCH_M));
+
+ /* single pulse of vsync is input to DPGM */
+ writel(HWD_VIIF_DPGM_VSYNC_PULSE, &(res->capture_reg->sys.DPGM_VSYNC_SOURCE));
+ if (dt_image == 0x0U) {
+ uint32_t temp_delay;
+ /* only long packet data */
+ /* Set Total size information and delay value for delayed Vsync in case of only long packet data */
+ sysclk_num = LINEPERIOD_IN_SYSCLK(in_img->htotal_size, 1000000U);
+ sysclk_num &= GENMASK(15, 0);
+ writel((in_img->vtotal_size << 16U) | sysclk_num,
+ &(res->capture_reg->sys.TOTALSIZE_M));
+ temp_delay = in_img->vbp_size - 4U;
+ temp_delay = min(temp_delay, 255U);
+ writel(temp_delay, &(res->capture_reg->sys.INT_M1_LINE));
+ } else {
+ uint32_t i, j;
+ /* image data will be input */
+ /* set preprocess type before L2ISP based on color_type. */
+ if ((color_type == CSI2_DT_YUV4228B) || (color_type == CSI2_DT_YUV42210B)) {
+ /* YUV422 */
+ color = 3U;
+ } else if ((color_type == CSI2_DT_RGB565) || (color_type == CSI2_DT_RGB888)) {
+ /* RGB */
+ color = 0U;
+ } else {
+ /* RGB or YUV444 from L1ISP */
+ color = 1U;
+ }
+ res->l2_input = (color << 4U);
+ writel(res->l2_input, &(res->capture_reg->sys.PREPROCCESS_FMTM));
+
+ /* set Total size and valid size information of image data */
+ sysclk_num = LINEPERIOD_IN_SYSCLK(in_img->htotal_size, in_img->pixel_clock);
+ sysclk_num &= GENMASK(15, 0);
+ writel((in_img->vtotal_size << 16U) | sysclk_num,
+ &(res->capture_reg->sys.TOTALSIZE_M));
+ writel((total_vact_size << 16U) | total_hact_size,
+ &(res->capture_reg->sys.VALSIZE_M));
+
+ /* set image size information to L2ISP */
+ writel(in_img->vactive_size, &(res->capture_reg->l2isp.L2_SENSOR_CROP_VSIZE));
+ writel(in_img->hactive_size, &(res->capture_reg->l2isp.L2_SENSOR_CROP_HSIZE));
+
+ /* RAW input case */
+ if (color_type >= CSI2_DT_RAW8) {
+ val = (in_img->interpolation_mode << 3U) | (in_img->input_num);
+ writel(val, &(res->capture_reg->l1isp.L1_IBUF_INPUT_ORDER));
+ res->interpolation_mode = in_img->interpolation_mode;
+ res->input_num = in_img->input_num;
+ writel(in_img->vactive_size, &(res->capture_reg->l1isp.L1_SYSM_HEIGHT));
+ writel(in_img->hactive_size, &(res->capture_reg->l1isp.L1_SYSM_WIDTH));
+ val = (in_img->hobc_margin << 8U) | in_img->hobc_width;
+ writel(val, &(res->capture_reg->l1isp.L1_HOBC_MARGIN));
+ res->hobc_size = in_img->hobc_margin + in_img->hobc_width;
+ }
+
+ /* Set rawpack */
+ writel(rawpack, &(res->capture_reg->sys.IPORTM_MAIN_RAW));
+ res->rawpack = rawpack;
+
+ /* Set yuv_conv */
+ writel(yuv_conv, &(res->capture_reg->sys.PREPROCCESS_C24M));
+
+ /* Set vsync delay */
+ hw_delay = in_img->vbp_size - SYSCLK_TO_NUMLINES(HWD_VIIF_TABLE_LOAD_TIME, in_img) +
+ 4U;
+ hw_delay = min(hw_delay, 255U);
+
+ sw_delay0 = hw_delay - SYSCLK_TO_NUMLINES(HWD_VIIF_REGBUF_ACCESS_TIME, in_img) + 2U;
+
+ if ((color_type == CSI2_DT_RAW8) || (color_type == CSI2_DT_RAW10) ||
+ (color_type == CSI2_DT_RAW12) || (color_type == CSI2_DT_RAW14)) {
+ sw_delay1 = SYSCLK_TO_NUMLINES(HWD_VIIF_REGBUF_ACCESS_TIME, in_img) +
+ HWD_VIIF_L1_DELAY_WO_HDRC + 1U;
+ } else {
+ sw_delay1 = 10U;
+ }
+ writel(sw_delay0 << 16U, &(res->capture_reg->sys.INT_M0_LINE));
+ writel((sw_delay1 << 16U) | hw_delay, &(res->capture_reg->sys.INT_M1_LINE));
+
+ /* M2_LINE is the same condition as M1_LINE */
+ writel((sw_delay1 << 16U) | hw_delay, &(res->capture_reg->sys.INT_M2_LINE));
+
+ /* Update internal information of pixel clock, htotal_size, information of L2 ROI */
+ res->pixel_clock = in_img->pixel_clock;
+ res->htotal_size = in_img->htotal_size;
+ for (i = 0; i < HWD_VIIF_ISP_MAX_REGBUF_NUM; i++) {
+ res->l2_roi_path_info[i].roi_num = 0;
+ for (j = 0; j < HWD_VIIF_MAX_POST_NUM; j++) {
+ res->l2_roi_path_info[i].post_enable_flag[j] = false;
+ res->l2_roi_path_info[i].post_crop_x[j] = 0;
+ res->l2_roi_path_info[i].post_crop_y[j] = 0;
+ res->l2_roi_path_info[i].post_crop_w[j] = 0;
+ res->l2_roi_path_info[i].post_crop_h[j] = 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_main_set_unit() - Set static configuration of MAIN unit
+ *
+ * @dt_image: DT of image
+ * - [0x0, 0x10-0x17, 0x1B, 0x1E, 0x1F, 0x22, 0x24-0x27, 0x2A-0x2D, 0x2E-0x3F](CH0 and CH1)
+ * @dt_long_packet: DT of long packet data [0x0, 0x10-0x3F]
+ * @in_img: Pointer to input image information
+ * @color_type: Color type of image [0x0, 0x1E, 0x1F, 0x22, 0x24, 0x2A-0x2D]
+ * @rawpack: RAW pack mode. For more refer @ref hwd_VIIF_raw_pack_mode
+ * @yuv_conv: YUV422 to YUV444 conversion mode. For more refer @ref hwd_VIIF_yuv_conversion_mode
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] #hwd_VIIF_main_set_unit_w_isp returns parameter error(CH0 or CH1)
+ */
+int32_t hwd_VIIF_main_set_unit(uint32_t module_id, uint32_t dt_image, uint32_t dt_long_packet,
+ const struct hwd_viif_input_img *in_img, uint32_t color_type,
+ uint32_t rawpack, uint32_t yuv_conv)
+{
+ return hwd_VIIF_main_set_unit_w_isp(module_id, dt_image, dt_long_packet, in_img, color_type,
+ rawpack, yuv_conv);
+}
+
+/**
+ * hwd_VIIF_main_mask_vlatch() - Control Vlatch mask of MAIN unit
+ *
+ * @enable: or disable Vlatch mask of MAIN unit. For more refer @ref hwd_VIIF_enable_flag.
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - "enable" is out of range
+ */
+int32_t hwd_VIIF_main_mask_vlatch(uint32_t module_id, uint32_t enable)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ if ((enable != HWD_VIIF_ENABLE) && (enable != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if ((res->ch != 0U) && (res->ch != 1U))
+ return -EINVAL;
+
+ if (enable == HWD_VIIF_ENABLE)
+ enable |= HWD_VIIF_ISP_VLATCH_MASK;
+
+ /* Control Vlatch mask */
+ writel(enable, &(res->capture_reg->sys.IPORTM0_LD));
+ writel(enable, &(res->capture_reg->sys.IPORTM1_LD));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_main_status_err_set_irq_mask() - Set mask condition for STATUS error of MAIN unit
+ *
+ * @mask: Pointer to STATUS error mask condition
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_main_status_err_set_irq_mask(uint32_t module_id, const uint32_t *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ writel(*mask, &(res->capture_reg->sys.INT_M_MASK));
+}
+
+/**
+ * hwd_VIIF_main_vsync_set_irq_mask() - Set mask condition for Vsync of MAIN unit
+ *
+ * @mask: Pointer to Vsync mask condition
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_main_vsync_set_irq_mask(uint32_t module_id, const uint32_t *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ writel(*mask, &(res->capture_reg->sys.INT_M_SYNC_MASK));
+}
+
+/**
+ * hwd_VIIF_main_get_next_frame_info() - Get next frame information of MAIN unit
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * @count0: count incremented by Vsync
+ * @count1: count incremented by delay_sync1
+ * @count2: count incremented by delay_sync2
+ * @hist_paddr: output address of L1ISP histogram data(physical address)
+ * @addr_info: output address of vdmac
+ * Return: None
+ */
+void hwd_VIIF_main_get_next_frame_info(uint32_t module_id, uint32_t *count0, uint32_t *count1,
+ uint32_t *count2, uint32_t *hist_paddr,
+ struct hwd_viif_main_transfer_addr_info *addr_info)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ if ((res->ch != 0U) && (res->ch != 1U))
+ return;
+
+ *count0 = readl(&res->capture_reg->sys.FN_M0);
+ *count1 = readl(&res->capture_reg->sys.FN_M1);
+ *count2 = readl(&res->capture_reg->sys.FN_M2);
+ addr_info->img_g_y_paddr = 0;
+ addr_info->img_b_u_paddr = 0;
+ addr_info->img_r_v_paddr = 0;
+ addr_info->long_packet_paddr = readl(&res->capture_reg->vdm.w_port_buf[0].VDM_W_STADR_BUF);
+ addr_info->emb_paddr = readl(&res->capture_reg->vdm.w_port_buf[1].VDM_W_STADR_BUF);
+ *hist_paddr = readl(&res->capture_reg->vdm.w_port_buf[2].VDM_W_STADR_BUF);
+}
+
+#define VDM_BIT_W00 BIT(0)
+#define VDM_BIT_W01 BIT(1)
+#define VDM_BIT_W02 BIT(2)
+#define VDM_BIT_W03 BIT(3)
+#define VDM_BIT_W04 BIT(4)
+#define VDM_BIT_W05 BIT(5)
+#define VDM_BIT_R00 BIT(0)
+#define VDM_BIT_R01 BIT(1)
+#define VDM_BIT_R02 BIT(2)
+
+#define VDM_ABORT_MASK_SUB_W (VDM_BIT_W03 | VDM_BIT_W04 | VDM_BIT_W05)
+#define VDM_ABORT_MASK_MAIN_W (VDM_BIT_W00 | VDM_BIT_W01 | VDM_BIT_W02)
+#define VDM_ABORT_MASK_MAIN_R (VDM_BIT_R00 | VDM_BIT_R01 | VDM_BIT_R02)
+
+/**
+ * hwd_VIIF_main_get_previous_frame_info() - Get and clear previous frame information of MAIN unit
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * @abort_r: abort information of read port
+ * @abort_w: abort information of write port
+ * @packet_info: CSI-2 packet information
+ * Return: None
+ */
+void hwd_VIIF_main_get_previous_frame_info(uint32_t module_id, uint32_t *abort_r, uint32_t *abort_w,
+ struct hwd_viif_csi2rx_capture_info *packet_info)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ if ((res->ch != 0U) && (res->ch != 1U))
+ return;
+
+ *abort_r = readl(&res->capture_reg->vdm.VDM_R_STOP) & VDM_ABORT_MASK_MAIN_R;
+ writel(*abort_r, &(res->capture_reg->vdm.VDM_R_STOP));
+ *abort_w = readl(&res->capture_reg->vdm.VDM_W_STOP) & VDM_ABORT_MASK_MAIN_W;
+ writel(*abort_w, &(res->capture_reg->vdm.VDM_W_STOP));
+
+ val = readl(&res->capture_reg->sys.MAINIMG_PKTSIZE);
+ packet_info->img_size_1st = val & GENMASK(15, 0);
+ packet_info->img_size_2nd = val >> 16U;
+ packet_info->img_num = readl(&res->capture_reg->sys.MAINIMG_HEIGHT);
+ val = readl(&res->capture_reg->sys.MAINEMBTOP_SIZE);
+ packet_info->emb_top_size = val & GENMASK(15, 0);
+ packet_info->emb_top_num = val >> 16U;
+ val = readl(&res->capture_reg->sys.MAINEMBBOT_SIZE);
+ packet_info->emb_bottom_size = val & GENMASK(15, 0);
+ packet_info->emb_bottom_num = val >> 16U;
+ val = readl(&res->capture_reg->sys.MAINOTHER_PKTSIZE);
+ packet_info->long_packet_size_1st = val & GENMASK(15, 0);
+ packet_info->long_packet_size_2nd = val >> 16U;
+ packet_info->long_packet_num = readl(&res->capture_reg->sys.MAINOTHER_HEIGHT);
+}
+
+/**
+ * hwd_VIIF_sub_set_unit() - Set static configuration of SUB unit
+ *
+ * @dt_image: DT of image [0x0, 0x1E, 0x1F, 0x22, 0x24, 0x2A-0x2D]
+ * @dt_long_packet: DT of long packet data [0x0, 0x10-0x3F]
+ * @in_img: Pointer to input image information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "dt_image" is out of range
+ * - [2] "dt_long_packet" is out of range
+ * - [3] Both "dt_image" and "dt_long_packet" are 0x0
+ * - [4] "in_img" is NULL
+ * - [5] member of "in_img" is invalid
+ */
+int32_t hwd_VIIF_sub_set_unit(uint32_t module_id, uint32_t dt_image, uint32_t dt_long_packet,
+ const struct hwd_viif_input_img *in_img)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t sysclk_num, temp_delay;
+
+ if (((dt_image > 0U) && (dt_image < 0x2aU)) || (dt_image > 0x2dU))
+ return -EINVAL;
+
+ if (((dt_long_packet > 0x0U) && (dt_long_packet < 0x10U)) ||
+ (dt_long_packet > HWD_VIIF_CSI2_MAX_DT)) {
+ return -EINVAL;
+ }
+
+ if ((dt_image == 0U) && (dt_long_packet == 0U))
+ return -EINVAL;
+
+ if (in_img == NULL)
+ return -EINVAL;
+
+ if ((in_img->hactive_size != 0U) ||
+ (in_img->interpolation_mode != HWD_VIIF_L1_INPUT_INTERPOLATION_LINE) ||
+ (in_img->input_num != HWD_VIIF_L1_INPUT_NUM_MIN) || (in_img->hobc_width != 0U) ||
+ (in_img->hobc_margin != 0U)) {
+ return -EINVAL;
+ }
+
+ if (dt_image == 0U) {
+ /* only long packet data */
+ if ((in_img->pixel_clock != 0U) ||
+ (in_img->htotal_size < HWD_VIIF_MIN_HTOTAL_NSEC) ||
+ (in_img->htotal_size > HWD_VIIF_MAX_HTOTAL_NSEC) ||
+ (in_img->vbp_size < HWD_VIIF_MIN_VBP_PACKET) ||
+ (in_img->vbp_size > HWD_VIIF_MAX_VBP_PACKET) || (in_img->vtotal_size != 0U) ||
+ (in_img->vactive_size != 0U)) {
+ return -EINVAL;
+ }
+ } else {
+ /* image data will be input */
+ if ((in_img->pixel_clock < HWD_VIIF_MIN_PIXEL_CLOCK) ||
+ (in_img->pixel_clock > HWD_VIIF_MAX_PIXEL_CLOCK) ||
+ (in_img->htotal_size < HWD_VIIF_MIN_HTOTAL_PIXEL) ||
+ (in_img->htotal_size > HWD_VIIF_MAX_HTOTAL_PIXEL) ||
+ (in_img->vtotal_size < HWD_VIIF_MIN_VTOTAL_LINE) ||
+ (in_img->vtotal_size > HWD_VIIF_MAX_VTOTAL_LINE) ||
+ (in_img->vbp_size < HWD_VIIF_MIN_VBP_LINE) ||
+ (in_img->vbp_size > HWD_VIIF_MAX_VBP_LINE) ||
+ (in_img->vactive_size < HWD_VIIF_MIN_VACTIVE_LINE_WO_L1ISP) ||
+ (in_img->vactive_size > HWD_VIIF_MAX_VACTIVE_LINE_WO_L1ISP) ||
+ ((in_img->vactive_size % 2U) != 0U)) {
+ return -EINVAL;
+ }
+
+ if (in_img->vtotal_size <= (in_img->vbp_size + in_img->vactive_size))
+ return -EINVAL;
+ }
+
+ /* Set DT of image data and DT of long packet data*/
+ writel(dt_image, &(res->capture_reg->sys.IPORTS_MAIN_DT));
+ writel(dt_long_packet, &(res->capture_reg->sys.IPORTS_OTHER));
+
+ if (dt_image == 0x0U)
+ sysclk_num = LINEPERIOD_IN_SYSCLK(in_img->htotal_size, 1000000U);
+ else
+ sysclk_num = LINEPERIOD_IN_SYSCLK(in_img->htotal_size, in_img->pixel_clock);
+
+ /* Set line size and delay value of delayed Vsync */
+ writel(sysclk_num & GENMASK(15, 0), &(res->capture_reg->sys.INT_SA0_LINE));
+ temp_delay = in_img->vbp_size - 4U;
+ if (temp_delay > 255U) {
+ /* Replace the value with HW max spec */
+ temp_delay = 255U;
+ }
+ writel(temp_delay, &(res->capture_reg->sys.INT_SA1_LINE));
+
+ return 0;
+}
+
+#define VDMAC_SRAM_BASE_ADDR_W03 0x440U
+
+/**
+ * hwd_VIIF_sub_set_img_transmission() - Set image transfer condition of SUB unit
+ *
+ * @img: Pointer to output image information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] Member of "img" is invalid
+ */
+int32_t hwd_VIIF_sub_set_img_transmission(uint32_t module_id, const struct hwd_viif_img *img)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t data_width, pitch, height, img_start_addr, img_end_addr;
+ uint32_t k, port_control;
+
+ if (img != NULL) {
+ if ((((img->width) % 2U) != 0U) || (((img->height) % 2U) != 0U))
+ return -EINVAL;
+
+ if ((img->width < HWD_VIIF_MIN_OUTPUT_IMG_WIDTH) ||
+ (img->height < HWD_VIIF_MIN_OUTPUT_IMG_HEIGHT) ||
+ (img->width > HWD_VIIF_MAX_OUTPUT_IMG_WIDTH_SUB) ||
+ (img->height > HWD_VIIF_MAX_OUTPUT_IMG_HEIGHT_SUB)) {
+ return -EINVAL;
+ }
+
+ img_start_addr = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch = img->pixelmap[0].pitch;
+ height = img->height;
+
+ switch (img->format) {
+ case HWD_VIIF_ONE_COLOR_8:
+ data_width = 0U;
+ img_end_addr = img_start_addr + img->width - 1U;
+ k = 1;
+ break;
+ case HWD_VIIF_ONE_COLOR_16:
+ data_width = 1U;
+ img_end_addr = img_start_addr + (img->width * 2U) - 1U;
+ k = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((img_start_addr % 4U) != 0U)
+ return -EINVAL;
+
+ if ((pitch < (img->width * k)) || (pitch > HWD_VIIF_MAX_PITCH) ||
+ ((pitch % 4U) != 0U)) {
+ return -EINVAL;
+ }
+
+ writel(VDMAC_SRAM_BASE_ADDR_W03,
+ &(res->capture_reg->vdm.w_port[3].VDM_W_SRAM_BASE));
+ writel(res->sram_size_w_port, &(res->capture_reg->vdm.w_port[3].VDM_W_SRAM_SIZE));
+ writel(img_start_addr, &(res->capture_reg->vdm.w_port[3].VDM_W_STADR));
+ writel(img_end_addr, &(res->capture_reg->vdm.w_port[3].VDM_W_ENDADR));
+ writel(height, &(res->capture_reg->vdm.w_port[3].VDM_W_HEIGHT));
+ writel(pitch, &(res->capture_reg->vdm.w_port[3].VDM_W_PITCH));
+ writel(data_width << 8U, &(res->capture_reg->vdm.w_port[3].VDM_W_CFG0));
+ port_control = ((uint32_t)1U << 3U) | readl(&res->capture_reg->vdm.VDM_W_ENABLE);
+ writel(port_control, &(res->capture_reg->vdm.VDM_W_ENABLE));
+ writel(HWD_VIIF_ENABLE, &(res->capture_reg->sys.IPORTS_IMGEN));
+ } else {
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_IMGEN));
+ port_control = ~((uint32_t)1U << 3U) & readl(&res->capture_reg->vdm.VDM_W_ENABLE);
+ writel(port_control, &(res->capture_reg->vdm.VDM_W_ENABLE));
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_sub_mask_vlatch() - Control Vlatch mask of SUB unit or VOIF loopback
+ *
+ * @enable: or disable Vlatch mask of SUB unit. For more refer @ref hwd_VIIF_enable_flag.
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - "enable" is out of range
+ */
+int32_t hwd_VIIF_sub_mask_vlatch(uint32_t module_id, uint32_t enable)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if ((enable != HWD_VIIF_ENABLE) && (enable != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ /* Control Vlach mask */
+ writel(enable, &(res->capture_reg->sys.IPORTS0_LD));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_sub_status_err_set_irq_mask() - Set mask condition for STATUS error of SUB unit or VOIF loopback
+ *
+ * @mask: Pointer to STATUS error mask condition
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_sub_status_err_set_irq_mask(uint32_t module_id, const uint32_t *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ writel(*mask, &(res->capture_reg->sys.INT_S_MASK));
+}
+
+/**
+ * hwd_VIIF_sub_vsync_set_irq_mask() - Set mask condition for Vsync of SUB unit or VOIF loopback
+ *
+ * @mask: Pointer to Vsync mask condition
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_sub_vsync_set_irq_mask(uint32_t module_id, const uint32_t *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ writel(*mask, &(res->capture_reg->sys.INT_S_SYNC_MASK));
+}
+
+/**
+ * hwd_VIIF_sub_get_next_frame_info() - Get next frame information of SUB unit
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * @count0: count incremented by Vsync
+ * @count1: count incremented by delay_sync1
+ * @img_paddr: output address of image data(physical address)
+ * @emb_paddr: output address of embedded data(physical address)
+ * @long_packet_paddr: output address of long packet data(physical address)
+ * Return: None
+ */
+void hwd_VIIF_sub_get_next_frame_info(uint32_t module_id, uint32_t *count0, uint32_t *count1,
+ uint32_t *img_paddr, uint32_t *emb_paddr,
+ uint32_t *long_packet_paddr)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ *count0 = readl(&res->capture_reg->sys.FN_SA0);
+ *count1 = readl(&res->capture_reg->sys.FN_SA1);
+ *img_paddr = readl(&res->capture_reg->vdm.w_port_buf[3].VDM_W_STADR_BUF);
+ *emb_paddr = readl(&res->capture_reg->vdm.w_port_buf[5].VDM_W_STADR_BUF);
+ *long_packet_paddr = readl(&res->capture_reg->vdm.w_port_buf[4].VDM_W_STADR_BUF);
+}
+
+/**
+ * hwd_VIIF_sub_get_previous_frame_info() - Get and clear previous frame information of SUB unit
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * @abort_w: abort information of write port
+ * @packet_info: CSI-2 packet information
+ * Return: None
+ */
+void hwd_VIIF_sub_get_previous_frame_info(uint32_t module_id, uint32_t *abort_w,
+ struct hwd_viif_csi2rx_capture_info *packet_info)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ val = VDM_ABORT_MASK_SUB_W & readl(&res->capture_reg->vdm.VDM_W_STOP);
+ *abort_w = val;
+ writel(val, &(res->capture_reg->vdm.VDM_W_STOP));
+ val = readl(&res->capture_reg->sys.SUBIMG_PKTSIZE);
+ packet_info->img_size_1st = val & GENMASK(15, 0);
+ packet_info->img_size_2nd = val >> 16U;
+ packet_info->img_num = readl(&res->capture_reg->sys.SUBIMG_HEIGHT);
+ val = readl(&res->capture_reg->sys.SUBEMBTOP_SIZE);
+ packet_info->emb_top_size = val & GENMASK(15, 0);
+ packet_info->emb_top_num = val >> 16U;
+ val = readl(&res->capture_reg->sys.SUBEMBBOT_SIZE);
+ packet_info->emb_bottom_size = val & GENMASK(15, 0);
+ packet_info->emb_bottom_num = val >> 16U;
+ val = readl(&res->capture_reg->sys.SUBOTHER_PKTSIZE);
+ packet_info->long_packet_size_1st = val & GENMASK(15, 0);
+ packet_info->long_packet_size_2nd = val >> 16U;
+ packet_info->long_packet_num = readl(&res->capture_reg->sys.SUBOTHER_HEIGHT);
+}
+
+/**
+ * hwd_VIIF_isp_set_regbuf_auto_transmission() - Set register buffer auto transmission
+ *
+ * @regbuf_id_read: register buffer ID for read transmission [0..3]
+ * @regbuf_id_write: register buffer ID for write transmission [0..3]
+ * @context_id: context ID [0..3]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_isp_set_regbuf_auto_transmission(uint32_t module_id, uint32_t regbuf_id_read,
+ uint32_t regbuf_id_write, uint32_t context_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ (void)regbuf_id_read;
+ (void)regbuf_id_write;
+ (void)context_id;
+
+ /* Set parameters for auto read transmission of register buffer */
+
+ if (res->dt_image_main_w_isp != 0x0U) {
+ /* configuration is done only when dt_image is not 0, means image data is input to ISP. */
+ writel(0x0, &(res->capture_reg->l1isp.L1_CRGBF_TRN_A_CONF));
+ writel(0x0, &(res->capture_reg->l2isp.L2_CRGBF_TRN_A_CONF));
+ writel(HWD_VIIF_L1_CRGBF_R_START_ADDR_LIMIT,
+ &(res->capture_reg->l1isp.L1_CRGBF_TRN_RBADDR));
+ writel(HWD_VIIF_L1_CRGBF_R_END_ADDR_LIMIT,
+ &(res->capture_reg->l1isp.L1_CRGBF_TRN_READDR));
+ writel(HWD_VIIF_L2_CRGBF_R_START_ADDR_LIMIT,
+ &(res->capture_reg->l2isp.L2_CRGBF_TRN_RBADDR));
+ writel(HWD_VIIF_L2_CRGBF_R_END_ADDR_LIMIT,
+ &(res->capture_reg->l2isp.L2_CRGBF_TRN_READDR));
+ val = BIT(16);
+ writel(val, &(res->capture_reg->l2isp.L2_CRGBF_TRN_A_CONF));
+ writel(val, &(res->capture_reg->l1isp.L1_CRGBF_TRN_A_CONF));
+ }
+}
+
+/**
+ * hwd_VIIF_isp_disable_regbuf_auto_transmission() - Disable register buffer auto transmission
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_isp_disable_regbuf_auto_transmission(uint32_t module_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if (res->dt_image_main_w_isp != 0x0U) {
+ writel(0x0, &(res->capture_reg->l1isp.L1_CRGBF_TRN_A_CONF));
+ writel(0x0, &(res->capture_reg->l2isp.L2_CRGBF_TRN_A_CONF));
+ }
+}
+
+#define L2_STATUS_REPORT_MASK 0x1eU
+
+/**
+ * hwd_VIIF_isp_get_info() - Get processing information of L1ISP and L2ISP
+ *
+ * @regbuf_id: register buffer ID [0..3]
+ * @regbuf_id_info: Latest register buffer ID
+ * @l1_info: L1ISP processing information
+ * @l2_addr_info: output address information of L2ISP
+ * @l2_transfer_status: status of L2ISP transmission
+ * @l1_regbuf_status: register buffer status of L1ISP
+ * @l2_regbuf_status: register buffer status of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_isp_get_info(uint32_t module_id, uint32_t regbuf_id, uint32_t *regbuf_id_info,
+ struct hwd_viif_l1_info *l1_info,
+ struct hwd_viif_l2_transfer_addr_info *l2_addr_info,
+ uint32_t *l2_transfer_status,
+ struct hwd_viif_isp_regbuf_status *l1_regbuf_status,
+ struct hwd_viif_isp_regbuf_status *l2_regbuf_status)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val, l2_status;
+
+ if (l1_info != NULL) {
+ /* change register buffer to regbuf0 where driver gets information */
+ writel(HWD_VIIF_ISP_REGBUF_MODE_BUFFER,
+ &(res->capture_reg->l1isp.L1_CRGBF_ACC_CONF));
+
+ /* get AWB info */
+ l1_info->awb_ave_u = readl(&res->capture_reg->l1isp.L1_AWHB_AVE_USIG);
+ l1_info->awb_ave_v = readl(&res->capture_reg->l1isp.L1_AWHB_AVE_VSIG);
+ l1_info->awb_accumulated_pixel = readl(&res->capture_reg->l1isp.L1_AWHB_NUM_UVON);
+ l1_info->awb_gain_r = readl(&res->capture_reg->l1isp.L1_AWHB_AWBGAINR);
+ l1_info->awb_gain_g = readl(&res->capture_reg->l1isp.L1_AWHB_AWBGAING);
+ l1_info->awb_gain_b = readl(&res->capture_reg->l1isp.L1_AWHB_AWBGAINB);
+ val = readl(&res->capture_reg->l1isp.L1_AWHB_R_CTR_STOP);
+ l1_info->awb_status_u = (FIELD_GET(BIT(1), val) != 0);
+ l1_info->awb_status_v = (FIELD_GET(BIT(0), val) != 0);
+
+ /* revert to register access from register buffer access */
+ writel(HWD_VIIF_ISP_REGBUF_MODE_BYPASS,
+ &(res->capture_reg->l1isp.L1_CRGBF_ACC_CONF));
+ }
+
+ if (l2_transfer_status != NULL) {
+ /* get L2ISP abort information */
+ l2_status = readl(&res->capture_reg->l2isp.L2_CRGBF_ISP_INT);
+ writel(l2_status, &(res->capture_reg->l2isp.L2_CRGBF_ISP_INT));
+ *l2_transfer_status = l2_status & L2_STATUS_REPORT_MASK;
+ }
+}
+
+/**
+ * hwd_VIIF_isp_set_regbuf_irq_mask() - Set mask condition for ISP register buffer
+ *
+ * @mask_l1: Pointer to mask configuration for L1ISP register buffer interruption
+ * @mask_l2: Pointer to mask configuration for L2ISP register buffer interruption
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_isp_set_regbuf_irq_mask(uint32_t module_id, const uint32_t *mask_l1,
+ const uint32_t *mask_l2)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ writel(*mask_l1, &(res->capture_reg->l1isp.L1_CRGBF_INT_MASK));
+ writel(*mask_l2, &(res->capture_reg->l2isp.L2_CRGBF_INT_MASK));
+}
+
+/**
+ * hwd_VIIF_l2_set_input_path() - Set input path of L2ISP
+ *
+ * @is_other_ch: input path of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ */
+int32_t hwd_VIIF_l2_set_input_path(uint32_t module_id, bool is_other_ch)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t input;
+
+ if (is_other_ch)
+ input = HWD_VIIF_L2_INPUT_OTHER_CH;
+ else
+ input = res->l2_input;
+
+ writel(input, &(res->capture_reg->sys.PREPROCCESS_FMTM));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_input_csc() - Set input CSC parameters of L2ISP
+ *
+ * @param: Pointer to input csc parameters of L2ISP
+ * @is_l1_rgb: input information of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] Member of "param" is invalid
+ */
+int32_t hwd_VIIF_l2_set_input_csc(uint32_t module_id, const struct hwd_viif_csc_param *param,
+ bool is_l1_rgb)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t i, val;
+ uint32_t enable = HWD_VIIF_ENABLE;
+ struct hwd_viif_csc_param hwd_param;
+ bool csc_enable_flag = true;
+
+ if (param != NULL) {
+ if (param->r_cr_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->g_y_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->b_cb_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->r_cr_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->g_y_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->b_cb_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ for (i = 0; i < HWD_VIIF_CSC_MAX_COEF_NUM; i++) {
+ if (param->coef[i] > HWD_VIIF_CSC_MAX_COEF_VALUE)
+ return -EINVAL;
+ }
+
+ if (is_l1_rgb) {
+ /* translated parameters are used */
+ hwd_param.r_cr_in_offset = param->b_cb_in_offset;
+ hwd_param.g_y_in_offset = param->r_cr_in_offset;
+ hwd_param.b_cb_in_offset = param->g_y_in_offset;
+ hwd_param.r_cr_out_offset = param->r_cr_out_offset;
+ hwd_param.g_y_out_offset = param->g_y_out_offset;
+ hwd_param.b_cb_out_offset = param->b_cb_out_offset;
+ hwd_param.coef[0] = param->coef[2];
+ hwd_param.coef[1] = param->coef[0];
+ hwd_param.coef[2] = param->coef[1];
+ hwd_param.coef[3] = param->coef[5];
+ hwd_param.coef[4] = param->coef[3];
+ hwd_param.coef[5] = param->coef[4];
+ hwd_param.coef[6] = param->coef[8];
+ hwd_param.coef[7] = param->coef[6];
+ hwd_param.coef[8] = param->coef[7];
+ } else {
+ /* original parameters are used */
+ hwd_param.r_cr_in_offset = param->r_cr_in_offset;
+ hwd_param.g_y_in_offset = param->g_y_in_offset;
+ hwd_param.b_cb_in_offset = param->b_cb_in_offset;
+ hwd_param.r_cr_out_offset = param->r_cr_out_offset;
+ hwd_param.g_y_out_offset = param->g_y_out_offset;
+ hwd_param.b_cb_out_offset = param->b_cb_out_offset;
+ hwd_param.coef[0] = param->coef[0];
+ hwd_param.coef[1] = param->coef[1];
+ hwd_param.coef[2] = param->coef[2];
+ hwd_param.coef[3] = param->coef[3];
+ hwd_param.coef[4] = param->coef[4];
+ hwd_param.coef[5] = param->coef[5];
+ hwd_param.coef[6] = param->coef[6];
+ hwd_param.coef[7] = param->coef[7];
+ hwd_param.coef[8] = param->coef[8];
+ }
+ } else {
+ if (is_l1_rgb) {
+ /* fixed parameters are used */
+ hwd_param.r_cr_in_offset = 0U;
+ hwd_param.g_y_in_offset = 0U;
+ hwd_param.b_cb_in_offset = 0U;
+ hwd_param.r_cr_out_offset = 0U;
+ hwd_param.g_y_out_offset = 0U;
+ hwd_param.b_cb_out_offset = 0U;
+ hwd_param.coef[0] = 0U;
+ hwd_param.coef[1] = 0x1000U;
+ hwd_param.coef[2] = 0U;
+ hwd_param.coef[3] = 0U;
+ hwd_param.coef[4] = 0U;
+ hwd_param.coef[5] = 0x1000U;
+ hwd_param.coef[6] = 0x1000U;
+ hwd_param.coef[7] = 0U;
+ hwd_param.coef[8] = 0U;
+ } else {
+ /* csc is disabled */
+ enable = HWD_VIIF_DISABLE;
+ csc_enable_flag = false;
+ }
+ }
+
+ if (csc_enable_flag) {
+ writel(hwd_param.g_y_in_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_YG_OFFSETI));
+ writel(hwd_param.coef[0], &(res->capture_reg->sys.l2isp_input_csc.MTB_YG1));
+ val = (hwd_param.coef[1] << HWD_VIIF_MTB_CB_YG_COEF_OFFSET) |
+ (hwd_param.coef[2] << HWD_VIIF_MTB_CR_YG_COEF_OFFSET);
+ writel(val, &(res->capture_reg->sys.l2isp_input_csc.MTB_YG2));
+ writel(hwd_param.g_y_out_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_YG_OFFSETO));
+ writel(hwd_param.b_cb_in_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_CB_OFFSETI));
+ writel(hwd_param.coef[3], &(res->capture_reg->sys.l2isp_input_csc.MTB_CB1));
+ val = (hwd_param.coef[4] << HWD_VIIF_MTB_CB_CB_COEF_OFFSET) |
+ (hwd_param.coef[5] << HWD_VIIF_MTB_CR_CB_COEF_OFFSET);
+ writel(val, &(res->capture_reg->sys.l2isp_input_csc.MTB_CB2));
+ writel(hwd_param.b_cb_out_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_CB_OFFSETO));
+ writel(hwd_param.r_cr_in_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_CR_OFFSETI));
+ writel(hwd_param.coef[6], &(res->capture_reg->sys.l2isp_input_csc.MTB_CR1));
+ val = (hwd_param.coef[7] << HWD_VIIF_MTB_CB_CR_COEF_OFFSET) |
+ (hwd_param.coef[8] << HWD_VIIF_MTB_CR_CR_COEF_OFFSET);
+ writel(val, &(res->capture_reg->sys.l2isp_input_csc.MTB_CR2));
+ writel(hwd_param.r_cr_out_offset,
+ &(res->capture_reg->sys.l2isp_input_csc.MTB_CR_OFFSETO));
+ }
+
+ writel(enable, &(res->capture_reg->sys.l2isp_input_csc.MTB));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_undist() - Set undistortion parameters of L2ISP
+ *
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @param: Pointer to undistortion parameters of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "param" is NULL
+ * - [2] Member of "param" is invalid
+ */
+int32_t hwd_VIIF_l2_set_undist(uint32_t module_id, uint32_t regbuf_id,
+ const struct hwd_viif_l2_undist *param)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t i, val;
+ uint32_t grid_num_h, grid_num_v;
+
+ if (param == NULL)
+ return -EINVAL;
+
+ if ((param->through_mode != HWD_VIIF_ENABLE) && (param->through_mode != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if ((param->roi_mode != HWD_VIIF_L2_UNDIST_POLY) &&
+ (param->roi_mode != HWD_VIIF_L2_UNDIST_GRID) &&
+ (param->roi_mode != HWD_VIIF_L2_UNDIST_POLY_TO_GRID) &&
+ (param->roi_mode != HWD_VIIF_L2_UNDIST_GRID_TO_POLY)) {
+ return -EINVAL;
+ }
+ if (param->roi_write_area_delta >= HWD_VIIF_L2_UNDIST_MAX_ROI_WRITE_AREA_DELTA)
+ return -EINVAL;
+
+ if ((param->sensor_crop_ofs_h < HWD_VIIF_L2_UNDIST_MIN_SENSOR_CROP_OFS_H) ||
+ (param->sensor_crop_ofs_h > HWD_VIIF_L2_UNDIST_MAX_SENSOR_CROP_OFS_H)) {
+ return -EINVAL;
+ }
+
+ if ((param->sensor_crop_ofs_v < HWD_VIIF_L2_UNDIST_MIN_SENSOR_CROP_OFS_V) ||
+ (param->sensor_crop_ofs_v > HWD_VIIF_L2_UNDIST_MAX_SENSOR_CROP_OFS_V)) {
+ return -EINVAL;
+ }
+
+ if (param->norm_scale > HWD_VIIF_L2_UNDIST_MAX_NORM_SCALE)
+ return -EINVAL;
+
+ if (param->valid_r_norm2_poly >= HWD_VIIF_L2_UNDIST_MAX_VALID_R_NORM2)
+ return -EINVAL;
+
+ if (param->valid_r_norm2_grid >= HWD_VIIF_L2_UNDIST_MAX_VALID_R_NORM2)
+ return -EINVAL;
+
+ for (i = 0; i < HWD_VIIF_L2_UNDIST_POLY_NUM; i++) {
+ if ((param->poly_write_g_coef[i] < HWD_VIIF_L2_UNDIST_MIN_POLY_COEF) ||
+ (param->poly_write_g_coef[i] > HWD_VIIF_L2_UNDIST_MAX_POLY_COEF)) {
+ return -EINVAL;
+ }
+ if ((param->poly_read_b_coef[i] < HWD_VIIF_L2_UNDIST_MIN_POLY_COEF) ||
+ (param->poly_read_b_coef[i] > HWD_VIIF_L2_UNDIST_MAX_POLY_COEF)) {
+ return -EINVAL;
+ }
+ if ((param->poly_read_g_coef[i] < HWD_VIIF_L2_UNDIST_MIN_POLY_COEF) ||
+ (param->poly_read_g_coef[i] > HWD_VIIF_L2_UNDIST_MAX_POLY_COEF)) {
+ return -EINVAL;
+ }
+ if ((param->poly_read_r_coef[i] < HWD_VIIF_L2_UNDIST_MIN_POLY_COEF) ||
+ (param->poly_read_r_coef[i] > HWD_VIIF_L2_UNDIST_MAX_POLY_COEF)) {
+ return -EINVAL;
+ }
+ }
+
+ if ((param->grid_node_num_h < HWD_VIIF_L2_UNDIST_MIN_GRID_NUM) ||
+ (param->grid_node_num_h > HWD_VIIF_L2_UNDIST_MAX_GRID_NUM)) {
+ return -EINVAL;
+ }
+
+ if ((param->grid_node_num_v < HWD_VIIF_L2_UNDIST_MIN_GRID_NUM) ||
+ (param->grid_node_num_v > HWD_VIIF_L2_UNDIST_MAX_GRID_NUM)) {
+ return -EINVAL;
+ }
+
+ grid_num_h = param->grid_node_num_h;
+ grid_num_v = param->grid_node_num_v;
+ if ((grid_num_h % 2U) != 0U)
+ grid_num_h += 1U;
+
+ if ((grid_num_v % 2U) != 0U)
+ grid_num_v += 1U;
+
+ if ((grid_num_v * grid_num_h) > HWD_VIIF_L2_UNDIST_MAX_GRID_TOTAL_NUM)
+ return -EINVAL;
+
+ if (param->grid_patch_hsize_inv >= HWD_VIIF_L2_UNDIST_MAX_GRID_PATCH_SIZE_INV)
+ return -EINVAL;
+
+ if (param->grid_patch_vsize_inv >= HWD_VIIF_L2_UNDIST_MAX_GRID_PATCH_SIZE_INV)
+ return -EINVAL;
+
+ val = readl(&res->capture_reg->l2isp.L2_SENSOR_CROP_HSIZE) & GENMASK(12, 0);
+ if (((param->sensor_crop_ofs_h / 2) + ((int16_t)val)) > 4095)
+ return -EINVAL;
+
+ val = readl(&res->capture_reg->l2isp.L2_SENSOR_CROP_VSIZE) & GENMASK(11, 0);
+ if (((param->sensor_crop_ofs_v / 2) + ((int16_t)val)) > 2047)
+ return -EINVAL;
+
+ /* set parameters related to L2ISP UNDIST */
+ if (param->through_mode == HWD_VIIF_ENABLE) {
+ /* Enable through mode */
+ writel(HWD_VIIF_ENABLE, &(res->capture_reg->l2isp.L2_MODE));
+ } else {
+ val = (param->roi_mode << 1U);
+ writel(val, &(res->capture_reg->l2isp.L2_MODE));
+ val = (uint32_t)param->sensor_crop_ofs_h & GENMASK(13, 0);
+ writel(val, &(res->capture_reg->l2isp.L2_SENSOR_CROP_OFS_H));
+ val = (uint32_t)param->sensor_crop_ofs_v & GENMASK(12, 0);
+ writel(val, &(res->capture_reg->l2isp.L2_SENSOR_CROP_OFS_V));
+ writel(param->norm_scale, &(res->capture_reg->l2isp.L2_NORM_SCALE));
+ writel(param->valid_r_norm2_poly, &(res->capture_reg->l2isp.L2_VALID_R_NORM2_POLY));
+ writel(param->valid_r_norm2_grid, &(res->capture_reg->l2isp.L2_VALID_R_NORM2_GRID));
+ writel(param->roi_write_area_delta,
+ &(res->capture_reg->l2isp.L2_ROI_WRITE_AREA_DELTA[0]));
+ for (i = 0; i < HWD_VIIF_L2_UNDIST_POLY_NUM; i++) {
+ val = (uint32_t)param->poly_write_g_coef[i];
+ writel(val, &(res->capture_reg->l2isp.L2_POLY10_WRITE_G_COEF[i]));
+ val = (uint32_t)param->poly_read_b_coef[i];
+ writel(val, &(res->capture_reg->l2isp.L2_POLY10_READ_B_COEF[i]));
+ val = (uint32_t)param->poly_read_g_coef[i];
+ writel(val, &(res->capture_reg->l2isp.L2_POLY10_READ_G_COEF[i]));
+ val = (uint32_t)param->poly_read_r_coef[i];
+ writel(val, &(res->capture_reg->l2isp.L2_POLY10_READ_R_COEF[i]));
+ }
+ writel(param->grid_node_num_h, &(res->capture_reg->l2isp.L2_GRID_NODE_NUM_H));
+ writel(param->grid_node_num_v, &(res->capture_reg->l2isp.L2_GRID_NODE_NUM_V));
+ writel(param->grid_patch_hsize_inv,
+ &(res->capture_reg->l2isp.L2_GRID_PATCH_HSIZE_INV));
+ writel(param->grid_patch_vsize_inv,
+ &(res->capture_reg->l2isp.L2_GRID_PATCH_VSIZE_INV));
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_undist_table_transmission() - Configure L2ISP transferring grid table for undistortion.
+ *
+ * @write_g: grid table address for G-WRITE(physical address)
+ * @read_b: grid table address for B-READ(physical address)
+ * @read_g: grid table address for G-READ(physical address)
+ * @read_r: grid table address for R-READ(physical address)
+ * @size: of each table [1024..8192] [byte]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ * Return: -EINVAL Parameter error
+ * - "write_g", "read_b", "read_g" or "read_r" is not 4byte alignment
+ * - "size" is out of range
+ * - "size" is not 0 when all table addresses are 0
+ */
+int32_t hwd_VIIF_l2_set_undist_table_transmission(uint32_t module_id, uintptr_t write_g,
+ uintptr_t read_b, uintptr_t read_g,
+ uintptr_t read_r, uint32_t size)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val = 0U;
+
+ if (((write_g % HWD_VIIF_L2_VDM_ALIGN) != 0U) || ((read_b % HWD_VIIF_L2_VDM_ALIGN) != 0U) ||
+ ((read_g % HWD_VIIF_L2_VDM_ALIGN) != 0U) || ((read_r % HWD_VIIF_L2_VDM_ALIGN) != 0U)) {
+ return -EINVAL;
+ }
+
+ if (((size != 0U) && (size < HWD_VIIF_L2_UNDIST_MIN_TABLE_SIZE)) ||
+ (size > HWD_VIIF_L2_UNDIST_MAX_TABLE_SIZE)) {
+ return -EINVAL;
+ }
+
+ if ((size % 4U) != 0U)
+ return -EINVAL;
+
+ if ((write_g == 0U) && (read_b == 0U) && (read_g == 0U) && (read_r == 0U) && (size != 0U))
+ return -EINVAL;
+
+ if (((write_g != 0U) || (read_b != 0U) || (read_g != 0U) || (read_r != 0U)) &&
+ (size == 0U)) {
+ return -EINVAL;
+ }
+
+ /* read_b: t_port[8], read_g: t_port[9], read_r: t_port[10], write_g: t_port[11] */
+ if (read_b != 0U) {
+ writel((uint32_t)read_b, &(res->capture_reg->vdm.t_port[8].VDM_T_STADR));
+ writel(size, &(res->capture_reg->vdm.t_port[8].VDM_T_SIZE));
+ val |= BIT(8);
+ }
+ if (read_g != 0U) {
+ writel((uint32_t)read_g, &(res->capture_reg->vdm.t_port[9].VDM_T_STADR));
+ writel(size, &(res->capture_reg->vdm.t_port[9].VDM_T_SIZE));
+ val |= BIT(9);
+ }
+ if (read_r != 0U) {
+ writel((uint32_t)read_r, &(res->capture_reg->vdm.t_port[10].VDM_T_STADR));
+ writel(size, &(res->capture_reg->vdm.t_port[10].VDM_T_SIZE));
+ val |= BIT(10);
+ }
+ if (write_g != 0U) {
+ writel((uint32_t)write_g, &(res->capture_reg->vdm.t_port[11].VDM_T_STADR));
+ writel(size, &(res->capture_reg->vdm.t_port[11].VDM_T_SIZE));
+ val |= BIT(11);
+ }
+
+ if (val != 0U) {
+ /* Set SRAM base address and size. t_group[1] is used only to transfer UNDIST table */
+ writel(HWD_VIIF_VDM_CFG_PARAM, &(res->capture_reg->vdm.t_group[1].VDM_T_CFG));
+ writel(HWD_VIIF_L2_VDM_GRID_SRAM_BASE,
+ &(res->capture_reg->vdm.t_group[1].VDM_T_SRAM_BASE));
+ writel(HWD_VIIF_L2_VDM_GRID_SRAM_SIZE,
+ &(res->capture_reg->vdm.t_group[1].VDM_T_SRAM_SIZE));
+ }
+
+ val |= (readl(&res->capture_reg->vdm.VDM_T_ENABLE) & ~((uint32_t)0xfU << 8U));
+ writel(val, &(res->capture_reg->vdm.VDM_T_ENABLE));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_roi_num_1() - Set ROI path condition when ROI num is 1
+ *
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ */
+static void hwd_VIIF_l2_set_roi_num_1(uint32_t regbuf_id, struct hwd_viif_res *res)
+{
+ uint32_t val, x_min, x_max, y_min, y_max;
+ uint32_t i, x, y, w, h;
+
+ /* ROI0 is input to POST0 and POST1 */
+ if (res->l2_roi_path_info[regbuf_id].post_enable_flag[0]) {
+ /* POST0 is enabled */
+ x_min = res->l2_roi_path_info[regbuf_id].post_crop_x[0];
+ x_max = res->l2_roi_path_info[regbuf_id].post_crop_x[0] +
+ res->l2_roi_path_info[regbuf_id].post_crop_w[0];
+ y_min = res->l2_roi_path_info[regbuf_id].post_crop_y[0];
+ y_max = res->l2_roi_path_info[regbuf_id].post_crop_y[0] +
+ res->l2_roi_path_info[regbuf_id].post_crop_h[0];
+ if (res->l2_roi_path_info[regbuf_id].post_enable_flag[1]) {
+ /* POST1 is enabled */
+ x_min = min(x_min, res->l2_roi_path_info[regbuf_id].post_crop_x[1]);
+ val = res->l2_roi_path_info[regbuf_id].post_crop_x[1] +
+ res->l2_roi_path_info[regbuf_id].post_crop_w[1];
+ x_max = max(x_max, val);
+ y_min = min(y_min, res->l2_roi_path_info[regbuf_id].post_crop_y[1]);
+ val = res->l2_roi_path_info[regbuf_id].post_crop_y[1] +
+ res->l2_roi_path_info[regbuf_id].post_crop_h[1];
+ y_max = max(y_max, val);
+ }
+ x = x_min;
+ y = y_min;
+ w = x_max - x_min;
+ h = y_max - y_min;
+ } else if (res->l2_roi_path_info[regbuf_id].post_enable_flag[1]) {
+ /* POST0 is disabled and POST1 is enabled */
+ x = res->l2_roi_path_info[regbuf_id].post_crop_x[1];
+ w = res->l2_roi_path_info[regbuf_id].post_crop_w[1];
+ y = res->l2_roi_path_info[regbuf_id].post_crop_y[1];
+ h = res->l2_roi_path_info[regbuf_id].post_crop_h[1];
+ } else {
+ /* All POSTs are disabled */
+ x = 0;
+ y = 0;
+ w = HWD_VIIF_CROP_MIN_W;
+ h = HWD_VIIF_CROP_MIN_H;
+ }
+ writel(x, &(res->capture_reg->l2isp.roi[0].L2_ROI_OUT_OFS_H));
+ writel(y, &(res->capture_reg->l2isp.roi[0].L2_ROI_OUT_OFS_V));
+ writel(w, &(res->capture_reg->l2isp.roi[0].L2_ROI_OUT_HSIZE));
+ writel(h, &(res->capture_reg->l2isp.roi[0].L2_ROI_OUT_VSIZE));
+
+ for (i = 0; i < HWD_VIIF_MAX_POST_NUM; i++) {
+ if (res->l2_roi_path_info[regbuf_id].post_enable_flag[i])
+ writel(0, &(res->capture_reg->l2isp.L2_ROI_TO_POST[i]));
+ else
+ writel(HWD_VIIF_L2_ROI_NONE, &(res->capture_reg->l2isp.L2_ROI_TO_POST[i]));
+ }
+}
+
+/**
+ * hwd_VIIF_l2_set_roi_num_2() - Set ROI path condition when ROI num is 2
+ *
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ */
+static void hwd_VIIF_l2_set_roi_num_2(uint32_t regbuf_id, struct hwd_viif_res *res)
+{
+ uint32_t i;
+
+ for (i = 0; i < HWD_VIIF_L2_ROI_MAX_NUM; i++) {
+ /* ROI-n is the same as CROP area of POST-n */
+ if (res->l2_roi_path_info[regbuf_id].post_enable_flag[i]) {
+ writel(res->l2_roi_path_info[regbuf_id].post_crop_x[i],
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_OFS_H));
+ writel(res->l2_roi_path_info[regbuf_id].post_crop_y[i],
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_OFS_V));
+ writel(res->l2_roi_path_info[regbuf_id].post_crop_w[i],
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_HSIZE));
+ writel(res->l2_roi_path_info[regbuf_id].post_crop_h[i],
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_VSIZE));
+ writel(i, &(res->capture_reg->l2isp.L2_ROI_TO_POST[i]));
+ } else {
+ writel(0, &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_OFS_H));
+ writel(0, &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_OFS_V));
+ writel(HWD_VIIF_CROP_MIN_W,
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_HSIZE));
+ writel(HWD_VIIF_CROP_MIN_H,
+ &(res->capture_reg->l2isp.roi[i].L2_ROI_OUT_VSIZE));
+ writel(HWD_VIIF_L2_ROI_NONE, &(res->capture_reg->l2isp.L2_ROI_TO_POST[i]));
+ }
+ }
+}
+
+/**
+ * hwd_VIIF_l2_set_roi_path() - Set ROI path condition
+ *
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ */
+static void hwd_VIIF_l2_set_roi_path(uint32_t regbuf_id, struct hwd_viif_res *res)
+{
+ if (res->l2_roi_path_info[regbuf_id].roi_num == 1U)
+ hwd_VIIF_l2_set_roi_num_1(regbuf_id, res);
+ else
+ hwd_VIIF_l2_set_roi_num_2(regbuf_id, res);
+}
+
+/**
+ * hwd_VIIF_l2_set_roi() - Set ROI parameters of L2ISP
+ *
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @param: Pointer to ROI parameters of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "param" is NULL
+ * - [2] Member of "param" is invalid
+ *
+ * see also: #hwd_VIIF_l2_set_roi_path
+ */
+int32_t hwd_VIIF_l2_set_roi(uint32_t module_id, uint32_t regbuf_id,
+ const struct hwd_viif_l2_roi *param)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ if (param == NULL)
+ return -EINVAL;
+
+ if ((param->roi_scale < HWD_VIIF_L2_ROI_MIN_SCALE) ||
+ (param->roi_scale > HWD_VIIF_L2_ROI_MAX_SCALE)) {
+ return -EINVAL;
+ }
+ if ((param->roi_scale_inv < HWD_VIIF_L2_ROI_MIN_SCALE_INV) ||
+ (param->roi_scale_inv > HWD_VIIF_L2_ROI_MAX_SCALE_INV)) {
+ return -EINVAL;
+ }
+ if ((param->corrected_wo_scale_hsize < HWD_VIIF_L2_ROI_MIN_CORRECTED_WO_SCALE_HSIZE) ||
+ (param->corrected_wo_scale_hsize > HWD_VIIF_L2_ROI_MAX_CORRECTED_WO_SCALE_HSIZE)) {
+ return -EINVAL;
+ }
+ if ((param->corrected_wo_scale_vsize < HWD_VIIF_L2_ROI_MIN_CORRECTED_WO_SCALE_VSIZE) ||
+ (param->corrected_wo_scale_vsize > HWD_VIIF_L2_ROI_MAX_CORRECTED_WO_SCALE_VSIZE)) {
+ return -EINVAL;
+ }
+ if ((param->corrected_hsize < HWD_VIIF_L2_ROI_MIN_CORRECTED_HSIZE) ||
+ (param->corrected_hsize > HWD_VIIF_L2_ROI_MAX_CORRECTED_HSIZE)) {
+ return -EINVAL;
+ }
+ if ((param->corrected_vsize < HWD_VIIF_L2_ROI_MIN_CORRECTED_VSIZE) ||
+ (param->corrected_vsize > HWD_VIIF_L2_ROI_MAX_CORRECTED_VSIZE)) {
+ return -EINVAL;
+ }
+
+ /* Set the number of ROI and update resource info with roi_num */
+ writel(1, &(res->capture_reg->l2isp.L2_ROI_NUM));
+ res->l2_roi_path_info[regbuf_id].roi_num = 1;
+
+ /* Update ROI area and input to each POST */
+ hwd_VIIF_l2_set_roi_path(regbuf_id, res);
+
+ /* Set the remaining parameters */
+ writel(param->roi_scale, &(res->capture_reg->l2isp.roi[0].L2_ROI_SCALE));
+ writel(param->roi_scale_inv, &(res->capture_reg->l2isp.roi[0].L2_ROI_SCALE_INV));
+ val = (param->corrected_wo_scale_hsize << 13U) | param->corrected_hsize;
+ writel(val, &(res->capture_reg->l2isp.roi[0].L2_ROI_CORRECTED_HSIZE));
+ val = (param->corrected_wo_scale_vsize << 12U) | param->corrected_vsize;
+ writel(val, &(res->capture_reg->l2isp.roi[0].L2_ROI_CORRECTED_VSIZE));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_gamma() - Set Gamma correction parameters of L2ISP
+ *
+ * @post_id: POST ID [0..1]
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @enable: or disable gamma correction of L2ISP. For more refer @ref hwd_VIIF_enable_flag.
+ * @vsplit: changing line position from 1st table to 2nd table [0..4094]
+ * @mode: Gamma correction mode. For more refer @ref hwd_VIIF_gamma_table_mode.
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "post_id", "enable", "vsplit" or "mode" is out of range
+ * - [2] "vsplit" is not 0 when "enable" is HWD_VIIF_DISABLE
+ * - [3] "mode" is not HWD_VIIF_GAMMA_COMPRESSED when enable is HWD_VIIF_DISABLE
+ *
+ * see also: #hwd_VIIF_l2_set_gamma
+ */
+int32_t hwd_VIIF_l2_set_gamma(uint32_t module_id, uint32_t post_id, uint32_t regbuf_id,
+ uint32_t enable, uint32_t vsplit, uint32_t mode)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ if (post_id >= HWD_VIIF_MAX_POST_NUM)
+ return -EINVAL;
+
+ if ((enable != HWD_VIIF_ENABLE) && (enable != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if (vsplit > HWD_VIIF_GAMMA_MAX_VSPLIT)
+ return -EINVAL;
+
+ if ((mode != HWD_VIIF_GAMMA_COMPRESSED) && (mode != HWD_VIIF_GAMMA_LINEAR))
+ return -EINVAL;
+
+ if ((enable == HWD_VIIF_DISABLE) && (vsplit != 0x0U))
+ return -EINVAL;
+
+ if ((enable == HWD_VIIF_DISABLE) && (mode != HWD_VIIF_GAMMA_COMPRESSED))
+ return -EINVAL;
+
+ /* Set gamma parameters of L2ISP */
+ val = (vsplit << 16U) | (mode << 4U) | enable;
+ writel(val, &(res->capture_reg->l2isp.post[post_id].L2_POST_GAMMA_M));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_gamma_table_transmission() - Configure L2ISP transferring gamma table.
+ *
+ * @post_id: POST ID [0..1]
+ * @gamma_table: Pointer to gamma table information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 operation completed successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "post_id" is out of range
+ * - [2] Member of "gamma_table" is invalid
+ */
+int32_t hwd_VIIF_l2_set_gamma_table_transmission(uint32_t module_id, uint32_t post_id,
+ const struct hwd_viif_l2_gamma_table *gamma_table)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t i, base_addr;
+ uint32_t vdm_enable = 0U;
+
+ if (post_id >= HWD_VIIF_MAX_POST_NUM)
+ return -EINVAL;
+
+ for (i = 0; i < 6U; i++) {
+ if ((gamma_table->table[i] % HWD_VIIF_L2_VDM_ALIGN) != 0U)
+ return -EINVAL;
+ }
+
+ /* table[0]: LUT0-G/Y: t_port[12 + post_id * 6] */
+ /* table[1]: LUT1-G/Y: t_port[13 + post_id * 6] */
+ /* table[2]: LUT0-B/U: t_port[14 + post_id * 6] */
+ /* table[3]: LUT1-B/U: t_port[15 + post_id * 6] */
+ /* table[4]: LUT0-R/V: t_port[16 + post_id * 6] */
+ /* table[5]: LUT1-R/V: t_port[17 + post_id * 6] */
+ for (i = 0; i < 6U; i++) {
+ if (gamma_table->table[i] != 0U) {
+ writel((uint32_t)gamma_table->table[i],
+ &(res->capture_reg->vdm.t_port[(12U + i + (post_id * 6U))]
+ .VDM_T_STADR));
+ writel(HWD_VIIF_L2_VDM_GAMMA_TABLE_SIZE,
+ &(res->capture_reg->vdm.t_port[(12U + i + (post_id * 6U))]
+ .VDM_T_SIZE));
+ vdm_enable |= BIT(i);
+ }
+ }
+ if (vdm_enable != 0U) {
+ /* t_group[2..3] is used only to transfer GAMMA table */
+ /* [2]: POST0, [3]: POST1 */
+ writel(HWD_VIIF_VDM_CFG_PARAM,
+ &(res->capture_reg->vdm.t_group[(post_id + 2U)].VDM_T_CFG));
+ base_addr = HWD_VIIF_L2_VDM_GAMMA_SRAM_BASE +
+ (HWD_VIIF_L2_VDM_GAMMA_SRAM_SIZE * post_id);
+ writel(base_addr, &(res->capture_reg->vdm.t_group[(post_id + 2U)].VDM_T_SRAM_BASE));
+ writel(HWD_VIIF_L2_VDM_GAMMA_SRAM_SIZE,
+ &(res->capture_reg->vdm.t_group[(post_id + 2U)].VDM_T_SRAM_SIZE));
+ vdm_enable = vdm_enable << (12U + (post_id * 6U));
+ }
+ vdm_enable |= (readl(&res->capture_reg->vdm.VDM_T_ENABLE) &
+ ~((uint32_t)0x3fU << (12U + (post_id * 6U))));
+ writel(vdm_enable, &(res->capture_reg->vdm.VDM_T_ENABLE));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_output_csc() - Set output CSC parameters of L2ISP
+ *
+ * @post_id: POST ID [0..1]
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @param: Pointer to output csc parameters of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "post_id" is out of range
+ * - [2] Member of "param" is invalid
+ */
+int32_t hwd_VIIF_l2_set_output_csc(uint32_t module_id, uint32_t post_id, uint32_t regbuf_id,
+ const struct hwd_viif_csc_param *param)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t i, val;
+ uint32_t enable = HWD_VIIF_DISABLE;
+
+ if (post_id >= HWD_VIIF_MAX_POST_NUM)
+ return -EINVAL;
+
+ if (param != NULL) {
+ if (param->r_cr_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->g_y_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->b_cb_in_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->r_cr_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->g_y_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ if (param->b_cb_out_offset > HWD_VIIF_CSC_MAX_OFFSET)
+ return -EINVAL;
+
+ for (i = 0; i < HWD_VIIF_CSC_MAX_COEF_NUM; i++) {
+ if (param->coef[i] > HWD_VIIF_CSC_MAX_COEF_VALUE)
+ return -EINVAL;
+ }
+
+ writel(param->g_y_in_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_YG_OFFSETI));
+ writel(param->coef[0], &(res->capture_reg->l2isp.post[post_id].csc.MTB_YG1));
+ val = (param->coef[1] << HWD_VIIF_MTB_CB_YG_COEF_OFFSET) |
+ (param->coef[2] << HWD_VIIF_MTB_CR_YG_COEF_OFFSET);
+ writel(val, &(res->capture_reg->l2isp.post[post_id].csc.MTB_YG2));
+ writel(param->g_y_out_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_YG_OFFSETO));
+ writel(param->b_cb_in_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_CB_OFFSETI));
+ writel(param->coef[3], &(res->capture_reg->l2isp.post[post_id].csc.MTB_CB1));
+ val = (param->coef[4] << HWD_VIIF_MTB_CB_CB_COEF_OFFSET) |
+ (param->coef[5] << HWD_VIIF_MTB_CR_CB_COEF_OFFSET);
+ writel(val, &(res->capture_reg->l2isp.post[post_id].csc.MTB_CB2));
+ writel(param->b_cb_out_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_CB_OFFSETO));
+ writel(param->r_cr_in_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_CR_OFFSETI));
+ writel(param->coef[6], &(res->capture_reg->l2isp.post[post_id].csc.MTB_CR1));
+ val = (param->coef[7] << HWD_VIIF_MTB_CB_CR_COEF_OFFSET) |
+ (param->coef[8] << HWD_VIIF_MTB_CR_CR_COEF_OFFSET);
+ writel(val, &(res->capture_reg->l2isp.post[post_id].csc.MTB_CR2));
+ writel(param->r_cr_out_offset,
+ &(res->capture_reg->l2isp.post[post_id].csc.MTB_CR_OFFSETO));
+ enable = HWD_VIIF_ENABLE;
+ }
+ writel(enable, &(res->capture_reg->l2isp.post[post_id].csc.MTB));
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_l2_set_img_transmission() - Set image transfer condition of L2ISP
+ *
+ * @post_id: POST ID [0..1]
+ * @regbuf_id: ISP register buffer ID [0..3]
+ * @enable: or disable image transfer of MAIN unit. For more refer @ref hwd_VIIF_enable_flag.
+ * @src: Pointer to crop area information
+ * @out_process: Pointer to output process information
+ * @img: Pointer to output image information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "post_id" or "enable" is out of range
+ * - [2] "src" or "out_process" is NULL when "enable" is HWD_VIIF_ENABLE
+ * - [3] "src" or "out_process" is not NULL when "enable" is HWD_VIIF_DISABLE
+ * - [4] Member of "src" is out of range
+ * - [5] "w" of "src" is not equal to 2 * "width" of "image" when "half_scal" of "out_process" is HWD_VIIF_ENABLE
+ * - [6] "h" of "src" is not equal to 2 * "height" of "image" when "half_scal" of "out_process" is HWD_VIIF_ENABLE
+ * - [7] "w" of "src" is not equal to "width" of "image" when "half_scal" of "out_process" is HWD_VIIF_DISABLE
+ * - [8] "h" of "src" is not equal to "height" of "image" when "half_scal" of "out_process" is HWD_VIIF_DISABLE
+ * - [9] Member of "out_process" is invalid
+ * - [10] "alpha" of "out_process" is not 0 when "format" of "img" is not HWD_VIIF_ARGB8888_PACKED
+ * - [11] "format" of "img" is not HWD_VIIF_ONE_COLOR_8 or HWD_VIIF_ONE_COLOR_16 when "select_color" of "out_process"
+ * is HWD_VIIF_COLOR_Y_G, HWD_VIIF_COLOR_U_B or HWD_VIIF_COLOR_V_R
+ * - [12] Member of "img" is invalid
+ *
+ * see also: #hwd_VIIF_l2_set_roi_path
+ */
+int32_t hwd_VIIF_l2_set_img_transmission(uint32_t module_id, uint32_t post_id, uint32_t regbuf_id,
+ uint32_t enable, const struct hwd_viif_img_area *src,
+ const struct hwd_viif_out_process *out_process,
+ const struct hwd_viif_img *img)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ int32_t ret = 0;
+ uint32_t pitch[HWD_VIIF_MAX_PLANE_NUM], img_start_addr[HWD_VIIF_MAX_PLANE_NUM];
+ uint32_t i, val, loop, k, r[HWD_VIIF_MAX_PLANE_NUM];
+
+ /* pitch alignment for planar or one color format */
+ uint32_t pitch_align = 128U;
+
+ if (post_id >= HWD_VIIF_MAX_POST_NUM)
+ return -EINVAL;
+
+ if ((enable != HWD_VIIF_ENABLE) && (enable != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if ((enable == HWD_VIIF_ENABLE) && ((src == NULL) || (out_process == NULL)))
+ return -EINVAL;
+
+ if ((enable == HWD_VIIF_DISABLE) && ((src != NULL) || (out_process != NULL)))
+ return -EINVAL;
+
+ if (enable == HWD_VIIF_ENABLE) {
+ if ((out_process->half_scale != HWD_VIIF_ENABLE) &&
+ (out_process->half_scale != HWD_VIIF_DISABLE)) {
+ return -EINVAL;
+ }
+
+ if ((out_process->select_color != HWD_VIIF_COLOR_Y_G) &&
+ (out_process->select_color != HWD_VIIF_COLOR_U_B) &&
+ (out_process->select_color != HWD_VIIF_COLOR_V_R) &&
+ (out_process->select_color != HWD_VIIF_COLOR_YUV_RGB)) {
+ return -EINVAL;
+ }
+
+ if ((img->format != HWD_VIIF_ARGB8888_PACKED) && (out_process->alpha != 0U))
+ return -EINVAL;
+
+ if (((img->width % 2U) != 0U) || ((img->height % 2U) != 0U) ||
+ (img->width < HWD_VIIF_MIN_OUTPUT_IMG_WIDTH) ||
+ (img->height < HWD_VIIF_MIN_OUTPUT_IMG_HEIGHT) ||
+ (img->width > HWD_VIIF_MAX_OUTPUT_IMG_WIDTH_ISP) ||
+ (img->height > HWD_VIIF_MAX_OUTPUT_IMG_HEIGHT_ISP)) {
+ return -EINVAL;
+ }
+
+ if ((src->x > HWD_VIIF_CROP_MAX_X_ISP) || (src->y > HWD_VIIF_CROP_MAX_Y_ISP) ||
+ (src->w < HWD_VIIF_CROP_MIN_W) || (src->w > HWD_VIIF_CROP_MAX_W_ISP) ||
+ (src->h < HWD_VIIF_CROP_MIN_H) || (src->h > HWD_VIIF_CROP_MAX_H_ISP)) {
+ return -EINVAL;
+ }
+
+ if (out_process->half_scale == HWD_VIIF_ENABLE) {
+ if ((src->w != (img->width * 2U)) || (src->h != (img->height * 2U)))
+ return -EINVAL;
+ } else {
+ if ((src->w != img->width) || (src->h != img->height))
+ return -EINVAL;
+ }
+
+ if ((out_process->select_color == HWD_VIIF_COLOR_Y_G) ||
+ (out_process->select_color == HWD_VIIF_COLOR_U_B) ||
+ (out_process->select_color == HWD_VIIF_COLOR_V_R)) {
+ if ((img->format != HWD_VIIF_ONE_COLOR_8) &&
+ (img->format != HWD_VIIF_ONE_COLOR_16)) {
+ return -EINVAL;
+ }
+ }
+
+ switch (img->format) {
+ case HWD_VIIF_YCBCR422_8_PACKED:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ loop = 1U;
+ k = 2U;
+ r[0] = 1U;
+ pitch_align = 256U;
+ break;
+ case HWD_VIIF_RGB888_PACKED:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ loop = 1U;
+ k = 3U;
+ r[0] = 1U;
+ pitch_align = 384U;
+ break;
+ case HWD_VIIF_ARGB8888_PACKED:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ loop = 1U;
+ k = 4U;
+ r[0] = 1U;
+ pitch_align = 512U;
+ break;
+ case HWD_VIIF_ONE_COLOR_8:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ loop = 1U;
+ k = 1U;
+ r[0] = 1U;
+ break;
+ case HWD_VIIF_ONE_COLOR_16:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ loop = 1U;
+ k = 2U;
+ r[0] = 1U;
+ break;
+ case HWD_VIIF_YCBCR422_8_PLANAR:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ img_start_addr[1] = (uint32_t)img->pixelmap[1].pmap_paddr;
+ img_start_addr[2] = (uint32_t)img->pixelmap[2].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ pitch[1] = img->pixelmap[1].pitch;
+ pitch[2] = img->pixelmap[2].pitch;
+ loop = HWD_VIIF_MAX_PLANE_NUM;
+ k = 1U;
+ r[0] = 1U;
+ r[1] = 2U;
+ r[2] = 2U;
+ break;
+ case HWD_VIIF_RGB888_YCBCR444_8_PLANAR:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ img_start_addr[1] = (uint32_t)img->pixelmap[1].pmap_paddr;
+ img_start_addr[2] = (uint32_t)img->pixelmap[2].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ pitch[1] = img->pixelmap[1].pitch;
+ pitch[2] = img->pixelmap[2].pitch;
+ loop = HWD_VIIF_MAX_PLANE_NUM;
+ k = 1U;
+ r[0] = 1U;
+ r[1] = 1U;
+ r[2] = 1U;
+ loop = HWD_VIIF_MAX_PLANE_NUM;
+ break;
+ case HWD_VIIF_YCBCR422_16_PLANAR:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ img_start_addr[1] = (uint32_t)img->pixelmap[1].pmap_paddr;
+ img_start_addr[2] = (uint32_t)img->pixelmap[2].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ pitch[1] = img->pixelmap[1].pitch;
+ pitch[2] = img->pixelmap[2].pitch;
+ loop = HWD_VIIF_MAX_PLANE_NUM;
+ k = 2U;
+ r[0] = 1U;
+ r[1] = 2U;
+ r[2] = 2U;
+ break;
+ case HWD_VIIF_RGB161616_YCBCR444_16_PLANAR:
+ img_start_addr[0] = (uint32_t)img->pixelmap[0].pmap_paddr;
+ img_start_addr[1] = (uint32_t)img->pixelmap[1].pmap_paddr;
+ img_start_addr[2] = (uint32_t)img->pixelmap[2].pmap_paddr;
+ pitch[0] = img->pixelmap[0].pitch;
+ pitch[1] = img->pixelmap[1].pitch;
+ pitch[2] = img->pixelmap[2].pitch;
+ loop = HWD_VIIF_MAX_PLANE_NUM;
+ k = 2U;
+ r[0] = 1U;
+ r[1] = 1U;
+ r[2] = 1U;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret == 0) {
+ for (i = 0; i < loop; i++) {
+ val = max(((img->width * k) / r[i]), 128U);
+ if ((pitch[i] < val) || (pitch[i] > HWD_VIIF_MAX_PITCH_ISP) ||
+ ((pitch[i] % pitch_align) != 0U) ||
+ ((img_start_addr[i] % 4U) != 0U)) {
+ ret = -EINVAL;
+ }
+ if (ret == -EINVAL)
+ break;
+ }
+
+ if (ret == 0) {
+ writel(img_start_addr[0],
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_OUT_STADR_G));
+ writel(pitch[0],
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_OUT_PITCH_G));
+ if (loop == HWD_VIIF_MAX_PLANE_NUM) {
+ writel(img_start_addr[1],
+ &(res->capture_reg->l2isp.post[post_id]
+ .L2_POST_OUT_STADR_B));
+ writel(img_start_addr[2],
+ &(res->capture_reg->l2isp.post[post_id]
+ .L2_POST_OUT_STADR_R));
+ writel(pitch[1], &(res->capture_reg->l2isp.post[post_id]
+ .L2_POST_OUT_PITCH_B));
+ writel(pitch[2], &(res->capture_reg->l2isp.post[post_id]
+ .L2_POST_OUT_PITCH_R));
+ }
+
+ /* Set CROP */
+ val = (src->y << 16U) | src->x;
+ writel(val,
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_CAP_OFFSET));
+ val = (src->h << 16U) | src->w;
+ writel(val,
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_CAP_SIZE));
+
+ /* Set output process */
+ writel(out_process->half_scale,
+ &(res->capture_reg->l2isp.post[post_id]
+ .L2_POST_HALF_SCALE_EN));
+ writel(out_process->select_color,
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_C_SELECT));
+ writel((uint32_t)out_process->alpha,
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_OPORTALP));
+ writel(img->format,
+ &(res->capture_reg->l2isp.post[post_id].L2_POST_OPORTFMT));
+
+ /* Update ROI area and input to each POST */
+ res->l2_roi_path_info[regbuf_id].post_enable_flag[post_id] = true;
+ res->l2_roi_path_info[regbuf_id].post_crop_x[post_id] = src->x;
+ res->l2_roi_path_info[regbuf_id].post_crop_y[post_id] = src->y;
+ res->l2_roi_path_info[regbuf_id].post_crop_w[post_id] = src->w;
+ res->l2_roi_path_info[regbuf_id].post_crop_h[post_id] = src->h;
+ hwd_VIIF_l2_set_roi_path(regbuf_id, res);
+ }
+ }
+ } else {
+ /* Update ROI area and input to each POST */
+ res->l2_roi_path_info[regbuf_id].post_enable_flag[post_id] = false;
+ res->l2_roi_path_info[regbuf_id].post_crop_x[post_id] = 0U;
+ res->l2_roi_path_info[regbuf_id].post_crop_y[post_id] = 0U;
+ res->l2_roi_path_info[regbuf_id].post_crop_w[post_id] = HWD_VIIF_CROP_MIN_W;
+ res->l2_roi_path_info[regbuf_id].post_crop_h[post_id] = HWD_VIIF_CROP_MIN_H;
+ hwd_VIIF_l2_set_roi_path(regbuf_id, res);
+ }
+
+ return ret;
+}
+
+/**
+ * hwd_VIIF_l2_set_irq_mask() - Set mask condition for L2ISP
+ *
+ * @mask: Pointer to L2ISP mask condition
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_l2_set_irq_mask(uint32_t module_id, const uint32_t *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ writel(*mask, &(res->capture_reg->l2isp.L2_CRGBF_ISP_INT_MASK));
+}
+
+/**
+ * hwd_VIIF_csi2rx_err_irq_handler() - CSI-2 RX error interruption handler
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: event information of CSI-2 RX error interruption
+ */
+uint32_t hwd_VIIF_csi2rx_err_irq_handler(uint32_t module_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ return readl(&res->csi2host_reg->CSI2RX_INT_ST_MAIN);
+}
+
+/**
+ * hwd_VIIF_status_err_irq_handler() - STATUS error interruption handler
+ *
+ * @event_main: information of STATUS error interruption of MAIN unit
+ * @event_sub: information of STATUS error interruption of SUB unit(CH0 and CH1) or VOIF loopback(CH2)
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_status_err_irq_handler(uint32_t module_id, uint32_t *event_main, uint32_t *event_sub)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val, mask;
+
+ *event_main = HWD_VIIF_NO_EVENT;
+ *event_sub = HWD_VIIF_NO_EVENT;
+
+ val = readl(&res->capture_reg->sys.INT_M_STATUS);
+ mask = readl(&res->capture_reg->sys.INT_M_MASK);
+ val = val & ~mask;
+ if (val != HWD_VIIF_NO_EVENT) {
+ writel(val, &(res->capture_reg->sys.INT_M_STATUS));
+ *event_main = val;
+ }
+
+ val = readl(&res->capture_reg->sys.INT_S_STATUS);
+ mask = readl(&res->capture_reg->sys.INT_S_MASK);
+ val = val & ~mask;
+ if (val != HWD_VIIF_NO_EVENT) {
+ writel(val, &(res->capture_reg->sys.INT_S_STATUS));
+ *event_sub = val;
+ }
+}
+
+/**
+ * hwd_VIIF_vsync_irq_handler() - Vsync interruption handler
+ *
+ * @event_main: information of Vsync interruption of MAIN unit
+ * @event_sub: information of Vsync interruption of SUB unit(CH0 and CH1)
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_vsync_irq_handler(uint32_t module_id, uint32_t *event_main, uint32_t *event_sub)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val, mask;
+
+ *event_main = HWD_VIIF_NO_EVENT;
+ *event_sub = HWD_VIIF_NO_EVENT;
+
+ val = readl(&res->capture_reg->sys.INT_M_SYNC);
+ mask = readl(&res->capture_reg->sys.INT_M_SYNC_MASK);
+ val = val & ~mask;
+ if (val != HWD_VIIF_NO_EVENT) {
+ writel(val, &(res->capture_reg->sys.INT_M_SYNC));
+ *event_main = val;
+ }
+
+ val = readl(&res->capture_reg->sys.INT_S_SYNC);
+ mask = readl(&res->capture_reg->sys.INT_S_SYNC_MASK);
+ val = val & ~mask;
+ if (val != HWD_VIIF_NO_EVENT) {
+ writel(val, &(res->capture_reg->sys.INT_S_SYNC));
+ *event_sub = val;
+ }
+}
+
+/**
+ * hwd_VIIF_isp_regbuf_irq_handler() - ISP register buffer interruption handler
+ *
+ * @event_l1: information of register buffer interruption of L1ISP
+ * @event_l2: information of register buffer interruption of L2ISP
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+void hwd_VIIF_isp_regbuf_irq_handler(uint32_t module_id, uint32_t *event_l1, uint32_t *event_l2)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val;
+
+ *event_l1 = HWD_VIIF_NO_EVENT;
+ *event_l2 = HWD_VIIF_NO_EVENT;
+
+ val = readl(&res->capture_reg->l1isp.L1_CRGBF_INT_MASKED_STAT);
+ if (val != HWD_VIIF_NO_EVENT) {
+ *event_l1 = val;
+ writel(val, &(res->capture_reg->l1isp.L1_CRGBF_INT_STAT));
+ }
+
+ val = readl(&res->capture_reg->l2isp.L2_CRGBF_INT_MASKED_STAT);
+ if (val != HWD_VIIF_NO_EVENT) {
+ *event_l2 = val;
+ writel(val, &(res->capture_reg->l2isp.L2_CRGBF_INT_STAT));
+ }
+}
diff --git a/drivers/media/platform/visconti/hwd_viif_csi2rx.c b/drivers/media/platform/visconti/hwd_viif_csi2rx.c
new file mode 100644
index 000000000..c95665ee9
--- /dev/null
+++ b/drivers/media/platform/visconti/hwd_viif_csi2rx.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/timekeeping.h>
+#include "hwd_viif.h"
+#include "hwd_viif_internal.h"
+
+#define CSI2_DT_YUV4208 0x18
+#define CSI2_DT_YUV42010 0x19
+#define CSI2_DT_YUV4208L 0x1A
+#define CSI2_DT_YUV4208C 0x1C
+#define CSI2_DT_YUV42010C 0x1D
+#define CSI2_DT_YUV4228B VISCONTI_CSI2_DT_YUV4228B
+#define CSI2_DT_YUV42210B VISCONTI_CSI2_DT_YUV42210B
+#define CSI2_DT_RGB444 0x20
+#define CSI2_DT_RGB555 0x21
+#define CSI2_DT_RGB565 VISCONTI_CSI2_DT_RGB565
+#define CSI2_DT_RGB666 0x23
+#define CSI2_DT_RGB888 VISCONTI_CSI2_DT_RGB888
+#define CSI2_DT_RAW8 VISCONTI_CSI2_DT_RAW8
+#define CSI2_DT_RAW10 VISCONTI_CSI2_DT_RAW10
+#define CSI2_DT_RAW12 VISCONTI_CSI2_DT_RAW12
+#define CSI2_DT_RAW14 VISCONTI_CSI2_DT_RAW14
+
+#define ROUNDUP_BY_4(val) (((val) + 3U) & 0xfffffffcU)
+
+#define TESTCTRL0_PHY_TESTCLK_1 0x2
+#define TESTCTRL0_PHY_TESTCLK_0 0x0
+#define TESTCTRL1_PHY_TESTEN 0x10000
+#define TESTCTRL1_PHY_TESTDOUT_SHIFT 8U
+
+/**
+ * hwd_VIIF_csi2rx_write_dphy_param() - Write CSI2RX DPHY params
+ *
+ * @test_mode: test code address
+ * @test_in: test code data
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+static void hwd_VIIF_csi2rx_write_dphy_param(uint32_t test_mode, uint8_t test_in,
+ struct hwd_viif_res *res)
+{
+ /* select MSB address register */
+ writel(TESTCTRL1_PHY_TESTEN, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* set MSB address of test_mode */
+ writel(FIELD_GET(0xF00, test_mode), &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* select and set LSB address register */
+ writel(TESTCTRL1_PHY_TESTEN | FIELD_GET(0xFF, test_mode),
+ &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* set the test code data */
+ writel((uint32_t)test_in, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+}
+
+/**
+ * hwd_VIIF_csi2rx_read_dphy_param() - Read CSI2RX DPHY params
+ *
+ * @test_mode: test code address
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: test code data
+ */
+static uint8_t hwd_VIIF_csi2rx_read_dphy_param(uint32_t test_mode, struct hwd_viif_res *res)
+{
+ uint32_t read_data;
+
+ /* select MSB address register */
+ writel(TESTCTRL1_PHY_TESTEN, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* set MSB address of test_mode */
+ writel(FIELD_GET(0xF00, test_mode), &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* select and set LSB address register */
+ writel(TESTCTRL1_PHY_TESTEN | FIELD_GET(0xFF, test_mode),
+ &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL1));
+
+ /* rise and clear the testclk */
+ writel(TESTCTRL0_PHY_TESTCLK_1, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(TESTCTRL0_PHY_TESTCLK_0, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* read the test code data */
+ read_data = readl(&res->csi2host_reg->CSI2RX_PHY_TESTCTRL1);
+ return (uint8_t)(read_data >> TESTCTRL1_PHY_TESTDOUT_SHIFT);
+}
+
+/**
+ * enum dphy_testcode - DPHY registers via the local communication path
+ */
+enum dphy_testcode {
+ DIG_RDWR_RX_SYS_0 = 0x001,
+ DIG_RDWR_RX_SYS_1 = 0x002,
+ DIG_RDWR_RX_SYS_3 = 0x004,
+ DIG_RDWR_RX_SYS_7 = 0x008,
+ DIG_RDWR_RX_RX_STARTUP_OVR_2 = 0x0E2,
+ DIG_RDWR_RX_RX_STARTUP_OVR_3 = 0x0E3,
+ DIG_RDWR_RX_RX_STARTUP_OVR_4 = 0x0E4,
+ DIG_RDWR_RX_RX_STARTUP_OVR_5 = 0x0E5,
+ DIG_RDWR_RX_CB_2 = 0x1AC,
+ DIG_RD_RX_TERM_CAL_0 = 0x220,
+ DIG_RD_RX_TERM_CAL_1 = 0x221,
+ DIG_RD_RX_TERM_CAL_2 = 0x222,
+ DIG_RDWR_RX_CLKLANE_LANE_6 = 0x307,
+ DIG_RD_RX_CLKLANE_OFFSET_CAL_0 = 0x39D,
+ DIG_RD_RX_LANE0_OFFSET_CAL_0 = 0x59F,
+ DIG_RD_RX_LANE0_DDL_0 = 0x5E0,
+ DIG_RD_RX_LANE1_OFFSET_CAL_0 = 0x79F,
+ DIG_RD_RX_LANE1_DDL_0 = 0x7E0,
+ DIG_RD_RX_LANE2_OFFSET_CAL_0 = 0x99F,
+ DIG_RD_RX_LANE2_DDL_0 = 0x9E0,
+ DIG_RD_RX_LANE3_OFFSET_CAL_0 = 0xB9F,
+ DIG_RD_RX_LANE3_DDL_0 = 0xBE0,
+};
+
+#define SYS_0_HSFREQRANGE_OVR BIT(5)
+#define SYS_7_RESERVED FIELD_PREP(0x1F, 0x0C)
+#define SYS_7_DESKEW_POL BIT(5)
+#define STARTUP_OVR_4_CNTVAL FIELD_PREP(0x70, 0x01)
+#define STARTUP_OVR_4_DDL_EN BIT(0)
+#define STARTUP_OVR_5_BYPASS BIT(0)
+#define CB_2_LPRX_BIAS BIT(6)
+#define CB_2_RESERVED FIELD_PREP(0x3F, 0x0B)
+#define CLKLANE_RXHS_PULL_LONG BIT(7)
+
+static const struct hwd_viif_dphy_hs_info dphy_hs_info[] = {
+ { 80, 0x0, 0x1cc }, { 85, 0x10, 0x1cc }, { 95, 0x20, 0x1cc }, { 105, 0x30, 0x1cc },
+ { 115, 0x1, 0x1cc }, { 125, 0x11, 0x1cc }, { 135, 0x21, 0x1cc }, { 145, 0x31, 0x1cc },
+ { 155, 0x2, 0x1cc }, { 165, 0x12, 0x1cc }, { 175, 0x22, 0x1cc }, { 185, 0x32, 0x1cc },
+ { 198, 0x3, 0x1cc }, { 213, 0x13, 0x1cc }, { 228, 0x23, 0x1cc }, { 243, 0x33, 0x1cc },
+ { 263, 0x4, 0x1cc }, { 288, 0x14, 0x1cc }, { 313, 0x25, 0x1cc }, { 338, 0x35, 0x1cc },
+ { 375, 0x5, 0x1cc }, { 425, 0x16, 0x1cc }, { 475, 0x26, 0x1cc }, { 525, 0x37, 0x1cc },
+ { 575, 0x7, 0x1cc }, { 625, 0x18, 0x1cc }, { 675, 0x28, 0x1cc }, { 725, 0x39, 0x1cc },
+ { 775, 0x9, 0x1cc }, { 825, 0x19, 0x1cc }, { 875, 0x29, 0x1cc }, { 925, 0x3a, 0x1cc },
+ { 975, 0xa, 0x1cc }, { 1025, 0x1a, 0x1cc }, { 1075, 0x2a, 0x1cc }, { 1125, 0x3b, 0x1cc },
+ { 1175, 0xb, 0x1cc }, { 1225, 0x1b, 0x1cc }, { 1275, 0x2b, 0x1cc }, { 1325, 0x3c, 0x1cc },
+ { 1375, 0xc, 0x1cc }, { 1425, 0x1c, 0x1cc }, { 1475, 0x2c, 0x1cc }
+};
+
+/**
+ * hwd_VIIF_csi2rx_get_dphy_hs_transfer_info() - Get DPHY HS info from table
+ *
+ * @dphy_rate: DPHY clock in MHz
+ * @hsfreqrange: HS Frequency Range
+ * @osc_freq_target: OSC Frequency Target
+ * Return: None
+ */
+static void hwd_VIIF_csi2rx_get_dphy_hs_transfer_info(uint32_t dphy_rate, uint32_t *hsfreqrange,
+ uint32_t *osc_freq_target,
+ struct hwd_viif_res *res)
+{
+ int i;
+ int table_size = sizeof(dphy_hs_info) / sizeof(dphy_hs_info[0]);
+
+ for (i = 1; i < table_size; i++) {
+ if (dphy_rate < dphy_hs_info[i].rate) {
+ *hsfreqrange = dphy_hs_info[i - 1].hsfreqrange;
+ *osc_freq_target = dphy_hs_info[i - 1].osc_freq_target;
+ return;
+ }
+ }
+
+ /* not found; return the largest entry */
+ *hsfreqrange = dphy_hs_info[table_size - 1].hsfreqrange;
+ *osc_freq_target = dphy_hs_info[table_size - 1].osc_freq_target;
+}
+
+/**
+ * hwd_VIIF_csi2rx_set_dphy_rate() - Set D-PHY rate
+ *
+ * @dphy_rate: D-PHY rate of 1 Lane[Mbps] [80..1500]
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: None
+ */
+static void hwd_VIIF_csi2rx_set_dphy_rate(uint32_t dphy_rate, struct hwd_viif_res *res)
+{
+ uint32_t hsfreqrange, osc_freq_target;
+
+ hwd_VIIF_csi2rx_get_dphy_hs_transfer_info(dphy_rate, &hsfreqrange, &osc_freq_target, res);
+
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_SYS_1, (uint8_t)hsfreqrange, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_SYS_0, SYS_0_HSFREQRANGE_OVR, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_RX_STARTUP_OVR_5, STARTUP_OVR_5_BYPASS, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_RX_STARTUP_OVR_4, STARTUP_OVR_4_CNTVAL, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_CB_2, CB_2_LPRX_BIAS | CB_2_RESERVED, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_SYS_7, SYS_7_DESKEW_POL | SYS_7_RESERVED, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_CLKLANE_LANE_6, CLKLANE_RXHS_PULL_LONG, res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_RX_STARTUP_OVR_2,
+ FIELD_GET(0xff, osc_freq_target), res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_RX_STARTUP_OVR_3,
+ FIELD_GET(0xf00, osc_freq_target), res);
+ hwd_VIIF_csi2rx_write_dphy_param(DIG_RDWR_RX_RX_STARTUP_OVR_4,
+ STARTUP_OVR_4_CNTVAL | STARTUP_OVR_4_DDL_EN, res);
+
+ writel(HWD_VIIF_DPHY_CFG_CLK_25M, &(res->capture_reg->sys.DPHY_FREQRANGE));
+}
+
+/**
+ * hwd_VIIF_csi2rx_check_dphy_calibration_status() - Check D-PHY calibration status
+ *
+ * @test_mode: test code related to calibration information
+ * @shift_val_err: shift value related to error information
+ * @shift_val_done: shift value related to done information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: HWD_VIIF_CSI2_CAL_NOT_DONE calibration is not done(out of target or not completed)
+ * Return: HWD_VIIF_CSI2_CAL_FAIL calibration was failed
+ * Return: HWD_VIIF_CSI2_CAL_SUCCESS calibration was succeeded
+ */
+static uint32_t hwd_VIIF_csi2rx_check_dphy_calibration_status(uint32_t test_mode,
+ uint32_t shift_val_err,
+ uint32_t shift_val_done,
+ struct hwd_viif_res *res)
+{
+ uint32_t read_data;
+ uint32_t ret = HWD_VIIF_CSI2_CAL_NOT_DONE;
+
+ read_data = (uint32_t)hwd_VIIF_csi2rx_read_dphy_param(test_mode, res);
+
+ if (read_data & BIT(shift_val_done)) {
+ ret = HWD_VIIF_CSI2_CAL_SUCCESS;
+
+ /* error check is not required for termination calibration with REXT(0x221) */
+ if (test_mode == DIG_RD_RX_TERM_CAL_1)
+ return ret;
+
+ /* done with error */
+ if (read_data & BIT(shift_val_err))
+ ret = HWD_VIIF_CSI2_CAL_FAIL;
+ }
+
+ return ret;
+}
+
+/**
+ * hwd_VIIF_csi2rx_initialize() - Initialize CSI-2 RX driver
+ *
+ * @num_lane: [1..4](VIIF CH0-CH1)
+ * @lane_assign: lane connection. For more refer @ref hwd_VIIF_dphy_lane_assignment
+ * @dphy_rate: D-PHY rate of 1 Lane[Mbps] [80..1500]
+ * @rext_calibration: enable or disable rext calibration. For more refer @ref hwd_VIIF_csi2rx_cal_status
+ * @err_target: Pointer to configuration for Line error detection.
+ * @input_mode: CSI-2 input mode of VIIF CH1. For more refer @ref hwd_VIIF_csi2rx_input_mode
+ * @mask: MASK of CSI-2 RX error interruption
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "num_lane", "lane_assign", "dphy_rate", "rext_calibration" or "input_mode" is out of range
+ * - [2] "err_target" is NULL
+ * - [3] member of "err_target" is invalid
+ */
+int32_t hwd_VIIF_csi2rx_initialize(uint32_t module_id, uint32_t num_lane, uint32_t lane_assign,
+ uint32_t dphy_rate, uint32_t rext_calibration,
+ const struct hwd_viif_csi2rx_line_err_target *err_target,
+ uint32_t input_mode, const struct hwd_viif_csi2rx_irq_mask *mask)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t i, val;
+
+ if (res->csi2rx_type == HWD_VIIF_CSI2_TYPE_4_LANES) {
+ if ((num_lane == 0U) || (num_lane > 4U) ||
+ (lane_assign > HWD_VIIF_CSI2_DPHY_L0L2L1L3)) {
+ return -EINVAL;
+ }
+ } else {
+ if (((num_lane != 1U) && (num_lane != 2U)) ||
+ (lane_assign != HWD_VIIF_CSI2_DPHY_L0L1L2L3)) {
+ return -EINVAL;
+ }
+ }
+
+ if (dphy_rate < HWD_VIIF_DPHY_MIN_DATA_RATE)
+ return -EINVAL;
+
+ if (dphy_rate > HWD_VIIF_DPHY_MAX_DATA_RATE)
+ return -EINVAL;
+
+ if ((rext_calibration != HWD_VIIF_ENABLE) && (rext_calibration != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if (input_mode > HWD_VIIF_CSI2_INPUT_OTHER)
+ return -EINVAL;
+
+ if ((res->ch != 1U) && (input_mode != HWD_VIIF_CSI2_INPUT_OWN))
+ return -EINVAL;
+
+ if (err_target == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 8U; i++) {
+ if (err_target->vc[i] > HWD_VIIF_CSI2_MAX_VC)
+ return -EINVAL;
+
+ if (err_target->dt[i] > HWD_VIIF_CSI2_MAX_DT)
+ return -EINVAL;
+
+ if ((err_target->dt[i] < HWD_VIIF_CSI2_MIN_DT) && (err_target->dt[i] != 0U))
+ return -EINVAL;
+ }
+
+ if (input_mode == HWD_VIIF_CSI2_INPUT_OWN) {
+ /* 1st phase of initialization */
+ writel(HWD_VIIF_ENABLE, &(res->csi2host_reg->CSI2RX_RESETN));
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_PHY_RSTZ));
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_PHY_SHUTDOWNZ));
+ writel(HWD_VIIF_ENABLE, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ ndelay(15U);
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+
+ /* Configure D-PHY frequency range */
+ hwd_VIIF_csi2rx_set_dphy_rate(dphy_rate, res);
+
+ /* 2nd phase of initialization */
+ writel((num_lane - 1U), &(res->csi2host_reg->CSI2RX_NLANES));
+ ndelay(5U);
+
+ /* configuration not to use rext */
+ if (rext_calibration == HWD_VIIF_DISABLE) {
+ hwd_VIIF_csi2rx_write_dphy_param(0x004, 0x10, res);
+ ndelay(5U);
+ }
+
+ /* Release D-PHY from Reset */
+ writel(HWD_VIIF_ENABLE, &(res->csi2host_reg->CSI2RX_PHY_SHUTDOWNZ));
+ ndelay(5U);
+ writel(HWD_VIIF_ENABLE, &(res->csi2host_reg->CSI2RX_PHY_RSTZ));
+
+ /* configuration of line error target */
+ val = (err_target->vc[3] << 30U) | (err_target->dt[3] << 24U) |
+ (err_target->vc[2] << 22U) | (err_target->dt[2] << 16U) |
+ (err_target->vc[1] << 14U) | (err_target->dt[1] << 8U) |
+ (err_target->vc[0] << 6U) | (err_target->dt[0]);
+ writel(val, &(res->csi2host_reg->CSI2RX_DATA_IDS_1));
+ if (res->csi2rx_type == HWD_VIIF_CSI2_TYPE_4_LANES) {
+ val = (err_target->vc[7] << 30U) | (err_target->dt[7] << 24U) |
+ (err_target->vc[6] << 22U) | (err_target->dt[6] << 16U) |
+ (err_target->vc[5] << 14U) | (err_target->dt[5] << 8U) |
+ (err_target->vc[4] << 6U) | (err_target->dt[4]);
+ writel(val, &(res->csi2host_reg->CSI2RX_DATA_IDS_2));
+ }
+
+ /* configuration of mask */
+ writel(mask->mask[0], &(res->csi2host_reg->CSI2RX_INT_MSK_PHY_FATAL));
+ writel(mask->mask[1], &(res->csi2host_reg->CSI2RX_INT_MSK_PKT_FATAL));
+ writel(mask->mask[2], &(res->csi2host_reg->CSI2RX_INT_MSK_FRAME_FATAL));
+ writel(mask->mask[3], &(res->csi2host_reg->CSI2RX_INT_MSK_PHY));
+ writel(mask->mask[4], &(res->csi2host_reg->CSI2RX_INT_MSK_PKT));
+ writel(mask->mask[5], &(res->csi2host_reg->CSI2RX_INT_MSK_LINE));
+
+ /* configuration of lane assignment */
+ writel(lane_assign, &(res->capture_reg->sys.DPHY_LANE));
+
+ res->other_csi2_flag = false;
+ } else {
+ /* configuration of csi2 port */
+ writel(input_mode, &(res->capture_reg->sys.CSI2SELECT));
+
+ res->other_csi2_flag = true;
+ }
+ return 0;
+}
+
+/**
+ * hwd_VIIF_csi2rx_uninitialize() - Uninitialize CSI-2 RX driver
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ */
+int32_t hwd_VIIF_csi2rx_uninitialize(uint32_t module_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if (res->other_csi2_flag == false) {
+ /* CSI2RX can be uninitialized because it has been enabled. */
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_PHY_SHUTDOWNZ));
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_PHY_RSTZ));
+ writel(HWD_VIIF_ENABLE, &(res->csi2host_reg->CSI2RX_PHY_TESTCTRL0));
+ writel(HWD_VIIF_DISABLE, &(res->csi2host_reg->CSI2RX_RESETN));
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_csi2rx_start() - Start CSI-2 input
+ *
+ * @vc_main: control CSI-2 input of MAIN unit. enable with configured VC: 0, 1, 2 or 3, keep disabling:
+ * @vc_sub: control CSI-2 input of SUB unit. enable with configured VC: 0, 1, 2 or 3, keep disabling:
+ * @packet: Pointer to packet information of embedded data and long packet data
+ * @voif_through: enable or disable VOIF through output. For more refer @ref drv_VIIF_enable_flag
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * HWD_VIIF_CSI2_NOT_CAPTURE
+ * HWD_VIIF_CSI2_NOT_CAPTURE
+ * - [1] "vc_main", "vc_sub" or "voif_through" is out of range
+ * - [2] member of "packet" is invalid
+ * - [3] "voif_through" is not HWD_VIIF_DISABLE in case of CH other than CH1
+ */
+int32_t hwd_VIIF_csi2rx_start(uint32_t module_id, int32_t vc_main, int32_t vc_sub,
+ const struct hwd_viif_csi2rx_packet *packet, uint32_t voif_through)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t val, i, pitch, height, dt, start_addr, end_addr;
+ uint32_t enable_vc0 = HWD_VIIF_DISABLE, enable_vc1 = HWD_VIIF_DISABLE;
+
+ if ((vc_main > 3) || (vc_main < HWD_VIIF_CSI2_NOT_CAPTURE) || (vc_sub > 3) ||
+ (vc_sub < HWD_VIIF_CSI2_NOT_CAPTURE)) {
+ return -EINVAL;
+ }
+
+ if ((res->ch >= 2U) && (vc_sub != HWD_VIIF_CSI2_NOT_CAPTURE))
+ return -EINVAL;
+
+ for (i = 0; i < 4U; i++) {
+ if ((packet->word_count[i] > HWD_VIIF_CSI2_MAX_WORD_COUNT) ||
+ (packet->packet_num[i] > HWD_VIIF_CSI2_MAX_PACKET_NUM)) {
+ return -EINVAL;
+ }
+
+ if (((i == 2U) || (i == 3U)) && (res->ch >= 2U))
+ if ((packet->word_count[i] != 0U) || (packet->packet_num[i] != 0U))
+ return -EINVAL;
+ }
+
+ if ((voif_through != HWD_VIIF_ENABLE) && (voif_through != HWD_VIIF_DISABLE))
+ return -EINVAL;
+
+ if ((res->ch != 1U) && (voif_through == HWD_VIIF_ENABLE))
+ return -EINVAL;
+
+ writel(HWD_VIIF_INPUT_CSI2, &(res->capture_reg->sys.IPORTM));
+
+ if (vc_main != HWD_VIIF_CSI2_NOT_CAPTURE) {
+ writel((uint32_t)vc_main, &(res->capture_reg->sys.VCID0SELECT));
+ enable_vc0 = HWD_VIIF_ENABLE;
+ }
+ if (vc_sub != HWD_VIIF_CSI2_NOT_CAPTURE) {
+ writel((uint32_t)vc_sub, &(res->capture_reg->sys.VCID1SELECT));
+ enable_vc1 = HWD_VIIF_ENABLE;
+ }
+
+ if ((res->ch == 0U) || (res->ch == 1U)) {
+ /* emb of MAIN unit: word_count[0], packet_num[0] and w_port[1](= w01) */
+ pitch = ROUNDUP_BY_4(packet->word_count[0]);
+ writel(pitch, &(res->capture_reg->vdm.w_port[1].VDM_W_PITCH));
+ writel(packet->packet_num[0], &(res->capture_reg->vdm.w_port[1].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[1].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[1].VDM_W_ENDADR));
+
+ /* long packet of MAIN unit: word_count[1], packet_num[1] and w_port[0](= w00) */
+ dt = readl(&res->capture_reg->sys.IPORTM_OTHER);
+ if ((dt == CSI2_DT_YUV4208) || (dt == CSI2_DT_YUV4208) ||
+ (dt == CSI2_DT_YUV4208C) || (dt == CSI2_DT_YUV42010C)) {
+ pitch = ROUNDUP_BY_4(packet->word_count[1]) +
+ ROUNDUP_BY_4(packet->word_count[1] * 2U);
+ height = packet->packet_num[1] >> 1U;
+ } else {
+ pitch = ROUNDUP_BY_4(packet->word_count[1]);
+ height = packet->packet_num[1];
+ }
+ writel(pitch, &(res->capture_reg->vdm.w_port[0].VDM_W_PITCH));
+ writel(height, &(res->capture_reg->vdm.w_port[0].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[0].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[0].VDM_W_ENDADR));
+
+ /* emb of SUB unit: word_count[2], packet_num[2] and w_port[5](= w05) */
+ pitch = ROUNDUP_BY_4(packet->word_count[2]);
+ writel(pitch, &(res->capture_reg->vdm.w_port[5].VDM_W_PITCH));
+ writel(packet->packet_num[2], &(res->capture_reg->vdm.w_port[5].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[5].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[5].VDM_W_ENDADR));
+
+ /* long packet of SUB unit: word_count[3], packet_num[3] and w_port[4](= w04) */
+ dt = readl(&res->capture_reg->sys.IPORTS_OTHER);
+ if ((dt == CSI2_DT_YUV4208) || (dt == CSI2_DT_YUV42010) ||
+ (dt == CSI2_DT_YUV4208C) || (dt == CSI2_DT_YUV42010C)) {
+ pitch = ROUNDUP_BY_4(packet->word_count[3]) +
+ ROUNDUP_BY_4(packet->word_count[3] * 2U);
+ height = packet->packet_num[3] >> 1U;
+ } else {
+ pitch = ROUNDUP_BY_4(packet->word_count[3]);
+ height = packet->packet_num[3];
+ }
+ writel(pitch, &(res->capture_reg->vdm.w_port[4].VDM_W_PITCH));
+ writel(height, &(res->capture_reg->vdm.w_port[4].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[4].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[4].VDM_W_ENDADR));
+ } else {
+ /* emb of MAIN unit: word_count[0], packet_num[0] and w_port[4](= w04) */
+ pitch = ROUNDUP_BY_4(packet->word_count[0]);
+ writel(pitch, &(res->capture_reg->vdm.w_port[4].VDM_W_PITCH));
+ writel(packet->packet_num[0], &(res->capture_reg->vdm.w_port[4].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[4].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[4].VDM_W_ENDADR));
+
+ /* long packet of MAIN unit: word_count[1], packet_num[1] and w_port[3](= w03) */
+ dt = readl(&res->capture_reg->sys.IPORTM_OTHER);
+ if ((dt == CSI2_DT_YUV4208) || (dt == CSI2_DT_YUV42010) ||
+ (dt == CSI2_DT_YUV4208C) || (dt == CSI2_DT_YUV42010C)) {
+ pitch = ROUNDUP_BY_4(packet->word_count[1]) +
+ ROUNDUP_BY_4(packet->word_count[1] * 2U);
+ height = packet->packet_num[1] >> 1U;
+ } else {
+ pitch = ROUNDUP_BY_4(packet->word_count[1]);
+ height = packet->packet_num[1];
+ }
+ writel(pitch, &(res->capture_reg->vdm.w_port[3].VDM_W_PITCH));
+ writel(height, &(res->capture_reg->vdm.w_port[3].VDM_W_HEIGHT));
+ start_addr = readl(&res->capture_reg->vdm.w_port[3].VDM_W_STADR);
+ end_addr = start_addr + pitch - 1U;
+ writel(end_addr, &(res->capture_reg->vdm.w_port[3].VDM_W_ENDADR));
+
+ /* Set CSI2 sync to Memory input mode for test data usecase */
+ writel(HWD_VIIF_MEM_SYNC_CSI2, &(res->capture_reg->sys.IPORTI_M_SYNCMODE));
+ }
+
+ if (voif_through == HWD_VIIF_ENABLE) {
+ /* Enable VOIF through input */
+ writel(voif_through, &(res->capture_reg->sys.CSI2THROUGHEN));
+ }
+
+ /* Control VC port enable */
+ val = enable_vc0 | (enable_vc1 << 4U);
+ writel(val, &(res->capture_reg->sys.VCPORTEN));
+
+ if (enable_vc0 == HWD_VIIF_ENABLE) {
+ /* Update flag information for run status of MAIN unit */
+ res->run_flag_main = true;
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_csi2rx_stop() - Stop CSI-2 input
+ *
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -ETIMEDOUT Driver timeout error
+ */
+int32_t hwd_VIIF_csi2rx_stop(uint32_t module_id)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+ uint32_t status_r, status_w, status_t, l2_status;
+ uint64_t timeout_ns, cur_ns;
+ bool run_flag = true;
+ int32_t ret = 0;
+
+ if ((res->ch != 0U) && (res->ch != 1U))
+ return -1;
+
+ /* Disable auto transmission of register buffer */
+ writel(0, &(res->capture_reg->l1isp.L1_CRGBF_TRN_A_CONF));
+ writel(0, &(res->capture_reg->l2isp.L2_CRGBF_TRN_A_CONF));
+
+ /* Wait for completion of register buffer transmission */
+ udelay(HWD_VIIF_WAIT_ISP_REGBF_TRNS_COMPLETE_TIME);
+
+ if (res->ch == 1U) {
+ /* Disable VOIF through input */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.CSI2THROUGHEN));
+ }
+
+ /* Stop all VCs, long packet input and emb data input of MAIN unit */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.VCPORTEN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTM_OTHEREN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTM_EMBEN));
+
+ /* Stop image data input, long packet input and emb data input of SUB unit */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_OTHEREN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_EMBEN));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->sys.IPORTS_IMGEN));
+
+ /* Stop VDMAC for all table ports, input ports and write ports */
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_T_ENABLE));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_R_ENABLE));
+ writel(HWD_VIIF_DISABLE, &(res->capture_reg->vdm.VDM_W_ENABLE));
+
+ /* Stop all groups(g00, g01 and g02) of VDMAC */
+ writel(0x7, &(res->capture_reg->vdm.VDM_ABORTSET));
+
+ timeout_ns = ktime_get_ns() + HWD_VIIF_WAIT_ABORT_COMPLETE_TIME * 1000;
+
+ do {
+ /* Get VDMAC transfer status */
+ status_r = readl(&res->capture_reg->vdm.VDM_R_RUN);
+ status_w = readl(&res->capture_reg->vdm.VDM_W_RUN);
+ status_t = readl(&res->capture_reg->vdm.VDM_T_RUN);
+
+ l2_status = readl(&res->capture_reg->l2isp.L2_BUS_L2_STATUS);
+
+ if ((status_r == 0U) && (status_w == 0U) && (status_t == 0U) && (l2_status == 0U))
+ run_flag = false;
+
+ cur_ns = ktime_get_ns();
+
+ if (cur_ns > timeout_ns) {
+ ret = -ETIMEDOUT;
+ run_flag = false;
+ }
+ } while (run_flag == true);
+
+ if (ret == 0) {
+ /* Clear run flag of MAIN unit */
+ res->run_flag_main = false;
+ }
+
+ return ret;
+}
+
+/**
+ * hwd_VIIF_csi2rx_get_dphy_status() - Get CSI-2 RX D-PHY status
+ *
+ * @ulps: Pointer to ULPS information
+ * @stop: Pointer to STOP status information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "ulps" or "stop" is NULL
+ */
+int32_t hwd_VIIF_csi2rx_get_dphy_status(uint32_t module_id, uint32_t *ulps, uint32_t *stop)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if (ulps == NULL)
+ return -EINVAL;
+
+ if (stop == NULL)
+ return -EINVAL;
+
+ *ulps = readl(&res->csi2host_reg->CSI2RX_PHY_RX);
+ *stop = readl(&res->csi2host_reg->CSI2RX_PHY_STOPSTATE);
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_csi2rx_get_calibration_status() - Get CSI-2 RX calibration status
+ *
+ * @calibration_status: Pointer to D-PHY calibration status information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "calibration_status" is NULL
+ */
+int32_t hwd_VIIF_csi2rx_get_calibration_status(
+ uint32_t module_id, struct hwd_viif_csi2rx_dphy_calibration_status *calibration_status)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if (calibration_status == NULL)
+ return -EINVAL;
+
+ /* arg0; test register, arg1: error bit, arg2: done bit */
+ /* 0x221: termination calibration with REXT */
+ calibration_status->term_cal_with_rext =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_TERM_CAL_1, 0, 7, res);
+ /* 0x39D: clock lane offset calibration */
+ calibration_status->clock_lane_offset_cal = hwd_VIIF_csi2rx_check_dphy_calibration_status(
+ DIG_RD_RX_CLKLANE_OFFSET_CAL_0, 4, 0, res);
+ /* 0x59F: data lane0 offset calibration */
+ calibration_status->data_lane0_offset_cal = hwd_VIIF_csi2rx_check_dphy_calibration_status(
+ DIG_RD_RX_LANE0_OFFSET_CAL_0, 2, 1, res);
+ /* 0x79F: data lane1 offset calibration */
+ calibration_status->data_lane1_offset_cal = hwd_VIIF_csi2rx_check_dphy_calibration_status(
+ DIG_RD_RX_LANE1_OFFSET_CAL_0, 2, 1, res);
+ /* 0x5E0: data lane0 DDL(Digital Delay Line) calibration */
+ calibration_status->data_lane0_ddl_tuning_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE0_DDL_0, 1, 2, res);
+ /* 0x7E0: data lane1 DDL calibration */
+ calibration_status->data_lane1_ddl_tuning_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE1_DDL_0, 1, 2, res);
+
+ if (res->csi2rx_type == HWD_VIIF_CSI2_TYPE_4_LANES) {
+ /* 0x99F: data lane2 offset calibration */
+ calibration_status->data_lane2_offset_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE2_OFFSET_CAL_0,
+ 2, 1, res);
+ /* 0xB9F: data lane3 offset calibration */
+ calibration_status->data_lane3_offset_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE3_OFFSET_CAL_0,
+ 2, 1, res);
+ /* 0x9E0: data lane2 DDL calibration */
+ calibration_status->data_lane2_ddl_tuning_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE2_DDL_0, 1, 2,
+ res);
+ /* 0xBE0: data lane3 DDL calibration */
+ calibration_status->data_lane3_ddl_tuning_cal =
+ hwd_VIIF_csi2rx_check_dphy_calibration_status(DIG_RD_RX_LANE3_DDL_0, 1, 2,
+ res);
+ } else {
+ calibration_status->data_lane2_offset_cal = HWD_VIIF_CSI2_CAL_NOT_DONE;
+ calibration_status->data_lane3_offset_cal = HWD_VIIF_CSI2_CAL_NOT_DONE;
+ calibration_status->data_lane2_ddl_tuning_cal = HWD_VIIF_CSI2_CAL_NOT_DONE;
+ calibration_status->data_lane3_ddl_tuning_cal = HWD_VIIF_CSI2_CAL_NOT_DONE;
+ }
+
+ return 0;
+}
+
+/**
+ * hwd_VIIF_csi2rx_get_err_status() - Get CSI-2 RX error status
+ *
+ * @err_phy_fatal: Pointer to D-PHY fatal error information
+ * @err_pkt_fatal: Pointer to Packet fatal error information
+ * @err_frame_fatal: Pointer to Frame fatal error information
+ * @err_phy: Pointer to D-PHY error information
+ * @err_pkt: Pointer to Packet error information
+ * @err_line: Pointer to Line error information
+ * @module_id: ID of each VIIF module; must be 0 or 1
+ * Return: 0 Operation completes successfully
+ * Return: -EINVAL Parameter error
+ * - [1] "err_phy_fatal", "err_pkt_fatal", "err_frame_fatal", "err_phy", "err_pkt" or "err_line" is NULL
+ */
+int32_t hwd_VIIF_csi2rx_get_err_status(uint32_t module_id, uint32_t *err_phy_fatal,
+ uint32_t *err_pkt_fatal, uint32_t *err_frame_fatal,
+ uint32_t *err_phy, uint32_t *err_pkt, uint32_t *err_line)
+{
+ struct hwd_viif_res *res = viif_id2res(module_id);
+
+ if ((err_phy_fatal == NULL) || (err_pkt_fatal == NULL) || (err_frame_fatal == NULL) ||
+ (err_phy == NULL) || (err_pkt == NULL) || (err_line == NULL)) {
+ return -EINVAL;
+ }
+ *err_phy_fatal = readl(&res->csi2host_reg->CSI2RX_INT_ST_PHY_FATAL);
+ *err_pkt_fatal = readl(&res->csi2host_reg->CSI2RX_INT_ST_PKT_FATAL);
+ *err_frame_fatal = readl(&res->csi2host_reg->CSI2RX_INT_ST_FRAME_FATAL);
+ *err_phy = readl(&res->csi2host_reg->CSI2RX_INT_ST_PHY);
+ *err_pkt = readl(&res->csi2host_reg->CSI2RX_INT_ST_PKT);
+ *err_line = readl(&res->csi2host_reg->CSI2RX_INT_ST_LINE);
+
+ return 0;
+}
diff --git a/drivers/media/platform/visconti/viif.c b/drivers/media/platform/visconti/viif.c
new file mode 100644
index 000000000..1869f0267
--- /dev/null
+++ b/drivers/media/platform/visconti/viif.c
@@ -0,0 +1,1830 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Toshiba Visconti Video Capture Support
+ *
+ * (C) Copyright 2022 TOSHIBA CORPORATION
+ * (C) Copyright 2022 Toshiba Electronic Devices & Storage Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "viif.h"
+
+#define DRIVER_NAME "viif"
+
+#define VIIF_CROP_MAX_X_ISP (8062U)
+#define VIIF_CROP_MAX_Y_ISP (3966U)
+#define VIIF_CROP_MIN_W (128U)
+#define VIIF_CROP_MAX_W_ISP (8190U)
+#define VIIF_CROP_MIN_H (128U)
+#define VIIF_CROP_MAX_H_ISP (4094U)
+
+#define VIIF_ISP_GUARD_START(viif_dev) \
+ do { \
+ hwd_VIIF_isp_disable_regbuf_auto_transmission(viif_dev->ch); \
+ ndelay(500); \
+ hwd_VIIF_main_mask_vlatch(viif_dev->ch, HWD_VIIF_ENABLE); \
+ } while (0)
+
+#define VIIF_ISP_GUARD_END(viif_dev) \
+ do { \
+ hwd_VIIF_main_mask_vlatch(viif_dev->ch, HWD_VIIF_DISABLE); \
+ hwd_VIIF_isp_set_regbuf_auto_transmission(viif_dev->ch, VIIF_ISP_REGBUF_0, \
+ VIIF_ISP_REGBUF_0, 0); \
+ } while (0)
+
+struct viif_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+};
+
+void viif_hw_on(struct viif_device *viif_dev)
+{
+ hwd_VIIF_initialize(viif_dev->ch, viif_dev->csi2host_reg, viif_dev->capture_reg);
+}
+
+void viif_hw_off(struct viif_device *viif_dev)
+{
+ /* Uninitialize HWD driver */
+ hwd_VIIF_uninitialize(viif_dev->ch);
+}
+
+static inline struct viif_buffer *vb2_to_viif(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct viif_buffer, vb);
+}
+
+static inline struct viif_device *v4l2_to_viif(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct viif_device, v4l2_dev);
+}
+
+static struct viif_subdev *to_viif_subdev(struct v4l2_async_subdev *asd)
+{
+ return container_of(asd, struct viif_subdev, asd);
+}
+
+static int viif_get_dv_timings(struct viif_device *viif_dev, struct v4l2_dv_timings *timings)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_ctrl *ctrl;
+ int ret;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = {
+ .pads = &pad_cfg,
+ };
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = 0,
+ };
+
+ /* some video I/F support dv_timings query */
+ ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings);
+ if (ret == 0)
+ return 0;
+
+ /* others: call some discrete APIs */
+ ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_fmt, &pad_state, &format);
+ if (ret != 0)
+ return ret;
+
+ timings->bt.width = format.format.width;
+ timings->bt.height = format.format.height;
+
+ ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_HBLANK);
+ if (!ctrl) {
+ dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n");
+ return -EINVAL;
+ }
+ timings->bt.hsync = v4l2_ctrl_g_ctrl(ctrl);
+
+ ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_VBLANK);
+ if (!ctrl) {
+ dev_err(viif_dev->dev, "subdev: V4L2_CID_VBLANK error.\n");
+ return -EINVAL;
+ }
+ timings->bt.vsync = v4l2_ctrl_g_ctrl(ctrl);
+
+ ctrl = v4l2_ctrl_find(viif_sd->v4l2_sd->ctrl_handler, V4L2_CID_PIXEL_RATE);
+ if (!ctrl) {
+ dev_err(viif_dev->dev, "subdev: V4L2_CID_PIXEL_RATE error.\n");
+ return -EINVAL;
+ }
+ timings->bt.pixelclock = v4l2_ctrl_g_ctrl_int64(ctrl);
+
+ return 0;
+}
+
+static int viif_main_set_unit(struct viif_device *viif_dev)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_dv_timings timings;
+ unsigned int dt_image, color_type, rawpack, yuv_conv;
+ struct hwd_viif_input_img in_img_main;
+ int ret = 0;
+ int mag_hactive = 1;
+ struct hwd_viif_l2_undist undist = { 0 };
+
+ ret = viif_get_dv_timings(viif_dev, &timings);
+ if (ret) {
+ dev_err(viif_dev->dev, "could not get timing information of subdev");
+ return -EINVAL;
+ }
+
+ viif_dev->mbus_is_rgb = false;
+
+ switch (viif_sd->mbus_code) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ dt_image = VISCONTI_CSI2_DT_RGB888;
+ viif_dev->mbus_is_rgb = true;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ dt_image = VISCONTI_CSI2_DT_YUV4228B;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ dt_image = VISCONTI_CSI2_DT_YUV42210B;
+ break;
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ dt_image = VISCONTI_CSI2_DT_RGB565;
+ viif_dev->mbus_is_rgb = true;
+ break;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ dt_image = VISCONTI_CSI2_DT_RAW8;
+ break;
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ dt_image = VISCONTI_CSI2_DT_RAW10;
+ break;
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ dt_image = VISCONTI_CSI2_DT_RAW12;
+ break;
+ case MEDIA_BUS_FMT_SRGGB14_1X14:
+ case MEDIA_BUS_FMT_SGRBG14_1X14:
+ case MEDIA_BUS_FMT_SGBRG14_1X14:
+ case MEDIA_BUS_FMT_SBGGR14_1X14:
+ dt_image = VISCONTI_CSI2_DT_RAW14;
+ break;
+ default:
+ dt_image = VISCONTI_CSI2_DT_RGB888;
+ break;
+ }
+
+ color_type = dt_image;
+
+ if ((color_type == VISCONTI_CSI2_DT_RAW8) || (color_type == VISCONTI_CSI2_DT_RAW10) ||
+ (color_type == VISCONTI_CSI2_DT_RAW12)) {
+ rawpack = viif_dev->rawpack_mode;
+ if (rawpack != HWD_VIIF_RAWPACK_DISABLE)
+ mag_hactive = 2;
+ } else
+ rawpack = HWD_VIIF_RAWPACK_DISABLE;
+
+ if ((color_type == VISCONTI_CSI2_DT_YUV4228B) || (color_type == VISCONTI_CSI2_DT_YUV42210B))
+ yuv_conv = HWD_VIIF_YUV_CONV_INTERPOLATION;
+ else
+ yuv_conv = HWD_VIIF_YUV_CONV_REPEAT;
+
+ in_img_main.hactive_size = timings.bt.width;
+ in_img_main.vactive_size = timings.bt.height;
+ in_img_main.htotal_size = timings.bt.width * mag_hactive + timings.bt.hsync;
+ in_img_main.vtotal_size = timings.bt.height + timings.bt.vsync;
+ in_img_main.pixel_clock = timings.bt.pixelclock / 1000;
+ in_img_main.vbp_size = timings.bt.vsync - 5;
+ viif_dev->pixel_clock = in_img_main.pixel_clock;
+
+ in_img_main.interpolation_mode = HWD_VIIF_L1_INPUT_INTERPOLATION_LINE;
+ in_img_main.input_num = 1;
+ in_img_main.hobc_width = 0;
+ in_img_main.hobc_margin = 0;
+
+ /* configuration of MAIN unit */
+ ret = hwd_VIIF_main_set_unit(viif_dev->ch, dt_image, 0, &in_img_main, color_type, rawpack,
+ yuv_conv);
+ if (ret) {
+ dev_err(viif_dev->dev, "main_set_unit error. %d\n", ret);
+ return ret;
+ }
+
+ /* Enable regbuf */
+ hwd_VIIF_isp_set_regbuf_auto_transmission(viif_dev->ch, VIIF_ISP_REGBUF_0,
+ VIIF_ISP_REGBUF_0, 0);
+
+ /* L2 UNDIST Enable through mode as default */
+ undist.through_mode = HWD_VIIF_ENABLE;
+ undist.sensor_crop_ofs_h = 1 - in_img_main.hactive_size;
+ undist.sensor_crop_ofs_v = 1 - in_img_main.vactive_size;
+ undist.grid_node_num_h = 16;
+ undist.grid_node_num_v = 16;
+ ret = hwd_VIIF_l2_set_undist(viif_dev->ch, VIIF_ISP_REGBUF_0, &undist);
+ if (ret)
+ dev_err(viif_dev->dev, "l2_set_undist error. %d\n", ret);
+ return ret;
+}
+
+struct visconti_mbus_format {
+ unsigned int code;
+ unsigned int bpp;
+} static visconti_mbus_formats[] = {
+ { .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24 },
+ { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_UYVY10_1X20, .bpp = 20 },
+ { .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8 },
+ { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10 },
+ { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12 },
+ { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14 },
+ { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14 },
+};
+
+static unsigned int viif_get_mbus_bpp(unsigned int mbus_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++)
+ if (visconti_mbus_formats[i].code == mbus_code)
+ return visconti_mbus_formats[i].bpp;
+
+ /* default bpp value */
+ return 24;
+}
+
+static bool viif_is_valid_mbus_code(unsigned int mbus_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(visconti_mbus_formats); i++)
+ if (visconti_mbus_formats[i].code == mbus_code)
+ return true;
+ return false;
+}
+
+static int viif_init_mbus_code(struct viif_device *viif_dev)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ struct device *dev = viif_dev->dev;
+
+ struct v4l2_subdev_mbus_code_enum code = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .index = 0,
+ };
+
+ while (!v4l2_subdev_call(v4l2_sd, pad, enum_mbus_code, NULL, &code)) {
+ if (viif_is_valid_mbus_code(code.code)) {
+ viif_sd->mbus_code = code.code;
+ viif_sd->bpp = viif_get_mbus_bpp(viif_sd->mbus_code);
+ dev_err(dev, "Found media bus: code=0x%x bpp=%d\n", viif_sd->mbus_code,
+ viif_sd->bpp);
+ break;
+ }
+ code.index++;
+ }
+ if (!viif_sd->mbus_code) {
+ dev_err(dev, "Unsupported media bus format for %s\n", v4l2_sd->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* L2ISP output csc setting for YUV to RGB(ITU-R BT.709) */
+static const struct hwd_viif_csc_param viif_csc_yuv2rgb = {
+ .r_cr_in_offset = 0x18000,
+ .g_y_in_offset = 0x1f000,
+ .b_cb_in_offset = 0x18000,
+ .coef = {
+ [0] = 0x1000,
+ [1] = 0xfd12,
+ [2] = 0xf8ad,
+ [3] = 0x1000,
+ [4] = 0x1d07,
+ [5] = 0x0000,
+ [6] = 0x1000,
+ [7] = 0x0000,
+ [8] = 0x18a2,
+ },
+ .r_cr_out_offset = 0x1000,
+ .g_y_out_offset = 0x1000,
+ .b_cb_out_offset = 0x1000,
+};
+
+/* L2ISP output csc setting for RGB to YUV(ITU-R BT.709) */
+static const struct hwd_viif_csc_param viif_csc_rgb2yuv = {
+ .r_cr_in_offset = 0x1f000,
+ .g_y_in_offset = 0x1f000,
+ .b_cb_in_offset = 0x1f000,
+ .coef = {
+ [0] = 0x0b71,
+ [1] = 0x0128,
+ [2] = 0x0367,
+ [3] = 0xf9b1,
+ [4] = 0x082f,
+ [5] = 0xfe20,
+ [6] = 0xf891,
+ [7] = 0xff40,
+ [8] = 0x082f,
+ },
+ .r_cr_out_offset = 0x8000,
+ .g_y_out_offset = 0x1000,
+ .b_cb_out_offset = 0x8000,
+};
+
+static int viif_l2_set_format(struct viif_device *viif_dev)
+{
+ struct v4l2_pix_format_mplane *pix = &viif_dev->v4l2_pix;
+ const struct hwd_viif_csc_param *csc_param = NULL;
+ bool out_is_rgb = false;
+
+ viif_dev->out_process.half_scale = HWD_VIIF_DISABLE;
+ viif_dev->out_process.select_color = HWD_VIIF_COLOR_YUV_RGB;
+ viif_dev->out_process.alpha = 0;
+
+ if (viif_dev->l2_crop_set_flag) {
+ viif_dev->img_area.x = viif_dev->l2_crop_param.x;
+ viif_dev->img_area.y = viif_dev->l2_crop_param.y;
+ viif_dev->img_area.w = viif_dev->l2_crop_param.w;
+ viif_dev->img_area.h = viif_dev->l2_crop_param.h;
+ } else {
+ viif_dev->img_area.x = 0;
+ viif_dev->img_area.y = 0;
+ viif_dev->img_area.w = pix->width;
+ viif_dev->img_area.h = pix->height;
+ }
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_RGB24:
+ viif_dev->out_format = HWD_VIIF_RGB888_PACKED;
+ out_is_rgb = true;
+ break;
+ case V4L2_PIX_FMT_ABGR32:
+ viif_dev->out_format = HWD_VIIF_ARGB8888_PACKED;
+ viif_dev->out_process.alpha = 0xff;
+ out_is_rgb = true;
+ break;
+ case V4L2_PIX_FMT_YUV422M:
+ viif_dev->out_format = HWD_VIIF_YCBCR422_8_PLANAR;
+ break;
+ case V4L2_PIX_FMT_YUV444M:
+ viif_dev->out_format = HWD_VIIF_RGB888_YCBCR444_8_PLANAR;
+ break;
+ case V4L2_PIX_FMT_Y16:
+ viif_dev->out_format = HWD_VIIF_ONE_COLOR_16;
+ viif_dev->out_process.select_color = HWD_VIIF_COLOR_Y_G;
+ break;
+ }
+
+ if (!viif_dev->mbus_is_rgb && out_is_rgb)
+ csc_param = &viif_csc_yuv2rgb; /* YUV -> RGB */
+ else if (viif_dev->mbus_is_rgb && !out_is_rgb)
+ csc_param = &viif_csc_rgb2yuv; /* RGB -> YUV */
+
+ return hwd_VIIF_l2_set_output_csc(viif_dev->ch, VIIF_L2ISP_POST_0, VIIF_ISP_REGBUF_0,
+ csc_param);
+}
+
+/* -----CSI2RX----- */
+static int viif_csi2rx_initialize(struct viif_device *viif_dev)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct hwd_viif_csi2rx_line_err_target err_target = { 0 };
+ struct hwd_viif_csi2rx_irq_mask csi2rx_mask;
+ struct v4l2_mbus_config cfg = { 0 };
+ int num_lane, dphy_rate;
+ int ret;
+
+ ret = v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_mbus_config, 0, &cfg);
+ if (ret) {
+ dev_dbg(viif_dev->dev, "subdev: g_mbus_config error. %d\n", ret);
+ num_lane = viif_sd->num_lane;
+ } else {
+ switch (cfg.flags & V4L2_MBUS_CSI2_LANES) {
+ case V4L2_MBUS_CSI2_1_LANE:
+ num_lane = 1;
+ break;
+ case V4L2_MBUS_CSI2_2_LANE:
+ num_lane = 2;
+ break;
+ case V4L2_MBUS_CSI2_3_LANE:
+ num_lane = 3;
+ break;
+ case V4L2_MBUS_CSI2_4_LANE:
+ num_lane = 4;
+ break;
+ default:
+ num_lane = 4;
+ break;
+ }
+ }
+
+ dphy_rate = viif_dev->pixel_clock * viif_sd->bpp / num_lane;
+ dphy_rate = dphy_rate / 1000;
+
+ /* check error for CH0: all supported DTs */
+ err_target.dt[0] = VISCONTI_CSI2_DT_RGB565;
+ err_target.dt[1] = VISCONTI_CSI2_DT_YUV4228B;
+ err_target.dt[2] = VISCONTI_CSI2_DT_YUV42210B;
+ err_target.dt[3] = VISCONTI_CSI2_DT_RGB888;
+ err_target.dt[4] = VISCONTI_CSI2_DT_RAW8;
+ err_target.dt[5] = VISCONTI_CSI2_DT_RAW10;
+ err_target.dt[6] = VISCONTI_CSI2_DT_RAW12;
+ err_target.dt[7] = VISCONTI_CSI2_DT_RAW14;
+
+ /* Define errors to be masked */
+ csi2rx_mask.mask[0] = 0x0000000F; /*check all for PHY_FATAL*/
+ csi2rx_mask.mask[1] = 0x0001000F; /*check all for PKT_FATAL*/
+ csi2rx_mask.mask[2] = 0x000F0F0F; /*check all for FRAME_FATAL*/
+ csi2rx_mask.mask[3] = 0x000F000F; /*check all for PHY*/
+ csi2rx_mask.mask[4] = 0x000F000F; /*check all for PKT*/
+ csi2rx_mask.mask[5] = 0x00FF00FF; /*check all for LINE*/
+
+ return hwd_VIIF_csi2rx_initialize(viif_dev->ch, num_lane, HWD_VIIF_CSI2_DPHY_L0L1L2L3,
+ dphy_rate, HWD_VIIF_ENABLE, &err_target,
+ HWD_VIIF_CSI2_INPUT_OWN, &csi2rx_mask);
+}
+
+static int viif_csi2rx_start(struct viif_device *viif_dev)
+{
+ uint32_t vc_main = 0;
+ struct hwd_viif_csi2rx_packet packet = { 0 };
+
+ viif_dev->masked_gamma_path = 0U;
+
+ return hwd_VIIF_csi2rx_start(viif_dev->ch, vc_main, HWD_VIIF_CSI2_NOT_CAPTURE, &packet,
+ HWD_VIIF_DISABLE);
+}
+
+static int viif_csi2rx_stop(struct viif_device *viif_dev)
+{
+ int32_t ret;
+
+ ret = hwd_VIIF_csi2rx_stop(viif_dev->ch);
+ if (ret)
+ dev_err(viif_dev->dev, "csi2rx_stop error. %d\n", ret);
+
+ hwd_VIIF_csi2rx_uninitialize(viif_dev->ch);
+
+ return ret;
+}
+
+/* ----- Private IOCTLs----- */
+static int viif_main_set_rawpack_mode(struct viif_device *viif_dev, uint32_t *rawpack)
+{
+ if (vb2_is_streaming(&viif_dev->vb2_vq))
+ return -EBUSY;
+
+ if ((*rawpack != VIIF_RAWPACK_DISABLE) && (*rawpack != VIIF_RAWPACK_MSBFIRST) &&
+ (*rawpack != VIIF_RAWPACK_LSBFIRST))
+ return -EINVAL;
+
+ viif_dev->rawpack_mode = *rawpack;
+ return 0;
+}
+
+#define VISCONTI_VIIF_DPC_TABLE_SIZE_MIN 1024
+#define VISCONTI_VIIF_DPC_TABLE_SIZE_MAX 8192
+static int viif_l2_set_undist(struct viif_device *viif_dev, struct viif_l2_undist_config *undist)
+{
+ int ret;
+ unsigned long irqflags;
+ uintptr_t table_write_g_paddr = 0;
+ uintptr_t table_read_b_paddr = 0;
+ uintptr_t table_read_g_paddr = 0;
+ uintptr_t table_read_r_paddr = 0;
+
+ if ((undist->size && (undist->size < VISCONTI_VIIF_DPC_TABLE_SIZE_MIN)) ||
+ (undist->size > VISCONTI_VIIF_DPC_TABLE_SIZE_MAX))
+ return -EINVAL;
+
+ if (undist->write_g) {
+ if (copy_from_user(viif_dev->table_vaddr->undist_write_g,
+ (void __user *)undist->write_g, undist->size))
+ return -EFAULT;
+ table_write_g_paddr = (uintptr_t)viif_dev->table_paddr->undist_write_g;
+ }
+ if (undist->read_b) {
+ if (copy_from_user(viif_dev->table_vaddr->undist_read_b,
+ (void __user *)undist->read_b, undist->size))
+ return -EFAULT;
+ table_read_b_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_b;
+ }
+ if (undist->read_g) {
+ if (copy_from_user(viif_dev->table_vaddr->undist_read_g,
+ (void __user *)undist->read_g, undist->size))
+ return -EFAULT;
+ table_read_g_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_g;
+ }
+ if (undist->read_r) {
+ if (copy_from_user(viif_dev->table_vaddr->undist_read_r,
+ (void __user *)undist->read_r, undist->size))
+ return -EFAULT;
+ table_read_r_paddr = (uintptr_t)viif_dev->table_paddr->undist_read_r;
+ }
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ VIIF_ISP_GUARD_START(viif_dev);
+ ret = hwd_VIIF_l2_set_undist_table_transmission(viif_dev->ch, table_write_g_paddr,
+ table_read_b_paddr, table_read_g_paddr,
+ table_read_r_paddr, undist->size);
+ if (ret) {
+ dev_err(viif_dev->dev, "l2_set_undist_table_transmission error. %d\n", ret);
+ goto err;
+ }
+
+ ret = hwd_VIIF_l2_set_undist(viif_dev->ch, VIIF_ISP_REGBUF_0,
+ (struct hwd_viif_l2_undist *)&undist->param);
+err:
+ VIIF_ISP_GUARD_END(viif_dev);
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+ return ret;
+}
+
+static int viif_l2_set_roi(struct viif_device *viif_dev, struct viif_l2_roi_config *roi)
+{
+ int ret;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ VIIF_ISP_GUARD_START(viif_dev);
+ ret = hwd_VIIF_l2_set_roi(viif_dev->ch, VIIF_ISP_REGBUF_0, (struct hwd_viif_l2_roi *)roi);
+ VIIF_ISP_GUARD_END(viif_dev);
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+ return ret;
+}
+
+#define VISCONTI_VIIF_GANMMA_TABLE_SIZE 512
+static int viif_l2_set_gamma(struct viif_device *viif_dev, struct viif_l2_gamma_config *l2_gamma)
+{
+ int ret;
+ unsigned long irqflags;
+ struct hwd_viif_l2_gamma_table hwd_table = { 0 };
+ uint32_t i;
+
+ for (i = 0; i < 6; i++) {
+ if (l2_gamma->table[i]) {
+ if (copy_from_user(viif_dev->table_vaddr->l2_gamma_table[i],
+ (void __user *)l2_gamma->table[i],
+ VISCONTI_VIIF_GANMMA_TABLE_SIZE))
+ return -EFAULT;
+ hwd_table.table[i] = (uintptr_t)viif_dev->table_paddr->l2_gamma_table[i];
+ }
+ }
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ VIIF_ISP_GUARD_START(viif_dev);
+ ret = hwd_VIIF_l2_set_gamma_table_transmission(viif_dev->ch, VIIF_L2ISP_POST_0, &hwd_table);
+ if (ret)
+ goto err;
+
+ ret = hwd_VIIF_l2_set_gamma(viif_dev->ch, VIIF_L2ISP_POST_0, VIIF_ISP_REGBUF_0,
+ l2_gamma->enable, l2_gamma->vsplit, l2_gamma->mode);
+err:
+ VIIF_ISP_GUARD_END(viif_dev);
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+ return ret;
+}
+
+static int viif_l2_set_crop(struct viif_device *viif_dev, struct viif_l2_crop_config *l2_crop)
+{
+ if ((l2_crop->x > VIIF_CROP_MAX_X_ISP) || (l2_crop->y > VIIF_CROP_MAX_Y_ISP) ||
+ (l2_crop->w < VIIF_CROP_MIN_W) || (l2_crop->w > VIIF_CROP_MAX_W_ISP) ||
+ (l2_crop->h < VIIF_CROP_MIN_H) || (l2_crop->h > VIIF_CROP_MAX_H_ISP)) {
+ return -EINVAL;
+ }
+
+ if (viif_dev->l2_crop_set_flag) {
+ if ((l2_crop->w != viif_dev->l2_crop_param.w) ||
+ (l2_crop->h != viif_dev->l2_crop_param.h))
+ return -EINVAL;
+ }
+
+ viif_dev->l2_crop_param.x = l2_crop->x;
+ viif_dev->l2_crop_param.y = l2_crop->y;
+ viif_dev->l2_crop_param.w = l2_crop->w;
+ viif_dev->l2_crop_param.h = l2_crop->h;
+
+ viif_dev->l2_crop_set_flag = true;
+
+ return 0;
+}
+
+static int viif_csi2rx_set_mbus_fmt(struct viif_device *viif_dev, uint32_t *mbus_code)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = {
+ .pads = &pad_cfg,
+ };
+ struct v4l2_subdev_format format = {
+ .pad = 0,
+ };
+ int ret = -EINVAL;
+
+ if (vb2_is_streaming(&viif_dev->vb2_vq))
+ return -EBUSY;
+
+ if (viif_is_valid_mbus_code(*mbus_code)) {
+ ret = v4l2_subdev_call(v4l2_sd, pad, get_fmt, &pad_state, &format);
+ if (ret) {
+ dev_err(viif_dev->dev, "subdev: get_fmt error. %d\n", ret);
+ return ret;
+ }
+
+ format.format.code = *mbus_code;
+ format.which = V4L2_SUBDEV_FORMAT_TRY;
+
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_state, &format);
+ if (ret) {
+ dev_err(viif_dev->dev, "subdev: set_fmt(TRY) error. %d\n", ret);
+ return ret;
+ }
+
+ viif_sd->mbus_code = *mbus_code;
+ viif_sd->bpp = viif_get_mbus_bpp(*mbus_code);
+ }
+ return ret;
+}
+
+static int
+viif_csi2rx_get_calibration_status(struct viif_device *viif_dev,
+ struct viif_csi2rx_dphy_calibration_status *calibration_status)
+{
+ int ret;
+
+ if (!vb2_is_streaming(&viif_dev->vb2_vq))
+ return -EIO;
+
+ ret = hwd_VIIF_csi2rx_get_calibration_status(
+ viif_dev->ch, (struct hwd_viif_csi2rx_dphy_calibration_status *)calibration_status);
+
+ return ret;
+}
+
+static int viif_csi2rx_get_err_status(struct viif_device *viif_dev,
+ struct viif_csi2rx_err_status *csi_err)
+{
+ int ret;
+
+ if (!vb2_is_streaming(&viif_dev->vb2_vq))
+ return -EIO;
+
+ ret = hwd_VIIF_csi2rx_get_err_status(viif_dev->ch, &csi_err->err_phy_fatal,
+ &csi_err->err_pkt_fatal, &csi_err->err_frame_fatal,
+ &csi_err->err_phy, &csi_err->err_pkt,
+ &csi_err->err_line);
+
+ return ret;
+}
+
+static int viif_isp_get_last_capture_status(struct viif_device *viif_dev,
+ struct viif_isp_capture_status *status)
+{
+ unsigned long irqflags;
+ struct hwd_viif_l1_info l1_info;
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ VIIF_ISP_GUARD_START(viif_dev);
+ hwd_VIIF_isp_get_info(viif_dev->ch, VIIF_ISP_REGBUF_0, NULL, &l1_info, NULL, NULL, NULL,
+ NULL);
+ status->l1_info.awb_ave_u = l1_info.awb_ave_u;
+ status->l1_info.awb_ave_v = l1_info.awb_ave_v;
+ status->l1_info.awb_accumulated_pixel = l1_info.awb_accumulated_pixel;
+ status->l1_info.awb_gain_r = l1_info.awb_gain_r;
+ status->l1_info.awb_gain_g = l1_info.awb_gain_g;
+ status->l1_info.awb_gain_b = l1_info.awb_gain_b;
+ status->l1_info.awb_status_u = l1_info.awb_status_u;
+ status->l1_info.awb_status_v = l1_info.awb_status_v;
+
+ VIIF_ISP_GUARD_END(viif_dev);
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+
+ return 0;
+}
+
+static long viif_ioctl_default(struct file *file, void *fh, bool valid_prio, unsigned int cmd,
+ void *arg)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ int ret;
+
+ switch (cmd) {
+ case VIDIOC_VIIF_MAIN_SET_RAWPACK_MODE:
+ ret = viif_main_set_rawpack_mode(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_L2_SET_UNDIST:
+ ret = viif_l2_set_undist(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_L2_SET_ROI:
+ ret = viif_l2_set_roi(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_L2_SET_GAMMA:
+ ret = viif_l2_set_gamma(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_L2_SET_CROP:
+ ret = viif_l2_set_crop(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_CSI2RX_SET_MBUS_FMT:
+ ret = viif_csi2rx_set_mbus_fmt(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_CSI2RX_GET_CALIBRATION_STATUS:
+ ret = viif_csi2rx_get_calibration_status(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_CSI2RX_GET_ERR_STATUS:
+ ret = viif_csi2rx_get_err_status(viif_dev, arg);
+ break;
+ case VIDIOC_VIIF_ISP_GET_LAST_CAPTURE_STATUS:
+ ret = viif_isp_get_last_capture_status(viif_dev, arg);
+ break;
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+ return ret;
+}
+
+/* ----- ISRs and VB2 Operations ----- */
+static int viif_set_img(struct viif_device *viif_dev, struct vb2_buffer *vb)
+{
+ struct v4l2_pix_format_mplane *pix = &viif_dev->v4l2_pix;
+ struct hwd_viif_img next_out_img;
+ dma_addr_t phys_addr;
+ int i, ret = 0;
+
+ next_out_img.width = pix->width;
+ next_out_img.height = pix->height;
+ next_out_img.format = viif_dev->out_format;
+
+ for (i = 0; i < pix->num_planes; i++) {
+ next_out_img.pixelmap[i].pitch = pix->plane_fmt[i].bytesperline;
+ phys_addr = vb2_dma_contig_plane_dma_addr(vb, i);
+ /* address mapping:
+ * - DDR0: (CPU)0x0_8000_0000-0x0_FFFF_FFFF -> (HW)0x8000_0000-0xFFFF_FFFF
+ * - DDR1: (CPU)0x8_8000_0000-0x8_FFFF_FFFF -> (HW)0x0000_0000-0x7FFF_FFFF
+ */
+ next_out_img.pixelmap[i].pmap_paddr = (phys_addr & 0x800000000UL) ?
+ (phys_addr & 0x7fffffff) :
+ (phys_addr & 0xffffffff);
+ }
+ VIIF_ISP_GUARD_START(viif_dev);
+ ret = hwd_VIIF_l2_set_img_transmission(viif_dev->ch, VIIF_L2ISP_POST_0, VIIF_ISP_REGBUF_0,
+ HWD_VIIF_ENABLE, &viif_dev->img_area,
+ &viif_dev->out_process, &next_out_img);
+ VIIF_ISP_GUARD_END(viif_dev);
+ if (ret)
+ dev_err(viif_dev->dev, "set img error. %d\n", ret);
+
+ return ret;
+}
+
+#define VIIF_SYNC_M_EVENT_DELAY2_SHIFT 2U
+#define MAIN_DELAY_INT_ERR_MASK 0x01000000U
+
+static void viif_vsync_irq_handler_w_isp(struct viif_device *viif_dev)
+{
+ uint32_t event_main, event_sub, mask, status_err, l2_transfer_status;
+ struct vb2_v4l2_buffer *vbuf;
+ struct viif_buffer *buf;
+ enum vb2_buffer_state state;
+
+ hwd_VIIF_vsync_irq_handler(viif_dev->ch, &event_main, &event_sub);
+
+ /* Delayed Vsync of MAIN unit */
+ if (((event_main >> VIIF_SYNC_M_EVENT_DELAY2_SHIFT) & 0x1U) == 0x1U) {
+ /* unmask timeout error of gamma table */
+ mask = MAIN_DELAY_INT_ERR_MASK;
+ hwd_VIIF_main_status_err_set_irq_mask(viif_dev->ch, &mask);
+ viif_dev->masked_gamma_path = 0;
+
+ /* Get abort status of L2ISP */
+ VIIF_ISP_GUARD_START(viif_dev);
+ hwd_VIIF_isp_get_info(viif_dev->ch, VIIF_ISP_REGBUF_0, NULL, NULL, NULL,
+ &l2_transfer_status, NULL, NULL);
+ VIIF_ISP_GUARD_END(viif_dev);
+
+ status_err = viif_dev->status_err;
+ viif_dev->status_err = 0;
+
+ vbuf = viif_dev->dma_active;
+ if (!vbuf)
+ goto next;
+
+ viif_dev->buf_cnt--;
+ vbuf->vb2_buf.timestamp = ktime_get_ns();
+ vbuf->sequence = viif_dev->sequence++;
+ vbuf->field = viif_dev->field;
+ if (status_err || l2_transfer_status)
+ state = VB2_BUF_STATE_ERROR;
+ else
+ state = VB2_BUF_STATE_DONE;
+
+ vb2_buffer_done(&vbuf->vb2_buf, state);
+ viif_dev->dma_active = NULL;
+
+ next:
+ vbuf = viif_dev->active;
+ if (!vbuf)
+ return;
+
+ if (viif_dev->last_active) {
+ viif_dev->dma_active = viif_dev->last_active;
+ viif_dev->last_active = NULL;
+ } else if (!viif_dev->dma_active) {
+ viif_dev->dma_active = vbuf;
+ buf = vb2_to_viif(vbuf);
+ list_del_init(&buf->queue);
+ }
+
+ if (!list_empty(&viif_dev->capture)) {
+ buf = list_entry(viif_dev->capture.next, struct viif_buffer, queue);
+ viif_dev->active = &buf->vb;
+ viif_set_img(viif_dev, &buf->vb.vb2_buf);
+ } else {
+ dev_dbg(viif_dev->dev, "no queue\n");
+ viif_dev->last_active = viif_dev->dma_active;
+ viif_dev->dma_active = NULL;
+ viif_dev->active = NULL;
+ }
+ }
+}
+
+#define VIIF_ERR_M_EVENT_GAMMATBL_SHIFT 8U
+#define VIIF_ERR_M_EVENT_GAMMATBL_MASK 0x7U
+
+static void viif_status_err_irq_handler(struct viif_device *viif_dev)
+{
+ uint32_t event_main, event_sub, val, mask;
+
+ hwd_VIIF_status_err_irq_handler(viif_dev->ch, &event_main, &event_sub);
+
+ if (event_main != 0U) {
+ /* mask for gamma table time out error which will be unmasked in the next Vsync */
+ val = (event_main >> VIIF_ERR_M_EVENT_GAMMATBL_SHIFT) &
+ VIIF_ERR_M_EVENT_GAMMATBL_MASK;
+ if (val != 0U) {
+ viif_dev->masked_gamma_path |= val;
+ mask = MAIN_DELAY_INT_ERR_MASK |
+ (viif_dev->masked_gamma_path << VIIF_ERR_M_EVENT_GAMMATBL_SHIFT);
+ hwd_VIIF_main_status_err_set_irq_mask(viif_dev->ch, &mask);
+ }
+
+ viif_dev->status_err = event_main;
+ }
+ dev_err(viif_dev->dev, "Status error 0x%x.\n", event_main);
+}
+
+static void viif_csi2rx_err_irq_handler(struct viif_device *viif_dev)
+{
+ uint32_t event;
+
+ event = hwd_VIIF_csi2rx_err_irq_handler(viif_dev->ch);
+ dev_err(viif_dev->dev, "CSI2RX error 0x%x.\n", event);
+}
+
+static irqreturn_t visconti_viif_irq(int irq, void *dev_id)
+{
+ struct viif_device *viif_dev = dev_id;
+ int irq_type = irq - viif_dev->irq[0];
+
+ spin_lock(&viif_dev->lock);
+
+ switch (irq_type) {
+ case 0:
+ viif_vsync_irq_handler_w_isp(viif_dev);
+ break;
+ case 1:
+ viif_status_err_irq_handler(viif_dev);
+ break;
+ case 2:
+ viif_csi2rx_err_irq_handler(viif_dev);
+ break;
+ }
+
+ spin_unlock(&viif_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int viif_vb2_setup(struct vb2_queue *vq, unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct viif_device *viif_dev = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format_mplane *pix = &viif_dev->v4l2_pix;
+ unsigned int i;
+
+ /* num_planes is set: just check plane sizes. */
+ if (*num_planes) {
+ for (i = 0; i < pix->num_planes; i++)
+ if (sizes[i] < pix->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ /* num_planes not set: called from REQBUFS, just set plane sizes. */
+ *num_planes = pix->num_planes;
+ for (i = 0; i < pix->num_planes; i++)
+ sizes[i] = pix->plane_fmt[i].sizeimage;
+
+ viif_dev->buf_cnt = 0;
+
+ return 0;
+}
+
+static void viif_vb2_queue(struct vb2_buffer *vb)
+{
+ struct viif_device *viif_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct viif_buffer *buf = vb2_to_viif(vbuf);
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ list_add_tail(&buf->queue, &viif_dev->capture);
+ viif_dev->buf_cnt++;
+
+ if (!viif_dev->active) {
+ viif_dev->active = vbuf;
+ if (!viif_dev->last_active)
+ viif_set_img(viif_dev, vb);
+ }
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+}
+
+static int viif_vb2_prepare(struct vb2_buffer *vb)
+{
+ struct viif_device *viif_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct v4l2_pix_format_mplane *pix = &viif_dev->v4l2_pix;
+ unsigned int i;
+
+ for (i = 0; i < pix->num_planes; i++) {
+ if (vb2_plane_size(vb, i) < pix->plane_fmt[i].sizeimage) {
+ dev_err(viif_dev->dev, "Plane size too small (%lu < %u)\n",
+ vb2_plane_size(vb, i), pix->plane_fmt[i].sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, pix->plane_fmt[i].sizeimage);
+ }
+ return 0;
+}
+
+static int viif_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct viif_device *viif_dev = vb2_get_drv_priv(vq);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ int ret;
+ unsigned long irqflags;
+
+ /* CSI2RX Init */
+ ret = viif_csi2rx_initialize(viif_dev);
+ if (ret)
+ return ret;
+
+ /* CSI2RX start streaming */
+ ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, 1);
+ if (ret) {
+ dev_err(viif_dev->dev, "Start subdev stream failed. %d\n", ret);
+ goto err;
+ }
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+
+ viif_csi2rx_start(viif_dev);
+ viif_dev->sequence = 0;
+
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+
+ return ret;
+err:
+ hwd_VIIF_csi2rx_uninitialize(viif_dev->ch);
+ return ret;
+}
+
+static void viif_stop_streaming(struct vb2_queue *vq)
+{
+ struct viif_device *viif_dev = vb2_get_drv_priv(vq);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct viif_buffer *buf;
+ unsigned long irqflags;
+ int ret;
+
+ spin_lock_irqsave(&viif_dev->lock, irqflags);
+ (void)viif_csi2rx_stop(viif_dev);
+
+ viif_dev->active = NULL;
+ if (viif_dev->dma_active) {
+ vb2_buffer_done(&viif_dev->dma_active->vb2_buf, VB2_BUF_STATE_ERROR);
+ viif_dev->buf_cnt--;
+ viif_dev->dma_active = NULL;
+ }
+ if (viif_dev->last_active) {
+ vb2_buffer_done(&viif_dev->last_active->vb2_buf, VB2_BUF_STATE_ERROR);
+ viif_dev->buf_cnt--;
+ viif_dev->last_active = NULL;
+ }
+
+ /* Release all queued buffers. */
+ list_for_each_entry (buf, &viif_dev->capture, queue) {
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ viif_dev->buf_cnt--;
+ }
+ INIT_LIST_HEAD(&viif_dev->capture);
+ if (viif_dev->buf_cnt)
+ dev_err(viif_dev->dev, "Buffer count error %d\n", viif_dev->buf_cnt);
+
+ spin_unlock_irqrestore(&viif_dev->lock, irqflags);
+
+ ret = v4l2_subdev_call(viif_sd->v4l2_sd, video, s_stream, 0);
+ if (ret)
+ dev_err(viif_dev->dev, "Stop subdev stream failed. %d\n", ret);
+}
+
+static const struct vb2_ops viif_vb2_ops = {
+ .queue_setup = viif_vb2_setup,
+ .buf_queue = viif_vb2_queue,
+ .buf_prepare = viif_vb2_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = viif_start_streaming,
+ .stop_streaming = viif_stop_streaming,
+};
+
+/* --- Video Device IOCTLs --- */
+static const struct viif_fmt viif_fmt_list[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .bpp = { 24, 0, 0 },
+ .num_planes = 1,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .pitch_align = 384,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .bpp = { 32, 0, 0 },
+ .num_planes = 1,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .pitch_align = 512,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422M,
+ .bpp = { 8, 4, 4 },
+ .num_planes = 3,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ .pitch_align = 128,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV444M,
+ .bpp = { 8, 8, 8 },
+ .num_planes = 3,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ .pitch_align = 128,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .bpp = { 16, 0, 0 },
+ .num_planes = 1,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ .pitch_align = 128,
+ },
+};
+
+static const struct viif_fmt *get_viif_fmt_from_fourcc(unsigned int fourcc)
+{
+ const struct viif_fmt *fmt = &viif_fmt_list[0];
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(viif_fmt_list); i++, fmt++)
+ if (fmt->fourcc == fourcc)
+ return fmt;
+
+ return NULL;
+}
+
+static void viif_update_plane_sizes(struct v4l2_plane_pix_format *plane, unsigned int bpl,
+ unsigned int szimage)
+{
+ memset(plane, 0, sizeof(*plane));
+
+ plane->sizeimage = szimage;
+ plane->bytesperline = bpl;
+}
+
+static void viif_calc_plane_sizes(const struct viif_fmt *viif_fmt,
+ struct v4l2_pix_format_mplane *pix)
+{
+ unsigned int i, bpl, szimage;
+
+ for (i = 0; i < viif_fmt->num_planes; i++) {
+ bpl = pix->width * viif_fmt->bpp[i] / 8;
+ /* round up ptch */
+ bpl = (bpl + (viif_fmt->pitch_align - 1)) / viif_fmt->pitch_align;
+ bpl *= viif_fmt->pitch_align;
+ szimage = pix->height * bpl;
+ viif_update_plane_sizes(&pix->plane_fmt[i], bpl, szimage);
+ }
+ pix->num_planes = viif_fmt->num_planes;
+}
+
+int viif_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ strscpy(cap->card, "Toshiba VIIF", sizeof(cap->card));
+ strscpy(cap->driver, DRIVER_NAME, sizeof(cap->driver));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:toshiba-viif-%s",
+ dev_name(viif_dev->dev));
+ return 0;
+}
+
+static int viif_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+ const struct viif_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(viif_fmt_list))
+ return -EINVAL;
+
+ fmt = &viif_fmt_list[f->index];
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int viif_try_fmt(struct viif_device *viif_dev, struct v4l2_format *v4l2_fmt)
+{
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_pix_format_mplane *pix = &v4l2_fmt->fmt.pix_mp;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ struct v4l2_subdev_pad_config pad_cfg;
+ struct v4l2_subdev_state pad_state = {
+ .pads = &pad_cfg,
+ };
+ const struct viif_fmt *viif_fmt;
+ int ret;
+
+ struct v4l2_subdev_format sd_format = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ };
+
+ viif_fmt = get_viif_fmt_from_fourcc(pix->pixelformat);
+ if (!viif_fmt)
+ return -EINVAL;
+
+ if (viif_dev->l2_crop_set_flag) {
+ if ((pix->width != viif_dev->l2_crop_param.w) ||
+ (pix->height != viif_dev->l2_crop_param.h))
+ return -EINVAL;
+ } else {
+ sd_format.format.code = viif_sd->mbus_code;
+ v4l2_fill_mbus_format_mplane(&sd_format.format, pix);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_state, &sd_format);
+ if (ret) {
+ dev_err(viif_dev->dev, "subdev: set_fmt(TRY) error. %d\n", ret);
+ return ret;
+ }
+ v4l2_fill_pix_format_mplane(pix, &sd_format.format);
+ }
+
+ viif_calc_plane_sizes(viif_fmt, pix);
+
+ return 0;
+}
+
+static int viif_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ return viif_try_fmt(viif_dev, f);
+}
+
+static int viif_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ int ret = 0;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+
+ if (vb2_is_streaming(&viif_dev->vb2_vq))
+ return -EBUSY;
+
+ if (f->type != viif_dev->vb2_vq.type)
+ return -EINVAL;
+
+ ret = viif_try_fmt(viif_dev, f);
+ if (ret)
+ return ret;
+
+ if (!viif_dev->l2_crop_set_flag) {
+ format.format.code = viif_sd->mbus_code;
+ v4l2_fill_mbus_format_mplane(&format.format, &f->fmt.pix_mp);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format);
+ if (ret) {
+ dev_err(viif_dev->dev, "subdev: set_fmt(ACTIVE) error. %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = viif_main_set_unit(viif_dev);
+ if (ret)
+ return ret;
+
+ viif_dev->v4l2_pix = f->fmt.pix_mp;
+ viif_dev->field = V4L2_FIELD_NONE;
+
+ return viif_l2_set_format(viif_dev);
+}
+
+static int viif_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ f->fmt.pix_mp = viif_dev->v4l2_pix;
+
+ return 0;
+}
+
+static int viif_enum_input(struct file *file, void *priv, struct v4l2_input *inp)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd;
+ struct v4l2_subdev *v4l2_sd;
+ int ret;
+
+ if (inp->index >= viif_dev->num_sd)
+ return -EINVAL;
+
+ viif_sd = &viif_dev->subdevs[inp->index];
+ v4l2_sd = viif_sd->v4l2_sd;
+
+ ret = v4l2_subdev_call(v4l2_sd, video, g_input_status, &inp->status);
+ if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ return ret;
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = 0;
+ if (v4l2_subdev_has_op(v4l2_sd, pad, dv_timings_cap))
+ inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ else
+ inp->capabilities = V4L2_IN_CAP_STD;
+ snprintf(inp->name, sizeof(inp->name), "Camera%u: %s", inp->index, viif_sd->v4l2_sd->name);
+
+ return 0;
+}
+
+static int viif_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ *i = viif_dev->sd_index;
+
+ return 0;
+}
+
+static int viif_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ if (i >= viif_dev->num_sd)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int viif_dv_timings_cap(struct file *file, void *priv_fh, struct v4l2_dv_timings_cap *cap)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, pad, dv_timings_cap, cap);
+}
+
+static int viif_enum_dv_timings(struct file *file, void *priv_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, pad, enum_dv_timings, timings);
+}
+
+static int viif_g_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, video, g_dv_timings, timings);
+}
+
+static int viif_s_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, video, s_dv_timings, timings);
+}
+
+static int viif_query_dv_timings(struct file *file, void *priv_fh, struct v4l2_dv_timings *timings)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, video, query_dv_timings, timings);
+}
+
+static int viif_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, pad, get_edid, edid);
+}
+
+static int viif_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+
+ return v4l2_subdev_call(viif_sd->v4l2_sd, pad, set_edid, edid);
+}
+
+static int viif_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ return v4l2_g_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a);
+}
+
+static int viif_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+
+ return v4l2_s_parm_cap(video_devdata(file), viif_dev->sd->v4l2_sd, a);
+}
+
+static int viif_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ struct v4l2_subdev_frame_size_enum fse = {
+ .code = viif_sd->mbus_code,
+ .index = fsize->index,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_size, NULL, &fse);
+ if (ret)
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = fse.max_width;
+ fsize->discrete.height = fse.max_height;
+
+ return 0;
+}
+
+static int viif_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ struct viif_subdev *viif_sd = viif_dev->sd;
+ struct v4l2_subdev *v4l2_sd = viif_sd->v4l2_sd;
+ struct v4l2_subdev_frame_interval_enum fie = {
+ .code = viif_sd->mbus_code,
+ .index = fival->index,
+ .width = fival->width,
+ .height = fival->height,
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call(v4l2_sd, pad, enum_frame_interval, NULL, &fie);
+ if (ret)
+ return ret;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = fie.interval;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops viif_ioctl_ops = {
+ .vidioc_querycap = viif_querycap,
+
+ .vidioc_enum_fmt_vid_cap = viif_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap_mplane = viif_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap_mplane = viif_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap_mplane = viif_g_fmt_vid_cap,
+
+ .vidioc_enum_input = viif_enum_input,
+ .vidioc_g_input = viif_g_input,
+ .vidioc_s_input = viif_s_input,
+
+ .vidioc_dv_timings_cap = viif_dv_timings_cap,
+ .vidioc_enum_dv_timings = viif_enum_dv_timings,
+ .vidioc_g_dv_timings = viif_g_dv_timings,
+ .vidioc_s_dv_timings = viif_s_dv_timings,
+ .vidioc_query_dv_timings = viif_query_dv_timings,
+
+ .vidioc_g_edid = viif_g_edid,
+ .vidioc_s_edid = viif_s_edid,
+
+ .vidioc_g_parm = viif_g_parm,
+ .vidioc_s_parm = viif_s_parm,
+
+ .vidioc_enum_framesizes = viif_enum_framesizes,
+ .vidioc_enum_frameintervals = viif_enum_frameintervals,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_default = viif_ioctl_default,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/* --- File Operations --- */
+static int viif_open(struct file *file)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ int ret, mask;
+
+ ret = v4l2_fh_open(file);
+ if (ret)
+ return ret;
+
+ viif_dev->rawpack_mode = HWD_VIIF_RAWPACK_DISABLE;
+ viif_dev->l2_crop_set_flag = false;
+ memset(&viif_dev->l2_crop_param, 0, sizeof(struct viif_l2_crop_config));
+
+ mutex_lock(&viif_dev->mlock);
+
+ /* Initialize HWD driver */
+ viif_hw_on(viif_dev);
+
+ /* VSYNC mask setting of MAIN unit */
+ mask = 0x00050003;
+ hwd_VIIF_main_vsync_set_irq_mask(viif_dev->ch, &mask);
+
+ /* STATUS error mask setting(unmask) of MAIN unit */
+ mask = 0x01000000;
+ hwd_VIIF_main_status_err_set_irq_mask(viif_dev->ch, &mask);
+
+ mutex_unlock(&viif_dev->mlock);
+
+ return ret;
+}
+
+static int viif_release(struct file *file)
+{
+ struct viif_device *viif_dev = video_drvdata(file);
+ int ret;
+
+ ret = vb2_fop_release(file);
+ if (ret)
+ return ret;
+
+ mutex_lock(&viif_dev->mlock);
+ viif_hw_off(viif_dev);
+ mutex_unlock(&viif_dev->mlock);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations viif_fops = {
+ .owner = THIS_MODULE,
+ .open = viif_open,
+ .release = viif_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+/* ----- Async Notifier Operations----- */
+static int visconti_viif_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *v4l2_sd, struct v4l2_async_subdev *asd)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev);
+ struct viif_subdev *viif_sd = to_viif_subdev(asd);
+
+ viif_sd->v4l2_sd = v4l2_sd;
+ viif_dev->num_sd++;
+
+ return 0;
+}
+
+static int visconti_viif_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct v4l2_device *v4l2_dev = notifier->v4l2_dev;
+ struct viif_device *viif_dev = v4l2_to_viif(v4l2_dev);
+ struct video_device *vdev = &viif_dev->vdev;
+ struct vb2_queue *q = &viif_dev->vb2_vq;
+ struct v4l2_subdev *v4l2_sd;
+ int ret;
+
+ ret = v4l2_device_register_subdev_nodes(v4l2_dev);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "Failed to register subdev nodes\n");
+ return ret;
+ }
+
+ /* Initialize vb2 queue. */
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ q->io_modes = VB2_DMABUF;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->ops = &viif_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->drv_priv = viif_dev;
+ q->buf_struct_size = sizeof(struct viif_buffer);
+ q->min_buffers_needed = 2;
+ q->lock = &viif_dev->mlock;
+ q->dev = viif_dev->v4l2_dev.dev;
+
+ ret = vb2_queue_init(q);
+ if (ret)
+ return ret;
+
+ /* Make sure at least one sensor is primary and use it to initialize */
+ if (!viif_dev->sd) {
+ viif_dev->sd = &viif_dev->subdevs[0];
+ viif_dev->sd_index = 0;
+ }
+
+ v4l2_sd = viif_dev->sd->v4l2_sd;
+
+ ret = viif_init_mbus_code(viif_dev);
+ if (ret)
+ return ret;
+
+ /* Register the video device. */
+ strscpy(vdev->name, DRIVER_NAME, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &viif_dev->mlock;
+ vdev->queue = &viif_dev->vb2_vq;
+ vdev->ctrl_handler = v4l2_sd->ctrl_handler;
+ vdev->fops = &viif_fops;
+ vdev->ioctl_ops = &viif_ioctl_ops;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING;
+ vdev->release = video_device_release_empty;
+ video_set_drvdata(vdev, viif_dev);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ dev_err(v4l2_dev->dev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations viif_notify_ops = {
+ .bound = visconti_viif_notify_bound,
+ .complete = visconti_viif_notify_complete,
+};
+
+/* ----- Probe and Remove ----- */
+static int visconti_viif_init_async_subdevs(struct viif_device *viif_dev, unsigned int n_sd)
+{
+ /* Reserve memory for 'n_sd' viif_subdev descriptors. */
+ viif_dev->subdevs =
+ devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->subdevs), GFP_KERNEL);
+ if (!viif_dev->subdevs)
+ return -ENOMEM;
+
+ /* Reserve memory for 'n_sd' pointers to async_subdevices.
+ * viif_dev->asds members will point to &viif_dev.asd
+ */
+ viif_dev->asds = devm_kcalloc(viif_dev->dev, n_sd, sizeof(*viif_dev->asds), GFP_KERNEL);
+ if (!viif_dev->asds)
+ return -ENOMEM;
+
+ viif_dev->sd = NULL;
+ viif_dev->sd_index = 0;
+ viif_dev->num_sd = 0;
+
+ return 0;
+}
+
+static int visconti_viif_parse_dt(struct viif_device *viif_dev)
+{
+ struct device_node *of = viif_dev->dev->of_node;
+ struct v4l2_fwnode_endpoint fw_ep;
+ struct viif_subdev *viif_sd;
+ struct device_node *ep;
+ unsigned int i;
+ int num_ep;
+ int ret;
+
+ memset(&fw_ep, 0, sizeof(struct v4l2_fwnode_endpoint));
+
+ num_ep = of_graph_get_endpoint_count(of);
+ if (!num_ep)
+ return -ENODEV;
+
+ ret = visconti_viif_init_async_subdevs(viif_dev, num_ep);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_ep; i++) {
+ ep = of_graph_get_endpoint_by_regs(of, 0, i);
+ if (!ep) {
+ dev_err(viif_dev->dev, "No subdevice connected on endpoint %u.\n", i);
+ ret = -ENODEV;
+ goto error_put_node;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &fw_ep);
+ if (ret) {
+ dev_err(viif_dev->dev, "Unable to parse endpoint #%u.\n", i);
+ goto error_put_node;
+ }
+
+ if (fw_ep.bus_type != V4L2_MBUS_CSI2_DPHY ||
+ fw_ep.bus.mipi_csi2.num_data_lanes == 0) {
+ dev_err(viif_dev->dev, "missing CSI-2 properties in endpoint\n");
+ ret = -EINVAL;
+ goto error_put_node;
+ }
+
+ /* Setup the ceu subdevice and the async subdevice. */
+ viif_sd = &viif_dev->subdevs[i];
+ INIT_LIST_HEAD(&viif_sd->asd.list);
+
+ viif_sd->mbus_flags = fw_ep.bus.mipi_csi2.flags;
+ viif_sd->num_lane = fw_ep.bus.mipi_csi2.num_data_lanes;
+ viif_sd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
+ viif_sd->asd.match.fwnode =
+ fwnode_graph_get_remote_port_parent(of_fwnode_handle(ep));
+
+ viif_dev->asds[i] = &viif_sd->asd;
+ of_node_put(ep);
+ }
+
+ return num_ep;
+
+error_put_node:
+ of_node_put(ep);
+ return ret;
+}
+
+static const struct of_device_id visconti_viif_of_table[] = {
+ {
+ .compatible = "toshiba,visconti-viif",
+ .data = (void *)VIIF_DEV_CSI,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, visconti_viif_of_table);
+
+static int visconti_viif_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct viif_device *viif_dev;
+ int ret, i, num_sd;
+ dma_addr_t table_paddr;
+ const struct of_device_id *of_id;
+
+ //ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
+ //if (ret)
+ // return ret;
+
+ viif_dev = devm_kzalloc(dev, sizeof(*viif_dev), GFP_KERNEL);
+ if (!viif_dev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, viif_dev);
+ viif_dev->dev = dev;
+
+ INIT_LIST_HEAD(&viif_dev->capture);
+ spin_lock_init(&viif_dev->lock);
+ mutex_init(&viif_dev->mlock);
+
+ viif_dev->capture_reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(viif_dev->capture_reg))
+ return PTR_ERR(viif_dev->capture_reg);
+
+ viif_dev->csi2host_reg = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(viif_dev->csi2host_reg))
+ return PTR_ERR(viif_dev->csi2host_reg);
+
+ device_property_read_u32(dev, "index", &viif_dev->ch);
+
+ for (i = 0; i < 3; i++) {
+ viif_dev->irq[i] = ret = platform_get_irq(pdev, i);
+ if (ret < 0) {
+ dev_err(dev, "failed to acquire irq resource\n");
+ return ret;
+ }
+ ret = devm_request_irq(dev, viif_dev->irq[i], visconti_viif_irq, 0, "viif",
+ viif_dev);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ return ret;
+ }
+ }
+
+ viif_dev->table_vaddr =
+ dma_alloc_wc(dev, sizeof(struct viif_table_area), &table_paddr, GFP_KERNEL);
+ if (!viif_dev->table_vaddr) {
+ dev_err(dev, "dma_alloc_wc failed\n");
+ return -ENOMEM;
+ }
+ viif_dev->table_paddr = (struct viif_table_area *)table_paddr;
+
+ ret = v4l2_device_register(dev, &viif_dev->v4l2_dev);
+ if (ret)
+ goto error_dma_free;
+
+ /* check device type */
+ of_id = of_match_device(visconti_viif_of_table, dev);
+ viif_dev->dev_type = (enum viif_dev_type)of_id->data;
+
+ num_sd = visconti_viif_parse_dt(viif_dev);
+ if (ret < 0) {
+ ret = num_sd;
+ goto error_v4l2_unregister;
+ }
+
+ viif_dev->notifier.v4l2_dev = &viif_dev->v4l2_dev;
+ v4l2_async_nf_init(&viif_dev->notifier);
+ for (i = 0; i < num_sd; i++) {
+ __v4l2_async_nf_add_subdev(&viif_dev->notifier, viif_dev->asds[i]);
+ }
+ //viif_dev->notifier.subdevs = viif_dev->asds;
+ //viif_dev->notifier.num_subdevs = num_sd;
+ viif_dev->notifier.ops = &viif_notify_ops;
+ ret = v4l2_async_nf_register(&viif_dev->v4l2_dev, &viif_dev->notifier);
+ if (ret)
+ goto error_v4l2_unregister;
+
+ return 0;
+
+error_v4l2_unregister:
+ v4l2_device_unregister(&viif_dev->v4l2_dev);
+error_dma_free:
+ dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr,
+ (dma_addr_t)viif_dev->table_paddr);
+ return ret;
+}
+
+static int visconti_viif_remove(struct platform_device *pdev)
+{
+ struct viif_device *viif_dev = platform_get_drvdata(pdev);
+
+ v4l2_async_nf_unregister(&viif_dev->notifier);
+ v4l2_device_unregister(&viif_dev->v4l2_dev);
+ video_unregister_device(&viif_dev->vdev);
+ dma_free_wc(&pdev->dev, sizeof(struct viif_table_area), viif_dev->table_vaddr,
+ (dma_addr_t)viif_dev->table_paddr);
+
+ return 0;
+}
+
+static struct platform_driver visconti_viif_driver = {
+ .probe = visconti_viif_probe,
+ .remove = visconti_viif_remove,
+ .driver = {
+ .name = "visconti_viif",
+ .of_match_table = visconti_viif_of_table,
+ },
+};
+
+module_platform_driver(visconti_viif_driver);
+
+MODULE_AUTHOR("Yuji Ishikawa <yuji2.ishikawa@toshiba.co.jp>");
+MODULE_DESCRIPTION("Toshiba Visconti Video Input driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
2.17.1

\
 
 \ /
  Last update: 2022-04-13 11:47    [W:0.214 / U:0.560 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site