* [PATCH v4 05/16] media: rkisp1: add Rockchip ISP1 subdev driver
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 06/16] media: rkisp1: add ISP1 statistics driver Jacob Chen
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the subdev driver for rockchip isp1.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/rkisp1.c | 1211 +++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/rkisp1.h | 132 +++
2 files changed, 1343 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.c
create mode 100644 drivers/media/platform/rockchip/isp1/rkisp1.h
diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.c b/drivers/media/platform/rockchip/isp1/rkisp1.c
new file mode 100644
index 000000000000..abe62feeee30
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
@@ -0,0 +1,1211 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-event.h>
+
+#include "common.h"
+#include "regs.h"
+
+#define CIF_ISP_INPUT_W_MAX 4032
+#define CIF_ISP_INPUT_H_MAX 3024
+#define CIF_ISP_INPUT_W_MIN 32
+#define CIF_ISP_INPUT_H_MIN 32
+#define CIF_ISP_OUTPUT_W_MAX CIF_ISP_INPUT_W_MAX
+#define CIF_ISP_OUTPUT_H_MAX CIF_ISP_INPUT_H_MAX
+#define CIF_ISP_OUTPUT_W_MIN CIF_ISP_INPUT_W_MIN
+#define CIF_ISP_OUTPUT_H_MIN CIF_ISP_INPUT_H_MIN
+
+/*
+ * NOTE: MIPI controller and input MUX are also configured in this file,
+ * because ISP Subdev is not only describe ISP submodule(input size,format, output size, format),
+ * but also a virtual route device.
+ */
+
+/*
+ * There are many variables named with format/frame in below code,
+ * please see here for their meaning.
+ *
+ * Cropping regions of ISP
+ *
+ * +---------------------------------------------------------+
+ * | Sensor image |
+ * | +---------------------------------------------------+ |
+ * | | ISP_ACQ (for black level) | |
+ * | | in_frm | |
+ * | | +--------------------------------------------+ | |
+ * | | | ISP_OUT | | |
+ * | | | in_crop | | |
+ * | | | +---------------------------------+ | | |
+ * | | | | ISP_IS | | | |
+ * | | | | rkisp1_isp_subdev: out_crop | | | |
+ * | | | +---------------------------------+ | | |
+ * | | +--------------------------------------------+ | |
+ * | +---------------------------------------------------+ |
+ * +---------------------------------------------------------+
+ */
+
+static inline struct rkisp1_device *sd_to_isp_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd->v4l2_dev, struct rkisp1_device, v4l2_dev);
+}
+
+/* Get sensor by enabled media link */
+static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
+{
+ struct media_pad *local;
+ struct media_entity *sensor_me;
+
+ local = &sd->entity.pads[RKISP1_ISP_PAD_SINK];
+ sensor_me = media_entity_remote_pad(local)->entity;
+
+ return media_entity_to_v4l2_subdev(sensor_me);
+}
+
+static struct rkisp1_sensor_info *sd_to_sensor(struct rkisp1_device *dev,
+ struct v4l2_subdev *sd)
+{
+ int i;
+
+ for (i = 0; i < dev->num_sensors; ++i)
+ if (dev->sensors[i].sd == sd)
+ return &dev->sensors[i];
+
+ return NULL;
+}
+
+/**************** register operations ****************/
+
+/*
+ * Image Stabilization.
+ * This should only be called when configuring CIF
+ * or at the frame end interrupt
+ */
+static void rkisp1_config_ism(struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
+ u32 val;
+
+ writel(0, base + CIF_ISP_IS_RECENTER);
+ writel(0, base + CIF_ISP_IS_MAX_DX);
+ writel(0, base + CIF_ISP_IS_MAX_DY);
+ writel(0, base + CIF_ISP_IS_DISPLACE);
+ writel(out_crop->left, base + CIF_ISP_IS_H_OFFS);
+ writel(out_crop->top, base + CIF_ISP_IS_V_OFFS);
+ writel(out_crop->width, base + CIF_ISP_IS_H_SIZE);
+ writel(out_crop->height, base + CIF_ISP_IS_V_SIZE);
+
+ /* IS(Image Stabilization) is always on, working as output crop */
+ writel(1, base + CIF_ISP_IS_CTRL);
+ val = readl(base + CIF_ISP_CTRL);
+ val |= CIF_ISP_CTRL_ISP_CFG_UPD;
+ writel(val, base + CIF_ISP_CTRL);
+}
+
+/*
+ * configure isp blocks with input format, size......
+ */
+static int rkisp1_config_isp(struct rkisp1_device *dev)
+{
+ struct ispsd_in_fmt *in_fmt;
+ struct ispsd_out_fmt *out_fmt;
+ struct v4l2_mbus_framefmt *in_frm;
+ struct v4l2_rect *out_crop, *in_crop;
+ struct rkisp1_sensor_info *sensor;
+ void __iomem *base = dev->base_addr;
+ u32 isp_ctrl = 0;
+ u32 irq_mask = 0;
+ u32 signal = 0;
+ u32 acq_mult = 0;
+ u32 val;
+
+ sensor = dev->active_sensor;
+ in_frm = &dev->isp_sdev.in_frm;
+ in_fmt = &dev->isp_sdev.in_fmt;
+ out_fmt = &dev->isp_sdev.out_fmt;
+ out_crop = &dev->isp_sdev.out_crop;
+ in_crop = &dev->isp_sdev.in_crop;
+ val = readl(base + CIF_ICCL);
+ writel(val | CIF_ICCL_ISP_CLK, base + CIF_ICCL);
+
+ if (in_fmt->fmt_type == FMT_BAYER) {
+ acq_mult = 1;
+ if (out_fmt->fmt_type == FMT_BAYER) {
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl =
+ CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656;
+ else
+ isp_ctrl =
+ CIF_ISP_CTRL_ISP_MODE_RAW_PICT;
+ } else {
+ writel(CIF_ISP_DEMOSAIC_TH(0xc),
+ base + CIF_ISP_DEMOSAIC);
+
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656;
+ else
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601;
+ }
+ } else if (in_fmt->fmt_type == FMT_YUV) {
+ acq_mult = 2;
+ if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
+ } else {
+ if (sensor->mbus.type == V4L2_MBUS_BT656)
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU656;
+ else
+ isp_ctrl = CIF_ISP_CTRL_ISP_MODE_ITU601;
+
+ }
+
+ irq_mask |= CIF_ISP_DATA_LOSS;
+ }
+
+ /* Set up input acquisition properties */
+ if (sensor->mbus.type == V4L2_MBUS_BT656 ||
+ sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ if (sensor->mbus.flags &
+ V4L2_MBUS_PCLK_SAMPLE_RISING)
+ signal = CIF_ISP_ACQ_PROP_POS_EDGE;
+ }
+
+ if (sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ if (sensor->mbus.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ signal |= CIF_ISP_ACQ_PROP_VSYNC_LOW;
+
+ if (sensor->mbus.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ signal |= CIF_ISP_ACQ_PROP_HSYNC_LOW;
+ }
+
+ writel(isp_ctrl, base + CIF_ISP_CTRL);
+ writel(signal | in_fmt->yuv_seq |
+ CIF_ISP_ACQ_PROP_BAYER_PAT(in_fmt->bayer_pat) |
+ CIF_ISP_ACQ_PROP_FIELD_SEL_ALL, base + CIF_ISP_ACQ_PROP);
+ writel(0, base + CIF_ISP_ACQ_NR_FRAMES);
+
+ /* Acquisition Size */
+ writel(0, base + CIF_ISP_ACQ_H_OFFS);
+ writel(0, base + CIF_ISP_ACQ_V_OFFS);
+ writel(acq_mult * in_frm->width, base + CIF_ISP_ACQ_H_SIZE);
+ writel(in_frm->height, base + CIF_ISP_ACQ_V_SIZE);
+
+ /* ISP Out Area */
+ writel(in_crop->left, base + CIF_ISP_OUT_H_OFFS);
+ writel(in_crop->top, base + CIF_ISP_OUT_V_OFFS);
+ writel(in_crop->width, base + CIF_ISP_OUT_H_SIZE);
+ writel(in_crop->height, base + CIF_ISP_OUT_V_SIZE);
+
+ /* interrupt mask */
+ irq_mask |= CIF_ISP_FRAME | CIF_ISP_V_START | CIF_ISP_PIC_SIZE_ERROR |
+ CIF_ISP_FRAME_IN;
+ writel(irq_mask, base + CIF_ISP_IMSC);
+
+ if (out_fmt->fmt_type == FMT_BAYER)
+ rkisp1_params_disable_isp(&dev->params_vdev);
+ else
+ rkisp1_params_configure_isp(&dev->params_vdev, in_fmt,
+ dev->isp_sdev.quantization);
+
+ return 0;
+}
+
+static int rkisp1_config_dvp(struct rkisp1_device *dev)
+{
+ struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
+ void __iomem *base = dev->base_addr;
+ u32 val, input_sel;
+
+ switch (in_fmt->bus_width) {
+ case 8:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO;
+ break;
+ case 10:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO;
+ break;
+ case 12:
+ input_sel = CIF_ISP_ACQ_PROP_IN_SEL_12B;
+ break;
+ default:
+ v4l2_err(&dev->v4l2_dev, "Invalid bus width\n");
+ return -EINVAL;
+ }
+
+ val = readl(base + CIF_ISP_ACQ_PROP);
+ writel(val | input_sel, base + CIF_ISP_ACQ_PROP);
+
+ return 0;
+}
+
+static int rkisp1_config_mipi(struct rkisp1_device *dev)
+{
+ u32 mipi_ctrl, val;
+ void __iomem *base = dev->base_addr;
+ struct ispsd_in_fmt *in_fmt = &dev->isp_sdev.in_fmt;
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ int lanes;
+
+ /*
+ * sensor->mbus is set in isp or d-phy notifier_bound function
+ */
+ switch (sensor->mbus.flags & V4L2_MBUS_CSI2_LANES) {
+ case V4L2_MBUS_CSI2_4_LANE:
+ lanes = 4;
+ break;
+ case V4L2_MBUS_CSI2_3_LANE:
+ lanes = 3;
+ break;
+ case V4L2_MBUS_CSI2_2_LANE:
+ lanes = 2;
+ break;
+ case V4L2_MBUS_CSI2_1_LANE:
+ lanes = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = readl(base + CIF_ICCL);
+ writel(val | CIF_ICCL_MIPI_CLK, base + CIF_ICCL);
+
+ mipi_ctrl = CIF_MIPI_CTRL_NUM_LANES(lanes - 1) |
+ CIF_MIPI_CTRL_SHUTDOWNLANES(0xf) |
+ CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP |
+ CIF_MIPI_CTRL_CLOCKLANE_ENA;
+
+ writel(mipi_ctrl, base + CIF_MIPI_CTRL);
+
+ /* Configure Data Type and Virtual Channel */
+ writel(CIF_MIPI_DATA_SEL_DT(in_fmt->mipi_dt) | CIF_MIPI_DATA_SEL_VC(0),
+ base + CIF_MIPI_IMG_DATA_SEL);
+
+ /* Clear MIPI interrupts */
+ writel(~0, base + CIF_MIPI_ICR);
+ /*
+ * Disable CIF_MIPI_ERR_DPHY interrupt here temporary for
+ * isp bus may be dead when switch isp.
+ */
+ writel(CIF_MIPI_FRAME_END | CIF_MIPI_ERR_CSI | CIF_MIPI_ERR_DPHY |
+ CIF_MIPI_SYNC_FIFO_OVFLW(0x03) | CIF_MIPI_ADD_DATA_OVFLW,
+ base + CIF_MIPI_IMSC);
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "\n MIPI_CTRL 0x%08x\n"
+ " MIPI_IMG_DATA_SEL 0x%08x\n"
+ " MIPI_STATUS 0x%08x\n"
+ " MIPI_IMSC 0x%08x\n",
+ readl(base + CIF_MIPI_CTRL),
+ readl(base + CIF_MIPI_IMG_DATA_SEL),
+ readl(base + CIF_MIPI_STATUS),
+ readl(base + CIF_MIPI_IMSC));
+
+ return 0;
+}
+
+/* Configure MUX */
+static int rkisp1_config_path(struct rkisp1_device *dev)
+{
+ int ret = 0;
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ u32 dpcl = readl(dev->base_addr + CIF_VI_DPCL);
+
+ if (sensor->mbus.type == V4L2_MBUS_BT656 ||
+ sensor->mbus.type == V4L2_MBUS_PARALLEL) {
+ ret = rkisp1_config_dvp(dev);
+ dpcl |= CIF_VI_DPCL_IF_SEL_PARALLEL;
+ } else if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ ret = rkisp1_config_mipi(dev);
+ dpcl |= CIF_VI_DPCL_IF_SEL_MIPI;
+ }
+
+ writel(dpcl, dev->base_addr + CIF_VI_DPCL);
+
+ return ret;
+}
+
+/* Hareware configure Entry */
+static int rkisp1_config_cif(struct rkisp1_device *dev)
+{
+ int ret = 0;
+ u32 cif_id;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ cif_id = readl(dev->base_addr + CIF_VI_ID);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "CIF_ID 0x%08x\n", cif_id);
+
+ ret = rkisp1_config_isp(dev);
+ if (ret < 0)
+ return ret;
+ ret = rkisp1_config_path(dev);
+ if (ret < 0)
+ return ret;
+ rkisp1_config_ism(dev);
+
+ return 0;
+}
+
+/* Mess register operations to stop isp */
+static int rkisp1_isp_stop(struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ /*
+ * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
+ * Stop ISP(isp) ->wait for ISP isp off
+ */
+ /* stop and clear MI, MIPI, and ISP interrupts */
+ writel(0, base + CIF_MIPI_IMSC);
+ writel(~0, base + CIF_MIPI_ICR);
+
+ writel(0, base + CIF_ISP_IMSC);
+ writel(~0, base + CIF_ISP_ICR);
+
+ writel(0, base + CIF_MI_IMSC);
+ writel(~0, base + CIF_MI_ICR);
+ val = readl(base + CIF_MIPI_CTRL);
+ writel(val & (~CIF_MIPI_CTRL_OUTPUT_ENA), base + CIF_MIPI_CTRL);
+ /* stop ISP */
+ val = readl(base + CIF_ISP_CTRL);
+ val &= ~(CIF_ISP_CTRL_ISP_INFORM_ENABLE | CIF_ISP_CTRL_ISP_ENABLE);
+ writel(val, base + CIF_ISP_CTRL);
+
+ val = readl(base + CIF_ISP_CTRL);
+ writel(val | CIF_ISP_CTRL_ISP_CFG_UPD, base + CIF_ISP_CTRL);
+
+ readx_poll_timeout(readl, base + CIF_ISP_RIS,
+ val, val & CIF_ISP_OFF, 20, 100);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "state(MP:%d, SP:%d), MI_CTRL:%x, ISP_CTRL:%x, MIPI_CTRL:%x\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state,
+ readl(base + CIF_MI_CTRL),
+ readl(base + CIF_ISP_CTRL),
+ readl(base + CIF_MIPI_CTRL));
+
+ writel(CIF_IRCL_MIPI_SW_RST | CIF_IRCL_ISP_SW_RST, base + CIF_IRCL);
+ writel(0x0, base + CIF_IRCL);
+
+ return 0;
+}
+
+/* Mess register operations to start isp */
+static int rkisp1_isp_start(struct rkisp1_device *dev)
+{
+ struct rkisp1_sensor_info *sensor = dev->active_sensor;
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state);
+
+ /* Activate MIPI */
+ if (sensor->mbus.type == V4L2_MBUS_CSI2) {
+ val = readl(base + CIF_MIPI_CTRL);
+ writel(val | CIF_MIPI_CTRL_OUTPUT_ENA, base + CIF_MIPI_CTRL);
+ }
+ /* Activate ISP */
+ val = readl(base + CIF_ISP_CTRL);
+ val |= CIF_ISP_CTRL_ISP_CFG_UPD | CIF_ISP_CTRL_ISP_ENABLE |
+ CIF_ISP_CTRL_ISP_INFORM_ENABLE;
+ writel(val, base + CIF_ISP_CTRL);
+
+ /* XXX: Is the 1000us too long?
+ * CIF spec says to wait for sufficient time after enabling
+ * the MIPI interface and before starting the sensor output.
+ */
+ usleep_range(1000, 1200);
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "SP state = %d, MP state = %d MI_CTRL 0x%08x\n"
+ " ISP_CTRL 0x%08x MIPI_CTRL 0x%08x\n",
+ dev->stream[RKISP1_STREAM_SP].state,
+ dev->stream[RKISP1_STREAM_MP].state,
+ readl(base + CIF_MI_CTRL),
+ readl(base + CIF_ISP_CTRL),
+ readl(base + CIF_MIPI_CTRL));
+
+ return 0;
+}
+
+static void rkisp1_config_clk(struct rkisp1_device *dev)
+{
+ u32 val = CIF_ICCL_ISP_CLK | CIF_ICCL_CP_CLK | CIF_ICCL_MRSZ_CLK |
+ CIF_ICCL_SRSZ_CLK | CIF_ICCL_JPEG_CLK | CIF_ICCL_MI_CLK |
+ CIF_ICCL_MIPI_CLK | CIF_ICCL_DCROP_CLK;
+
+ writel(val, dev->base_addr + CIF_ICCL);
+}
+
+/***************************** isp sub-devs *******************************/
+
+static const struct ispsd_in_fmt rkisp1_isp_input_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW10,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW12,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 12,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_RGGB,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_BGGR,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_GBRG,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt_type = FMT_BAYER,
+ .mipi_dt = CIF_CSI2_DT_RAW8,
+ .bayer_pat = RAW_GRBG,
+ .bus_width = 8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_YCBYCR,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_YCRYCB,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_CBYCRY,
+ .bus_width = 16,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .fmt_type = FMT_YUV,
+ .mipi_dt = CIF_CSI2_DT_YUV422_8b,
+ .yuv_seq = CIF_ISP_ACQ_PROP_CRYCBY,
+ .bus_width = 16,
+ },
+};
+
+static const struct ispsd_out_fmt rkisp1_isp_output_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt_type = FMT_YUV,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .fmt_type = FMT_BAYER,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .fmt_type = FMT_BAYER,
+ },
+};
+
+static const struct ispsd_in_fmt *find_in_fmt(u32 mbus_code)
+{
+ const struct ispsd_in_fmt *fmt;
+ int i, array_size = ARRAY_SIZE(rkisp1_isp_input_formats);
+
+ for (i = 0; i < array_size; i++) {
+ fmt = &rkisp1_isp_input_formats[i];
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static const struct ispsd_out_fmt *find_out_fmt(u32 mbus_code)
+{
+ const struct ispsd_out_fmt *fmt;
+ int i, array_size = ARRAY_SIZE(rkisp1_isp_output_formats);
+
+ for (i = 0; i < array_size; i++) {
+ fmt = &rkisp1_isp_output_formats[i];
+ if (fmt->mbus_code == mbus_code)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int rkisp1_isp_sd_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ int i = code->index;
+
+ if (code->pad == RKISP1_ISP_PAD_SINK) {
+ if (i >= ARRAY_SIZE(rkisp1_isp_input_formats))
+ return -EINVAL;
+ code->code = rkisp1_isp_input_formats[i].mbus_code;
+ } else {
+ if (i >= ARRAY_SIZE(rkisp1_isp_output_formats))
+ return -EINVAL;
+ code->code = rkisp1_isp_output_formats[i].mbus_code;
+ }
+
+ return 0;
+}
+
+#define sd_to_isp_sd(_sd) container_of(_sd, struct rkisp1_isp_subdev, sd)
+static int rkisp1_isp_sd_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+
+ if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
+ (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
+ return -EINVAL;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ fmt->format = *mf;
+ return 0;
+ }
+
+ if (fmt->pad == RKISP1_ISP_PAD_SINK) {
+ *mf = isp_sd->in_frm;
+ } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ /* format of source pad */
+ *mf = isp_sd->in_frm;
+ /* window size of source pad */
+ mf->width = isp_sd->out_crop.width;
+ mf->height = isp_sd->out_crop.height;
+ mf->quantization = isp_sd->quantization;
+ }
+ mf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static void rkisp1_isp_sd_try_fmt(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
+ const struct ispsd_in_fmt *in_fmt;
+ const struct ispsd_out_fmt *out_fmt;
+
+ switch (pad) {
+ case RKISP1_ISP_PAD_SINK:
+ in_fmt = find_in_fmt(fmt->code);
+ if (in_fmt)
+ fmt->code = in_fmt->mbus_code;
+ else
+ fmt->code = MEDIA_BUS_FMT_SRGGB10_1X10;
+ fmt->width = clamp_t(u32, fmt->width, CIF_ISP_INPUT_W_MIN,
+ CIF_ISP_INPUT_W_MAX);
+ fmt->height = clamp_t(u32, fmt->height, CIF_ISP_INPUT_H_MIN,
+ CIF_ISP_INPUT_H_MAX);
+ break;
+ case RKISP1_ISP_PAD_SOURCE_PATH:
+ out_fmt = find_out_fmt(fmt->code);
+ if (out_fmt)
+ fmt->code = out_fmt->mbus_code;
+ else
+ fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
+ /* window size is set in s_selection */
+ fmt->width = isp_sd->out_crop.width;
+ fmt->height = isp_sd->out_crop.height;
+ /* full range by default */
+ if (!fmt->quantization)
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+}
+
+static int rkisp1_isp_sd_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
+ struct v4l2_mbus_framefmt *mf = &fmt->format;
+
+ if ((fmt->pad != RKISP1_ISP_PAD_SINK) &&
+ (fmt->pad != RKISP1_ISP_PAD_SOURCE_PATH))
+ return -EINVAL;
+
+ rkisp1_isp_sd_try_fmt(sd, fmt->pad, mf);
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *try_mf;
+
+ mf = v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+ *try_mf = *mf;
+ return 0;
+ }
+
+ if (fmt->pad == RKISP1_ISP_PAD_SINK) {
+ const struct ispsd_in_fmt *in_fmt;
+
+ in_fmt = find_in_fmt(mf->code);
+ isp_sd->in_fmt = *in_fmt;
+ isp_sd->in_frm = *mf;
+ } else if (fmt->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ const struct ispsd_out_fmt *out_fmt;
+
+ /* Ignore width/height */
+ out_fmt = find_out_fmt(mf->code);
+ isp_sd->out_fmt = *out_fmt;
+ /*
+ * It is quantization for output,
+ * isp use bt601 limit-range in internal
+ */
+ isp_sd->quantization = mf->quantization;
+ }
+
+ return 0;
+}
+
+static void rkisp1_isp_sd_try_crop(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+ struct v4l2_mbus_framefmt in_frm = isp_sd->in_frm;
+ struct v4l2_rect in_crop = isp_sd->in_crop;
+ struct v4l2_rect *input = &sel->r;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ in_frm = *v4l2_subdev_get_try_format(sd, cfg, RKISP1_ISP_PAD_SINK);
+ in_crop = *v4l2_subdev_get_try_crop(sd, cfg, RKISP1_ISP_PAD_SINK);
+ }
+
+ input->left = ALIGN(input->left, 2);
+ input->width = ALIGN(input->width, 2);
+
+ if (sel->pad == RKISP1_ISP_PAD_SINK) {
+ input->left = clamp_t(u32, input->left, 0, in_frm.width);
+ input->top = clamp_t(u32, input->top, 0, in_frm.height);
+ input->width = clamp_t(u32, input->width, CIF_ISP_INPUT_W_MIN,
+ in_frm.width - input->left);
+ input->height = clamp_t(u32, input->height,
+ CIF_ISP_INPUT_H_MIN,
+ in_frm.height - input->top);
+ } else if (sel->pad == RKISP1_ISP_PAD_SOURCE_PATH) {
+ input->left = clamp_t(u32, input->left, 0, in_crop.width);
+ input->top = clamp_t(u32, input->top, 0, in_crop.height);
+ input->width = clamp_t(u32, input->width, CIF_ISP_OUTPUT_W_MIN,
+ in_crop.width - input->left);
+ input->height = clamp_t(u32, input->height, CIF_ISP_OUTPUT_H_MIN,
+ in_crop.height - input->top);
+ }
+}
+
+static int rkisp1_isp_sd_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+
+ if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
+ sel->pad != RKISP1_ISP_PAD_SINK)
+ return -EINVAL;
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *try_sel;
+
+ try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
+ sel->r = *try_sel;
+ return 0;
+ }
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ if (sel->pad == RKISP1_ISP_PAD_SINK) {
+ sel->r.height = isp_sd->in_frm.height;
+ sel->r.width = isp_sd->in_frm.width;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ } else {
+ sel->r = isp_sd->in_crop;
+ }
+ break;
+ case V4L2_SEL_TGT_CROP:
+ if (sel->pad == RKISP1_ISP_PAD_SINK)
+ sel->r = isp_sd->in_crop;
+ else
+ sel->r = isp_sd->out_crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rkisp1_isp_sd_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ struct rkisp1_isp_subdev *isp_sd = sd_to_isp_sd(sd);
+ struct rkisp1_device *dev = sd_to_isp_dev(sd);
+
+ if (sel->pad != RKISP1_ISP_PAD_SOURCE_PATH &&
+ sel->pad != RKISP1_ISP_PAD_SINK)
+ return -EINVAL;
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__, sel->pad,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+ rkisp1_isp_sd_try_crop(sd, cfg, sel);
+
+ if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_rect *try_sel;
+
+ try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
+ *try_sel = sel->r;
+ return 0;
+ }
+
+ if (sel->pad == RKISP1_ISP_PAD_SINK)
+ isp_sd->in_crop = sel->r;
+ else
+ isp_sd->out_crop = sel->r;
+
+ return 0;
+}
+
+static int rkisp1_isp_sd_s_stream(struct v4l2_subdev *sd, int on)
+{
+ struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
+ struct rkisp1_sensor_info *sensor;
+ struct v4l2_subdev *sensor_sd;
+ int ret = 0;
+
+ if (!on)
+ return rkisp1_isp_stop(isp_dev);
+
+ sensor_sd = get_remote_sensor(sd);
+ if (!sensor_sd)
+ return -ENODEV;
+
+ sensor = sd_to_sensor(isp_dev, sensor_sd);
+ /*
+ * Update sensor bus configuration. This is only effective
+ * for sensors chained off an external CSI2 PHY.
+ */
+ ret = v4l2_subdev_call(sensor->sd, video, g_mbus_config,
+ &sensor->mbus);
+ if (ret && ret != -ENOIOCTLCMD)
+ return ret;
+ isp_dev->active_sensor = sensor;
+
+ atomic_set(&isp_dev->isp_sdev.frm_sync_seq, 0);
+ ret = rkisp1_config_cif(isp_dev);
+ if (ret < 0)
+ return ret;
+
+ return rkisp1_isp_start(isp_dev);
+}
+
+static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct rkisp1_device *dev = sd_to_isp_dev(sd);
+ int ret;
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "s_power: %d\n", on);
+
+ if (on) {
+ ret = pm_runtime_get_sync(dev->dev);
+ if (ret < 0)
+ return ret;
+
+ rkisp1_config_clk(dev);
+ } else {
+ ret = pm_runtime_put(dev->dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rkisp1_subdev_link_validate(struct media_link *link)
+{
+ if (link->source->index == RKISP1_ISP_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+static int rkisp1_subdev_fmt_link_validate(struct v4l2_subdev *sd,
+ struct media_link *link,
+ struct v4l2_subdev_format *source_fmt,
+ struct v4l2_subdev_format *sink_fmt)
+{
+ if (source_fmt->format.code != sink_fmt->format.code)
+ return -EINVAL;
+
+ /* Crop is available */
+ if (source_fmt->format.width < sink_fmt->format.width ||
+ source_fmt->format.height < sink_fmt->format.height)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void
+riksp1_isp_queue_event_sof(struct rkisp1_isp_subdev *isp)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence =
+ atomic_inc_return(&isp->frm_sync_seq) - 1,
+ };
+ v4l2_event_queue(isp->sd.devnode, &event);
+}
+
+static int rkisp1_isp_sd_subs_evt(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* Line number. For now only zero accepted. */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static const struct v4l2_subdev_pad_ops rkisp1_isp_sd_pad_ops = {
+ .enum_mbus_code = rkisp1_isp_sd_enum_mbus_code,
+ .get_selection = rkisp1_isp_sd_get_selection,
+ .set_selection = rkisp1_isp_sd_set_selection,
+ .get_fmt = rkisp1_isp_sd_get_fmt,
+ .set_fmt = rkisp1_isp_sd_set_fmt,
+ .link_validate = rkisp1_subdev_fmt_link_validate,
+};
+
+static const struct media_entity_operations rkisp1_isp_sd_media_ops = {
+ .link_validate = rkisp1_subdev_link_validate,
+};
+
+static const struct v4l2_subdev_video_ops rkisp1_isp_sd_video_ops = {
+ .s_stream = rkisp1_isp_sd_s_stream,
+};
+
+static const struct v4l2_subdev_core_ops rkisp1_isp_core_ops = {
+ .subscribe_event = rkisp1_isp_sd_subs_evt,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+ .s_power = rkisp1_isp_sd_s_power,
+};
+
+static struct v4l2_subdev_ops rkisp1_isp_sd_ops = {
+ .core = &rkisp1_isp_core_ops,
+ .video = &rkisp1_isp_sd_video_ops,
+ .pad = &rkisp1_isp_sd_pad_ops,
+};
+
+static void rkisp1_isp_sd_init_default_fmt(struct rkisp1_isp_subdev *isp_sd)
+{
+ struct v4l2_mbus_framefmt *in_frm = &isp_sd->in_frm;
+ struct v4l2_rect *in_crop = &isp_sd->in_crop;
+ struct v4l2_rect *out_crop = &isp_sd->out_crop;
+ struct ispsd_in_fmt *in_fmt = &isp_sd->in_fmt;
+ struct ispsd_out_fmt *out_fmt = &isp_sd->out_fmt;
+
+ *in_fmt = rkisp1_isp_input_formats[0];
+ in_frm->width = RKISP1_DEFAULT_WIDTH;
+ in_frm->height = RKISP1_DEFAULT_HEIGHT;
+ in_frm->code = in_fmt->mbus_code;
+
+ in_crop->width = in_frm->width;
+ in_crop->height = in_frm->height;
+ in_crop->left = 0;
+ in_crop->top = 0;
+
+ /* propagate to source */
+ *out_crop = *in_crop;
+ *out_fmt = rkisp1_isp_output_formats[0];
+}
+
+int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
+ struct v4l2_device *v4l2_dev)
+{
+ struct rkisp1_isp_subdev *isp_sdev = &isp_dev->isp_sdev;
+ struct v4l2_subdev *sd = &isp_sdev->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &rkisp1_isp_sd_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->entity.ops = &rkisp1_isp_sd_media_ops;
+ snprintf(sd->name, sizeof(sd->name), "rkisp1-isp-subdev");
+
+ isp_sdev->pads[RKISP1_ISP_PAD_SINK].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ isp_sdev->pads[RKISP1_ISP_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_PATH].flags = MEDIA_PAD_FL_SOURCE;
+ isp_sdev->pads[RKISP1_ISP_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN;
+ ret = media_entity_pads_init(&sd->entity, RKISP1_ISP_PAD_MAX,
+ isp_sdev->pads);
+ if (ret < 0)
+ return ret;
+
+ sd->owner = THIS_MODULE;
+ v4l2_set_subdevdata(sd, isp_dev);
+
+ sd->grp_id = GRP_ID_ISP;
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret < 0) {
+ v4l2_err(sd, "Failed to register isp subdev\n");
+ goto err_cleanup_media_entity;
+ }
+
+ rkisp1_isp_sd_init_default_fmt(isp_sdev);
+
+ return 0;
+err_cleanup_media_entity:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev)
+{
+ struct v4l2_subdev *sd = &isp_dev->isp_sdev.sd;
+
+ v4l2_device_unregister_subdev(sd);
+ media_entity_cleanup(&sd->entity);
+}
+
+/**************** Interrupter Handler ****************/
+
+void rkisp1_mipi_isr(unsigned int mis, struct rkisp1_device *dev)
+{
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ void __iomem *base = dev->base_addr;
+ u32 val;
+
+ writel(~0, base + CIF_MIPI_ICR);
+
+ /*
+ * Disable DPHY errctrl interrupt, because this dphy
+ * erctrl signal is asserted until the next changes
+ * of line state. This time is may be too long and cpu
+ * is hold in this interrupt.
+ */
+ if (mis & CIF_MIPI_ERR_CTRL(0x0f)) {
+ val = readl(base + CIF_MIPI_IMSC);
+ writel(val & ~CIF_MIPI_ERR_CTRL(0x0f), base + CIF_MIPI_IMSC);
+ dev->isp_sdev.dphy_errctrl_disabled = true;
+ }
+
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (mis == CIF_MIPI_FRAME_END) {
+ /*
+ * Enable DPHY errctrl interrupt again, if mipi have receive
+ * the whole frame without any error.
+ */
+ if (dev->isp_sdev.dphy_errctrl_disabled) {
+ val = readl(base + CIF_MIPI_IMSC);
+ val |= CIF_MIPI_ERR_CTRL(0x0f);
+ writel(val, base + CIF_MIPI_IMSC);
+ dev->isp_sdev.dphy_errctrl_disabled = false;
+ }
+ } else {
+ v4l2_warn(v4l2_dev, "MIPI mis error: 0x%08x\n", mis);
+ }
+}
+
+void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev)
+{
+ void __iomem *base = dev->base_addr;
+ unsigned int isp_mis_tmp = 0;
+ unsigned int isp_err = 0;
+
+ /* start edge of v_sync */
+ if (isp_mis & CIF_ISP_V_START) {
+ riksp1_isp_queue_event_sof(&dev->isp_sdev);
+
+ writel(CIF_ISP_V_START, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_V_START)
+ v4l2_err(&dev->v4l2_dev, "isp icr v_statr err: 0x%x\n",
+ isp_mis_tmp);
+ }
+
+ if ((isp_mis & CIF_ISP_PIC_SIZE_ERROR)) {
+ /* Clear pic_size_error */
+ writel(CIF_ISP_PIC_SIZE_ERROR, base + CIF_ISP_ICR);
+ isp_err = readl(base + CIF_ISP_ERR);
+ v4l2_err(&dev->v4l2_dev,
+ "CIF_ISP_PIC_SIZE_ERROR (0x%08x)", isp_err);
+ writel(isp_err, base + CIF_ISP_ERR_CLR);
+ } else if ((isp_mis & CIF_ISP_DATA_LOSS)) {
+ /* Clear data_loss */
+ writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
+ v4l2_err(&dev->v4l2_dev, "CIF_ISP_DATA_LOSS\n");
+ writel(CIF_ISP_DATA_LOSS, base + CIF_ISP_ICR);
+ }
+
+ /* sampled input frame is complete */
+ if (isp_mis & CIF_ISP_FRAME_IN) {
+ writel(CIF_ISP_FRAME_IN, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_FRAME_IN)
+ v4l2_err(&dev->v4l2_dev, "isp icr frame_in err: 0x%x\n",
+ isp_mis_tmp);
+ }
+
+ /* frame was completely put out */
+ if (isp_mis & CIF_ISP_FRAME) {
+ u32 isp_ris = 0;
+ /* Clear Frame In (ISP) */
+ writel(CIF_ISP_FRAME, base + CIF_ISP_ICR);
+ isp_mis_tmp = readl(base + CIF_ISP_MIS);
+ if (isp_mis_tmp & CIF_ISP_FRAME)
+ v4l2_err(&dev->v4l2_dev,
+ "isp icr frame end err: 0x%x\n", isp_mis_tmp);
+
+ isp_ris = readl(base + CIF_ISP_RIS);
+ if (isp_ris & (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN |
+ CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY))
+ rkisp1_stats_isr(&dev->stats_vdev, isp_ris);
+ }
+
+ /*
+ * Then update changed configs. Some of them involve
+ * lot of register writes. Do those only one per frame.
+ * Do the updates in the order of the processing flow.
+ */
+ rkisp1_params_isr(&dev->params_vdev, isp_mis);
+}
diff --git a/drivers/media/platform/rockchip/isp1/rkisp1.h b/drivers/media/platform/rockchip/isp1/rkisp1.h
new file mode 100644
index 000000000000..3ea008e899d9
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.h
@@ -0,0 +1,132 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _RKISP1_H
+#define _RKISP1_H
+
+#include <linux/platform_device.h>
+#include <media/v4l2-fwnode.h>
+#include "common.h"
+
+struct rkisp1_stream;
+
+/*
+ * struct ispsd_in_fmt - ISP intput-pad format
+ *
+ * Translate mbus_code to hardware format values
+ *
+ * @bus_width: used for parallel
+ */
+struct ispsd_in_fmt {
+ u32 mbus_code;
+ u8 fmt_type;
+ u32 mipi_dt;
+ u32 yuv_seq;
+ enum rkisp1_fmt_raw_pat_type bayer_pat;
+ u8 bus_width;
+};
+
+struct ispsd_out_fmt {
+ u32 mbus_code;
+ u8 fmt_type;
+};
+
+struct rkisp1_ie_config {
+ unsigned int effect;
+};
+
+enum rkisp1_isp_pad {
+ RKISP1_ISP_PAD_SINK,
+ RKISP1_ISP_PAD_SINK_PARAMS,
+ RKISP1_ISP_PAD_SOURCE_PATH,
+ RKISP1_ISP_PAD_SOURCE_STATS,
+ RKISP1_ISP_PAD_MAX
+};
+
+/*
+ * struct rkisp1_isp_subdev - ISP sub-device
+ *
+ * See Cropping regions of ISP in rkisp1.c for details
+ * @in_frm: input size, don't have to be equal to sensor size
+ * @in_fmt: intput format
+ * @in_crop: crop for sink pad
+ * @out_fmt: output format
+ * @out_crop: output size
+ *
+ * @dphy_errctrl_disabled: if dphy errctrl is disabled(avoid endless interrupt)
+ * @frm_sync_seq: frame sequence, to sync frame_id between video devices.
+ * @quantization: output quantization
+ */
+struct rkisp1_isp_subdev {
+ struct v4l2_subdev sd;
+ struct media_pad pads[RKISP1_ISP_PAD_MAX];
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_mbus_framefmt in_frm;
+ struct ispsd_in_fmt in_fmt;
+ struct v4l2_rect in_crop;
+ struct ispsd_out_fmt out_fmt;
+ struct v4l2_rect out_crop;
+ bool dphy_errctrl_disabled;
+ atomic_t frm_sync_seq;
+ enum v4l2_quantization quantization;
+};
+
+int rkisp1_register_isp_subdev(struct rkisp1_device *isp_dev,
+ struct v4l2_device *v4l2_dev);
+
+void rkisp1_unregister_isp_subdev(struct rkisp1_device *isp_dev);
+
+void rkisp1_mipi_isr(unsigned int mipi_mis, struct rkisp1_device *dev);
+
+void rkisp1_isp_isr(unsigned int isp_mis, struct rkisp1_device *dev);
+
+static inline
+struct ispsd_out_fmt *rkisp1_get_ispsd_out_fmt(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->out_fmt;
+}
+
+static inline
+struct ispsd_in_fmt *rkisp1_get_ispsd_in_fmt(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->in_fmt;
+}
+
+static inline
+struct v4l2_rect *rkisp1_get_isp_sd_win(struct rkisp1_isp_subdev *isp_sdev)
+{
+ return &isp_sdev->out_crop;
+}
+
+#endif /* _RKISP1_H */
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 06/16] media: rkisp1: add ISP1 statistics driver
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2017-12-18 12:14 ` [PATCH v4 05/16] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 07/16] media: rkisp1: add ISP1 params driver Jacob Chen
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the capture video driver for rockchip isp1 statistics block.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/isp_stats.c | 522 +++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/isp_stats.h | 85 ++++
2 files changed, 607 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_stats.h
diff --git a/drivers/media/platform/rockchip/isp1/isp_stats.c b/drivers/media/platform/rockchip/isp1/isp_stats.c
new file mode 100644
index 000000000000..dd30ebe95721
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_stats.c
@@ -0,0 +1,522 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP statistics */
+#include "dev.h"
+#include "regs.h"
+
+#define RKISP1_ISP_STATS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_STATS_REQ_BUFS_MAX 8
+
+static int rkisp1_stats_enum_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_stats_vdev *stats_vdev = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = stats_vdev->vdev_fmt.fmt.meta.dataformat;
+ return 0;
+}
+
+static int rkisp1_stats_g_fmt_meta_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_stats_vdev *stats_vdev = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = stats_vdev->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = stats_vdev->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_stats_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strcpy(cap->driver, DRIVER_NAME);
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_stats_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_cap = rkisp1_stats_enum_fmt_meta_cap,
+ .vidioc_g_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_s_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_try_fmt_meta_cap = rkisp1_stats_g_fmt_meta_cap,
+ .vidioc_querycap = rkisp1_stats_querycap
+};
+
+struct v4l2_file_operations rkisp1_stats_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int rkisp1_stats_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = vq->drv_priv;
+
+ *num_planes = 1;
+
+ *num_buffers = clamp_t(u32, *num_buffers, RKISP1_ISP_STATS_REQ_BUFS_MIN,
+ RKISP1_ISP_STATS_REQ_BUFS_MAX);
+
+ sizes[0] = sizeof(struct rkisp1_stat_buffer);
+
+ INIT_LIST_HEAD(&stats_vdev->stat);
+
+ return 0;
+}
+
+static void rkisp1_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *stats_buf = to_rkisp1_buffer(vbuf);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_isp_stats_vdev *stats_dev = vq->drv_priv;
+ unsigned long flags;
+
+ stats_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
+ spin_lock_irqsave(&stats_dev->irq_lock, flags);
+ list_add_tail(&stats_buf->queue, &stats_dev->stat);
+ spin_unlock_irqrestore(&stats_dev->irq_lock, flags);
+}
+
+static void rkisp1_stats_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ /* stop stats received firstly */
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ stats_vdev->streamon = false;
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+
+ drain_workqueue(stats_vdev->readout_wq);
+
+ for (i = 0; i < RKISP1_ISP_STATS_REQ_BUFS_MAX; i++) {
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ if (!list_empty(&stats_vdev->stat)) {
+ buf = list_first_entry(&stats_vdev->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+ } else {
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+ break;
+ }
+
+ if (buf)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ buf = NULL;
+ }
+}
+
+static int
+rkisp1_stats_vb2_start_streaming(struct vb2_queue *queue,
+ unsigned int count)
+{
+ struct rkisp1_isp_stats_vdev *stats_vdev = queue->drv_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stats_vdev->irq_lock, flags);
+ stats_vdev->streamon = true;
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops rkisp1_stats_vb2_ops = {
+ .queue_setup = rkisp1_stats_vb2_queue_setup,
+ .buf_queue = rkisp1_stats_vb2_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = rkisp1_stats_vb2_stop_streaming,
+ .start_streaming = rkisp1_stats_vb2_start_streaming,
+};
+
+static int rkisp1_stats_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = queue_to_node(q);
+
+ q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = stats_vdev;
+ q->ops = &rkisp1_stats_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_stats_get_awb_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ /* Protect against concurrent access from ISR? */
+ u32 reg_val;
+
+ pbuf->meas_type |= CIFISP_STAT_AWB;
+ reg_val = readl(stats_vdev->dev->base_addr + CIF_ISP_AWB_WHITE_CNT);
+ pbuf->params.awb.awb_mean[0].cnt = CIF_ISP_AWB_GET_PIXEL_CNT(reg_val);
+ reg_val = readl(stats_vdev->dev->base_addr + CIF_ISP_AWB_MEAN);
+
+ pbuf->params.awb.awb_mean[0].mean_cr_or_r =
+ CIF_ISP_AWB_GET_MEAN_CR_R(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_cb_or_b =
+ CIF_ISP_AWB_GET_MEAN_CB_B(reg_val);
+ pbuf->params.awb.awb_mean[0].mean_y_or_g =
+ CIF_ISP_AWB_GET_MEAN_Y_G(reg_val);
+}
+
+static void rkisp1_stats_get_aec_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ unsigned int i;
+ void __iomem *addr = stats_vdev->dev->base_addr + CIF_ISP_EXP_MEAN_00;
+
+ pbuf->meas_type |= CIFISP_STAT_AUTOEXP;
+ for (i = 0; i < CIFISP_AE_MEAN_MAX; i++)
+ pbuf->params.ae.exp_mean[i] = (u8)readl(addr + i * 4);
+}
+
+static void rkisp1_stats_get_afc_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ void __iomem *base_addr;
+ struct cifisp_af_stat *af;
+
+ pbuf->meas_type = CIFISP_STAT_AFM_FIN;
+
+ af = &pbuf->params.af;
+ base_addr = stats_vdev->dev->base_addr;
+ af->window[0].sum = readl(base_addr + CIF_ISP_AFM_SUM_A);
+ af->window[0].lum = readl(base_addr + CIF_ISP_AFM_LUM_A);
+ af->window[1].sum = readl(base_addr + CIF_ISP_AFM_SUM_B);
+ af->window[1].lum = readl(base_addr + CIF_ISP_AFM_LUM_B);
+ af->window[2].sum = readl(base_addr + CIF_ISP_AFM_SUM_C);
+ af->window[2].lum = readl(base_addr + CIF_ISP_AFM_LUM_C);
+}
+
+static void rkisp1_stats_get_hst_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ int i;
+ void __iomem *addr = stats_vdev->dev->base_addr + CIF_ISP_HIST_BIN_0;
+
+ pbuf->meas_type |= CIFISP_STAT_HIST;
+ for (i = 0; i < CIFISP_HIST_BIN_N_MAX; i++)
+ pbuf->params.hist.hist_bins[i] = readl(addr + (i * 4));
+}
+
+static void rkisp1_stats_get_bls_meas(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_stat_buffer *pbuf)
+{
+ struct rkisp1_device *dev = stats_vdev->dev;
+ const struct ispsd_in_fmt *in_fmt =
+ rkisp1_get_ispsd_in_fmt(&dev->isp_sdev);
+ void __iomem *base = stats_vdev->dev->base_addr;
+ struct cifisp_bls_meas_val *bls_val;
+
+ bls_val = &pbuf->params.ae.bls_val;
+ if (in_fmt->bayer_pat == RAW_BGGR) {
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_GBRG) {
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_GRBG) {
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_D_MEASURED);
+ } else if (in_fmt->bayer_pat == RAW_RGGB) {
+ bls_val->meas_r = readl(base + CIF_ISP_BLS_A_MEASURED);
+ bls_val->meas_gr = readl(base + CIF_ISP_BLS_B_MEASURED);
+ bls_val->meas_gb = readl(base + CIF_ISP_BLS_C_MEASURED);
+ bls_val->meas_b = readl(base + CIF_ISP_BLS_D_MEASURED);
+ }
+}
+
+static void
+rkisp1_stats_send_measurement(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct rkisp1_isp_readout_work *meas_work)
+{
+ unsigned long lock_flags = 0;
+ unsigned int cur_frame_id = -1;
+ struct rkisp1_stat_buffer *cur_stat_buf;
+ struct rkisp1_buffer *cur_buf = NULL;
+
+ spin_lock_irqsave(&stats_vdev->irq_lock, lock_flags);
+ cur_frame_id = atomic_read(&stats_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+ if (cur_frame_id != meas_work->frame_id) {
+ v4l2_warn(stats_vdev->vnode.vdev.v4l2_dev,
+ "Measurement late(%d, %d)\n",
+ cur_frame_id, meas_work->frame_id);
+ cur_frame_id = meas_work->frame_id;
+ }
+ /* get one empty buffer */
+ if (!list_empty(&stats_vdev->stat)) {
+ cur_buf = list_first_entry(&stats_vdev->stat,
+ struct rkisp1_buffer, queue);
+ list_del(&cur_buf->queue);
+ }
+ spin_unlock_irqrestore(&stats_vdev->irq_lock, lock_flags);
+
+ if (!cur_buf)
+ return;
+
+ cur_stat_buf =
+ (struct rkisp1_stat_buffer *)(cur_buf->vaddr[0]);
+
+ if (meas_work->isp_ris & CIF_ISP_AWB_DONE) {
+ rkisp1_stats_get_awb_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AWB;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_AFM_FIN) {
+ rkisp1_stats_get_afc_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AFM_FIN;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_EXP_END) {
+ rkisp1_stats_get_aec_meas(stats_vdev, cur_stat_buf);
+ rkisp1_stats_get_bls_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_AUTOEXP;
+ }
+
+ if (meas_work->isp_ris & CIF_ISP_HIST_MEASURE_RDY) {
+ rkisp1_stats_get_hst_meas(stats_vdev, cur_stat_buf);
+ cur_stat_buf->meas_type |= CIFISP_STAT_HIST;
+ }
+
+ vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0,
+ sizeof(struct rkisp1_stat_buffer));
+ cur_buf->vb.sequence = cur_frame_id;
+ cur_buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void rkisp1_stats_readout_work(struct work_struct *work)
+{
+ struct rkisp1_isp_readout_work *readout_work = container_of(work,
+ struct rkisp1_isp_readout_work,
+ work);
+ struct rkisp1_isp_stats_vdev *stats_vdev = readout_work->stats_vdev;
+
+ if (readout_work->readout == RKISP1_ISP_READOUT_MEAS)
+ rkisp1_stats_send_measurement(stats_vdev, readout_work);
+
+ kfree(readout_work);
+}
+
+int rkisp1_stats_isr(struct rkisp1_isp_stats_vdev *stats_vdev, u32 isp_ris)
+{
+ unsigned int isp_mis_tmp = 0;
+ struct rkisp1_isp_readout_work *work;
+ unsigned int cur_frame_id =
+ atomic_read(&stats_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+#ifdef LOG_ISR_EXE_TIME
+ ktime_t in_t = ktime_get();
+#endif
+
+ writel((CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN | CIF_ISP_EXP_END |
+ CIF_ISP_HIST_MEASURE_RDY),
+ stats_vdev->dev->base_addr + CIF_ISP_ICR);
+
+ isp_mis_tmp = readl(stats_vdev->dev->base_addr + CIF_ISP_MIS);
+ if (isp_mis_tmp &
+ (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN |
+ CIF_ISP_EXP_END | CIF_ISP_HIST_MEASURE_RDY))
+ v4l2_err(stats_vdev->vnode.vdev.v4l2_dev,
+ "isp icr 3A info err: 0x%x\n",
+ isp_mis_tmp);
+
+ if (isp_ris & (CIF_ISP_AWB_DONE | CIF_ISP_AFM_FIN | CIF_ISP_EXP_END |
+ CIF_ISP_HIST_MEASURE_RDY)) {
+ work = (struct rkisp1_isp_readout_work *)
+ kzalloc(sizeof(struct rkisp1_isp_readout_work),
+ GFP_ATOMIC);
+ if (work) {
+ INIT_WORK(&work->work,
+ rkisp1_stats_readout_work);
+ work->readout = RKISP1_ISP_READOUT_MEAS;
+ work->stats_vdev = stats_vdev;
+ work->frame_id = cur_frame_id;
+ work->isp_ris = isp_ris;
+ if (!queue_work(stats_vdev->readout_wq,
+ &work->work))
+ kfree(work);
+ } else {
+ v4l2_err(stats_vdev->vnode.vdev.v4l2_dev,
+ "Could not allocate work\n");
+ }
+ }
+
+#ifdef LOG_ISR_EXE_TIME
+ if (isp_ris & (CIF_ISP_EXP_END | CIF_ISP_AWB_DONE |
+ CIF_ISP_FRAME | CIF_ISP_HIST_MEASURE_RDY)) {
+ unsigned int diff_us =
+ ktime_to_us(ktime_sub(ktime_get(), in_t));
+
+ if (diff_us > g_longest_isr_time)
+ g_longest_isr_time = diff_us;
+
+ v4l2_info(stats_vdev->vnode.vdev.v4l2_dev,
+ "isp_isr time %d %d\n", diff_us, g_longest_isr_time);
+ }
+#endif
+
+ return 0;
+}
+
+static void rkisp1_init_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ stats_vdev->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_STAT_3A;
+ stats_vdev->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_stat_buffer);
+}
+
+int rkisp1_register_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev)
+{
+ int ret;
+ struct rkisp1_vdev_node *node = &stats_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ stats_vdev->dev = dev;
+ mutex_init(&node->vlock);
+ INIT_LIST_HEAD(&stats_vdev->stat);
+ spin_lock_init(&stats_vdev->irq_lock);
+
+ strlcpy(vdev->name, "rkisp1-statistics", sizeof(vdev->name));
+
+ video_set_drvdata(vdev, stats_vdev);
+ vdev->ioctl_ops = &rkisp1_stats_ioctl;
+ vdev->fops = &rkisp1_stats_fops;
+ vdev->release = video_device_release_empty;
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ rkisp1_stats_init_vb2_queue(vdev->queue, stats_vdev);
+ rkisp1_init_stats_vdev(stats_vdev);
+ video_set_drvdata(vdev, stats_vdev);
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+ vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret < 0)
+ goto err_release_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(&vdev->dev,
+ "could not register Video for Linux device\n");
+ goto err_cleanup_media_entity;
+ }
+
+ stats_vdev->readout_wq =
+ alloc_workqueue("measurement_queue",
+ WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+
+ if (!stats_vdev->readout_wq) {
+ ret = -ENOMEM;
+ goto err_unreg_vdev;
+ }
+
+ return 0;
+err_unreg_vdev:
+ video_unregister_device(vdev);
+err_cleanup_media_entity:
+ media_entity_cleanup(&vdev->entity);
+err_release_queue:
+ vb2_queue_release(vdev->queue);
+ return ret;
+}
+
+void rkisp1_unregister_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev)
+{
+ struct rkisp1_vdev_node *node = &stats_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ destroy_workqueue(stats_vdev->readout_wq);
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ vb2_queue_release(vdev->queue);
+}
diff --git a/drivers/media/platform/rockchip/isp1/isp_stats.h b/drivers/media/platform/rockchip/isp1/isp_stats.h
new file mode 100644
index 000000000000..71fd4e447fd9
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_stats.h
@@ -0,0 +1,85 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _RKISP1_ISP_STATS_H
+#define _RKISP1_ISP_STATS_H
+
+#include <linux/rkisp1-config.h>
+#include "common.h"
+
+struct rkisp1_isp_stats_vdev;
+
+enum rkisp1_isp_readout_cmd {
+ RKISP1_ISP_READOUT_MEAS,
+ RKISP1_ISP_READOUT_META,
+};
+
+struct rkisp1_isp_readout_work {
+ struct work_struct work;
+ struct rkisp1_isp_stats_vdev *stats_vdev;
+
+ unsigned int frame_id;
+ unsigned int isp_ris;
+ enum rkisp1_isp_readout_cmd readout;
+ struct vb2_buffer *vb;
+};
+
+/*
+ * struct rkisp1_isp_stats_vdev - ISP Statistics device
+ *
+ * @irq_lock: buffer queue lock
+ * @stat: stats buffer list
+ * @readout_wq: workqueue for statistics information read
+ */
+struct rkisp1_isp_stats_vdev {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *dev;
+
+ spinlock_t irq_lock;
+ struct list_head stat;
+ struct v4l2_format vdev_fmt;
+ bool streamon;
+
+ struct workqueue_struct *readout_wq;
+};
+
+int rkisp1_stats_isr(struct rkisp1_isp_stats_vdev *stats_vdev, u32 isp_ris);
+
+int rkisp1_register_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev);
+
+void rkisp1_unregister_stats_vdev(struct rkisp1_isp_stats_vdev *stats_vdev);
+
+#endif /* _RKISP1_ISP_STATS_H */
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 07/16] media: rkisp1: add ISP1 params driver
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2017-12-18 12:14 ` [PATCH v4 05/16] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
2017-12-18 12:14 ` [PATCH v4 06/16] media: rkisp1: add ISP1 statistics driver Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 09/16] media: rkisp1: add rockchip isp1 core driver Jacob Chen
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the output video driver that accept params from userspace.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/rockchip/isp1/isp_params.c | 1537 +++++++++++++++++++++
drivers/media/platform/rockchip/isp1/isp_params.h | 76 +
2 files changed, 1613 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.c
create mode 100644 drivers/media/platform/rockchip/isp1/isp_params.h
diff --git a/drivers/media/platform/rockchip/isp1/isp_params.c b/drivers/media/platform/rockchip/isp1/isp_params.c
new file mode 100644
index 000000000000..287f8523dbd9
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_params.c
@@ -0,0 +1,1537 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-vmalloc.h> /* for ISP params */
+#include "dev.h"
+#include "regs.h"
+
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MIN 2
+#define RKISP1_ISP_PARAMS_REQ_BUFS_MAX 8
+
+#define BLS_START_H_MAX_IS_VALID(val) ((val) < CIFISP_BLS_START_H_MAX)
+#define BLS_STOP_H_MAX_IS_VALID(val) ((val) < CIFISP_BLS_STOP_H_MAX)
+
+#define BLS_START_V_MAX_IS_VALID(val) ((val) < CIFISP_BLS_START_V_MAX)
+#define BLS_STOP_V_MAX_IS_VALID(val) ((val) < CIFISP_BLS_STOP_V_MAX)
+
+#define BLS_SAMPLE_MAX_IS_VALID(val) ((val) < CIFISP_BLS_SAMPLES_MAX)
+
+#define BLS_FIX_SUB_IS_VALID(val) \
+ ((val) > (s16) CIFISP_BLS_FIX_SUB_MIN && (val) < CIFISP_BLS_FIX_SUB_MAX)
+
+#define RKISP1_ISP_DPCC_LINE_THRESH(n) (CIF_ISP_DPCC_LINE_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_LINE_MAD_FAC(n) (CIF_ISP_DPCC_LINE_MAD_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_PG_FAC(n) (CIF_ISP_DPCC_PG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RND_THRESH(n) (CIF_ISP_DPCC_RND_THRESH_1 + 0x14 * (n))
+#define RKISP1_ISP_DPCC_RG_FAC(n) (CIF_ISP_DPCC_RG_FAC_1 + 0x14 * (n))
+#define RKISP1_ISP_CC_COEFF(n) (CIF_ISP_CC_COEFF_0 + (n) * 4)
+
+static inline void rkisp1_iowrite32(struct rkisp1_isp_params_vdev *params_vdev,
+ u32 value, u32 addr)
+{
+ iowrite32(value, params_vdev->dev->base_addr + addr);
+}
+
+static inline u32 rkisp1_ioread32(struct rkisp1_isp_params_vdev *params_vdev,
+ u32 addr)
+{
+ return ioread32(params_vdev->dev->base_addr + addr);
+}
+
+static inline void isp_param_set_bits(struct rkisp1_isp_params_vdev
+ *params_vdev,
+ u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_ioread32(params_vdev, reg);
+ rkisp1_iowrite32(params_vdev, val | bit_mask, reg);
+}
+
+static inline void isp_param_clear_bits(struct rkisp1_isp_params_vdev
+ *params_vdev,
+ u32 reg, u32 bit_mask)
+{
+ u32 val;
+
+ val = rkisp1_ioread32(params_vdev, reg);
+ rkisp1_iowrite32(params_vdev, val & ~bit_mask, reg);
+}
+
+/* ISP BP interface function */
+static void dpcc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpcc_config *arg)
+{
+ unsigned int i;
+
+ rkisp1_iowrite32(params_vdev, arg->mode, CIF_ISP_DPCC_MODE);
+ rkisp1_iowrite32(params_vdev, arg->output_mode,
+ CIF_ISP_DPCC_OUTPUT_MODE);
+ rkisp1_iowrite32(params_vdev, arg->set_use, CIF_ISP_DPCC_SET_USE);
+
+ rkisp1_iowrite32(params_vdev, arg->methods[0].method,
+ CIF_ISP_DPCC_METHODS_SET_1);
+ rkisp1_iowrite32(params_vdev, arg->methods[1].method,
+ CIF_ISP_DPCC_METHODS_SET_2);
+ rkisp1_iowrite32(params_vdev, arg->methods[2].method,
+ CIF_ISP_DPCC_METHODS_SET_3);
+ for (i = 0; i < CIFISP_DPCC_METHODS_MAX; i++) {
+ rkisp1_iowrite32(params_vdev, arg->methods[i].line_thresh,
+ RKISP1_ISP_DPCC_LINE_THRESH(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].line_mad_fac,
+ RKISP1_ISP_DPCC_LINE_MAD_FAC(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].pg_fac,
+ RKISP1_ISP_DPCC_PG_FAC(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].rnd_thresh,
+ RKISP1_ISP_DPCC_RND_THRESH(i));
+ rkisp1_iowrite32(params_vdev, arg->methods[i].rg_fac,
+ RKISP1_ISP_DPCC_RG_FAC(i));
+ }
+
+ rkisp1_iowrite32(params_vdev, arg->rnd_offs, CIF_ISP_DPCC_RND_OFFS);
+ rkisp1_iowrite32(params_vdev, arg->ro_limits, CIF_ISP_DPCC_RO_LIMITS);
+}
+
+/* ISP black level subtraction interface function */
+static void bls_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_bls_config *arg)
+{
+ u32 new_control = 0;
+
+ /* fixed subtraction values */
+ if (!arg->enable_auto) {
+ const struct cifisp_bls_fixed_val *pval = &arg->fixed_val;
+
+ switch (params_vdev->raw_type) {
+ case RAW_BGGR:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_A_FIXED);
+ break;
+ case RAW_GBRG:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_B_FIXED);
+ break;
+ case RAW_GRBG:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_D_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_C_FIXED);
+ break;
+ case RAW_RGGB:
+ rkisp1_iowrite32(params_vdev,
+ pval->r, CIF_ISP_BLS_A_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gr, CIF_ISP_BLS_B_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->gb, CIF_ISP_BLS_C_FIXED);
+ rkisp1_iowrite32(params_vdev,
+ pval->b, CIF_ISP_BLS_D_FIXED);
+ break;
+ default:
+ break;
+ }
+
+ new_control = CIF_ISP_BLS_MODE_FIXED;
+ } else {
+ if (arg->en_windows & BIT(1)) {
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.h_offs,
+ CIF_ISP_BLS_H2_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.h_size,
+ CIF_ISP_BLS_H2_STOP);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.v_offs,
+ CIF_ISP_BLS_V2_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window2.v_size,
+ CIF_ISP_BLS_V2_STOP);
+ new_control |= CIF_ISP_BLS_WINDOW_2;
+ }
+
+ if (arg->en_windows & BIT(0)) {
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.h_offs,
+ CIF_ISP_BLS_H1_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.h_size,
+ CIF_ISP_BLS_H1_STOP);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.v_offs,
+ CIF_ISP_BLS_V1_START);
+ rkisp1_iowrite32(params_vdev, arg->bls_window1.v_size,
+ CIF_ISP_BLS_V1_STOP);
+ new_control |= CIF_ISP_BLS_WINDOW_1;
+ }
+
+ rkisp1_iowrite32(params_vdev, arg->bls_samples,
+ CIF_ISP_BLS_SAMPLES);
+
+ new_control |= CIF_ISP_BLS_MODE_MEASURED;
+ }
+ rkisp1_iowrite32(params_vdev, new_control, CIF_ISP_BLS_CTRL);
+}
+
+/* ISP LS correction interface function */
+static void
+__lsc_correct_matrix_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_lsc_config *pconfig)
+{
+ int i, j;
+ unsigned int isp_lsc_status, sram_addr, isp_lsc_table_sel;
+ unsigned int data;
+
+ isp_lsc_status = rkisp1_ioread32(params_vdev, CIF_ISP_LSC_STATUS);
+
+ /* CIF_ISP_LSC_TABLE_ADDRESS_153 = ( 17 * 18 ) >> 1 */
+ sram_addr = (isp_lsc_status & CIF_ISP_LSC_ACTIVE_TABLE) ?
+ CIF_ISP_LSC_TABLE_ADDRESS_0 :
+ CIF_ISP_LSC_TABLE_ADDRESS_153;
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_R_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_GR_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_GB_TABLE_ADDR);
+ rkisp1_iowrite32(params_vdev, sram_addr, CIF_ISP_LSC_B_TABLE_ADDR);
+
+ /* program data tables (table size is 9 * 17 = 153) */
+ for (i = 0; i < ((CIF_ISP_LSC_SECTORS_MAX + 1) *
+ (CIF_ISP_LSC_SECTORS_MAX + 1));
+ i += CIF_ISP_LSC_SECTORS_MAX + 1) {
+ /*
+ * 17 sectors with 2 values in one DWORD = 9
+ * DWORDs (2nd value of last DWORD unused)
+ */
+ for (j = 0; j < (CIF_ISP_LSC_SECTORS_MAX + 1); j += 2) {
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->r_data_tbl[i + j],
+ pconfig->r_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_R_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->gr_data_tbl[i + j],
+ pconfig->gr_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_GR_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->gb_data_tbl[i + j],
+ pconfig->gb_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_GB_TABLE_DATA);
+
+ data = CIF_ISP_LSC_TABLE_DATA(
+ pconfig->b_data_tbl[i + j],
+ pconfig->b_data_tbl[i + j + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_B_TABLE_DATA);
+ }
+ }
+
+ isp_lsc_table_sel = (isp_lsc_status & CIF_ISP_LSC_ACTIVE_TABLE) ?
+ CIF_ISP_LSC_TABLE_0 : CIF_ISP_LSC_TABLE_1;
+ rkisp1_iowrite32(params_vdev, isp_lsc_table_sel, CIF_ISP_LSC_TABLE_SEL);
+}
+
+static void lsc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_lsc_config *arg)
+{
+ int i;
+ u32 lsc_ctrl;
+ unsigned int data;
+
+ /* To config must be off , store the current status firstly */
+ lsc_ctrl = rkisp1_ioread32(params_vdev, CIF_ISP_LSC_CTRL);
+ isp_param_clear_bits(params_vdev, CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ __lsc_correct_matrix_config(params_vdev, arg);
+
+ for (i = 0; i < 4; i++) {
+ /* program x size tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2],
+ arg->x_size_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_XSIZE_01 + i * 4);
+
+ /* program x grad tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2],
+ arg->x_grad_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_XGRAD_01 + i * 4);
+
+ /* program y size tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2],
+ arg->y_size_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_YSIZE_01 + i * 4);
+
+ /* program y grad tables */
+ data = CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2],
+ arg->y_grad_tbl[i * 2 + 1]);
+ rkisp1_iowrite32(params_vdev, data,
+ CIF_ISP_LSC_YGRAD_01 + i * 4);
+ }
+
+ /* restore the bls ctrl status */
+ if (lsc_ctrl & CIF_ISP_LSC_CTRL_ENA) {
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ } else {
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ }
+}
+
+/* ISP Filtering function */
+static void flt_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_flt_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->thresh_bl0, CIF_ISP_FILT_THRESH_BL0);
+ rkisp1_iowrite32(params_vdev, arg->thresh_bl1, CIF_ISP_FILT_THRESH_BL1);
+ rkisp1_iowrite32(params_vdev, arg->thresh_sh0, CIF_ISP_FILT_THRESH_SH0);
+ rkisp1_iowrite32(params_vdev, arg->thresh_sh1, CIF_ISP_FILT_THRESH_SH1);
+ rkisp1_iowrite32(params_vdev, arg->fac_bl0, CIF_ISP_FILT_FAC_BL0);
+ rkisp1_iowrite32(params_vdev, arg->fac_bl1, CIF_ISP_FILT_FAC_BL1);
+ rkisp1_iowrite32(params_vdev, arg->fac_mid, CIF_ISP_FILT_FAC_MID);
+ rkisp1_iowrite32(params_vdev, arg->fac_sh0, CIF_ISP_FILT_FAC_SH0);
+ rkisp1_iowrite32(params_vdev, arg->fac_sh1, CIF_ISP_FILT_FAC_SH1);
+ rkisp1_iowrite32(params_vdev, arg->lum_weight, CIF_ISP_FILT_LUM_WEIGHT);
+
+ rkisp1_iowrite32(params_vdev, (arg->mode ? CIF_ISP_FLT_MODE_DNR : 0) |
+ CIF_ISP_FLT_CHROMA_V_MODE(arg->chr_v_mode) |
+ CIF_ISP_FLT_CHROMA_H_MODE(arg->chr_h_mode) |
+ CIF_ISP_FLT_GREEN_STAGE1(arg->grn_stage1),
+ CIF_ISP_FILT_MODE);
+}
+
+/* ISP demosaic interface function */
+static int bdm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_bdm_config *arg)
+{
+ /* set demosaic threshold */
+ rkisp1_iowrite32(params_vdev, arg->demosaic_th, CIF_ISP_DEMOSAIC);
+ return 0;
+}
+
+/* ISP GAMMA correction interface function */
+static void sdg_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_sdg_config *arg)
+{
+ int i;
+
+ rkisp1_iowrite32(params_vdev,
+ arg->xa_pnts.gamma_dx0, CIF_ISP_GAMMA_DX_LO);
+ rkisp1_iowrite32(params_vdev,
+ arg->xa_pnts.gamma_dx1, CIF_ISP_GAMMA_DX_HI);
+
+ for (i = 0; i < CIFISP_DEGAMMA_CURVE_SIZE; i++) {
+ rkisp1_iowrite32(params_vdev, arg->curve_r.gamma_y[i],
+ CIF_ISP_GAMMA_R_Y0 + i * 4);
+ rkisp1_iowrite32(params_vdev, arg->curve_g.gamma_y[i],
+ CIF_ISP_GAMMA_G_Y0 + i * 4);
+ rkisp1_iowrite32(params_vdev, arg->curve_b.gamma_y[i],
+ CIF_ISP_GAMMA_B_Y0 + i * 4);
+ }
+}
+
+/* ISP GAMMA correction interface function */
+static void goc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_goc_config *arg)
+{
+ int i;
+
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ rkisp1_iowrite32(params_vdev, arg->mode, CIF_ISP_GAMMA_OUT_MODE);
+
+ for (i = 0; i < CIFISP_GAMMA_OUT_MAX_SAMPLES; i++)
+ rkisp1_iowrite32(params_vdev, arg->gamma_y[i],
+ CIF_ISP_GAMMA_OUT_Y_0 + i * 4);
+}
+
+/* ISP Cross Talk */
+static void ctk_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_ctk_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->coeff0, CIF_ISP_CT_COEFF_0);
+ rkisp1_iowrite32(params_vdev, arg->coeff1, CIF_ISP_CT_COEFF_1);
+ rkisp1_iowrite32(params_vdev, arg->coeff2, CIF_ISP_CT_COEFF_2);
+ rkisp1_iowrite32(params_vdev, arg->coeff3, CIF_ISP_CT_COEFF_3);
+ rkisp1_iowrite32(params_vdev, arg->coeff4, CIF_ISP_CT_COEFF_4);
+ rkisp1_iowrite32(params_vdev, arg->coeff5, CIF_ISP_CT_COEFF_5);
+ rkisp1_iowrite32(params_vdev, arg->coeff6, CIF_ISP_CT_COEFF_6);
+ rkisp1_iowrite32(params_vdev, arg->coeff7, CIF_ISP_CT_COEFF_7);
+ rkisp1_iowrite32(params_vdev, arg->coeff8, CIF_ISP_CT_COEFF_8);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_r, CIF_ISP_CT_OFFSET_R);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_g, CIF_ISP_CT_OFFSET_G);
+ rkisp1_iowrite32(params_vdev, arg->ct_offset_b, CIF_ISP_CT_OFFSET_B);
+}
+
+static void ctk_enable(struct rkisp1_isp_params_vdev *params_vdev, bool en)
+{
+ if (en)
+ return;
+
+ /* Write back the default values. */
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_0);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_1);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_2);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_3);
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_4);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_5);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_6);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_COEFF_7);
+ rkisp1_iowrite32(params_vdev, 0x80, CIF_ISP_CT_COEFF_8);
+
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_R);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_G);
+ rkisp1_iowrite32(params_vdev, 0, CIF_ISP_CT_OFFSET_B);
+}
+
+/* ISP White Balance Mode */
+static void awb_meas_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_meas_config *arg)
+{
+ /* based on the mode,configure the awb module */
+ if (arg->awb_mode == CIFISP_AWB_MODE_YCBCR) {
+ /* Reference Cb and Cr */
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_REF_CR_SET(arg->awb_ref_cr) |
+ arg->awb_ref_cb, CIF_ISP_AWB_REF);
+ /* Yc Threshold */
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_MAX_Y_SET(arg->max_y) |
+ CIF_ISP_AWB_MIN_Y_SET(arg->min_y) |
+ CIF_ISP_AWB_MAX_CS_SET(arg->max_csum) |
+ arg->min_c, CIF_ISP_AWB_THRESH);
+ }
+
+ /* window offset */
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.v_offs, CIF_ISP_AWB_WND_V_OFFS);
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.h_offs, CIF_ISP_AWB_WND_H_OFFS);
+ /* AWB window size */
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.v_size, CIF_ISP_AWB_WND_V_SIZE);
+ rkisp1_iowrite32(params_vdev,
+ arg->awb_wnd.h_size, CIF_ISP_AWB_WND_H_SIZE);
+ /* Number of frames */
+ rkisp1_iowrite32(params_vdev,
+ arg->frames, CIF_ISP_AWB_FRAMES);
+}
+
+static void awb_meas_enable(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_meas_config *arg, bool en)
+{
+ u32 reg_val = rkisp1_ioread32(params_vdev, CIF_ISP_AWB_PROP);
+
+ /* switch off */
+ reg_val &= CIF_ISP_AWB_MODE_MASK_NONE;
+
+ if (en) {
+ if (arg->awb_mode == CIFISP_AWB_MODE_RGB)
+ reg_val |= CIF_ISP_AWB_MODE_RGB_EN;
+ else
+ reg_val |= CIF_ISP_AWB_MODE_YCBCR_EN;
+
+ rkisp1_iowrite32(params_vdev, reg_val, CIF_ISP_AWB_PROP);
+
+ /* Measurements require AWB block be active. */
+ /* TODO: need to enable here ? awb_gain_enable has done this */
+ isp_param_set_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ } else {
+ rkisp1_iowrite32(params_vdev,
+ reg_val, CIF_ISP_AWB_PROP);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+}
+
+static void awb_gain_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_awb_gain_config *arg)
+{
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AWB_GAIN_R_SET(arg->gain_green_r) |
+ arg->gain_green_b, CIF_ISP_AWB_GAIN_G);
+
+ rkisp1_iowrite32(params_vdev, CIF_ISP_AWB_GAIN_R_SET(arg->gain_red) |
+ arg->gain_blue, CIF_ISP_AWB_GAIN_RB);
+}
+
+static void aec_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_aec_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+
+ rkisp1_iowrite32(params_vdev,
+ ((arg->autostop) ? CIF_ISP_EXP_CTRL_AUTOSTOP : 0) |
+ ((arg->mode == CIFISP_EXP_MEASURING_MODE_1) ?
+ CIF_ISP_EXP_CTRL_MEASMODE_1 : 0), CIF_ISP_EXP_CTRL);
+
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.h_offs, CIF_ISP_EXP_H_OFFSET);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.v_offs, CIF_ISP_EXP_V_OFFSET);
+
+ block_hsize = arg->meas_window.h_size / CIF_ISP_EXP_COLUMN_NUM - 1;
+ block_vsize = arg->meas_window.v_size / CIF_ISP_EXP_ROW_NUM - 1;
+
+ rkisp1_iowrite32(params_vdev, CIF_ISP_EXP_H_SIZE_SET(block_hsize),
+ CIF_ISP_EXP_H_SIZE);
+ rkisp1_iowrite32(params_vdev, CIF_ISP_EXP_V_SIZE_SET(block_vsize),
+ CIF_ISP_EXP_V_SIZE);
+}
+
+static void cproc_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_cproc_config *arg)
+{
+ struct cifisp_isp_other_cfg *cur_other_cfg = ¶ms_vdev->cur_params.others;
+ struct cifisp_ie_config *cur_ie_config = &cur_other_cfg->ie_config;
+ u32 effect = cur_ie_config->effect;
+ u32 quantization = params_vdev->quantization;
+
+ rkisp1_iowrite32(params_vdev, arg->contrast, CIF_C_PROC_CONTRAST);
+ rkisp1_iowrite32(params_vdev, arg->hue, CIF_C_PROC_HUE);
+ rkisp1_iowrite32(params_vdev, arg->sat, CIF_C_PROC_SATURATION);
+ rkisp1_iowrite32(params_vdev, arg->brightness, CIF_C_PROC_BRIGHTNESS);
+
+ if (quantization != V4L2_QUANTIZATION_FULL_RANGE ||
+ effect != V4L2_COLORFX_NONE) {
+ isp_param_clear_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_YOUT_FULL |
+ CIF_C_PROC_YIN_FULL |
+ CIF_C_PROC_COUT_FULL);
+ } else {
+ isp_param_set_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_YOUT_FULL |
+ CIF_C_PROC_YIN_FULL |
+ CIF_C_PROC_COUT_FULL);
+ }
+}
+
+static void hst_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_hst_config *arg)
+{
+ unsigned int block_hsize, block_vsize;
+ static const u32 hist_weight_regs[] = {
+ CIF_ISP_HIST_WEIGHT_00TO30, CIF_ISP_HIST_WEIGHT_40TO21,
+ CIF_ISP_HIST_WEIGHT_31TO12, CIF_ISP_HIST_WEIGHT_22TO03,
+ CIF_ISP_HIST_WEIGHT_13TO43, CIF_ISP_HIST_WEIGHT_04TO34,
+ CIF_ISP_HIST_WEIGHT_44,
+ };
+ int i;
+ const u8 *weight;
+
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_HIST_PREDIV_SET(arg->histogram_predivider),
+ CIF_ISP_HIST_PROP);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.h_offs,
+ CIF_ISP_HIST_H_OFFS);
+ rkisp1_iowrite32(params_vdev,
+ arg->meas_window.v_offs,
+ CIF_ISP_HIST_V_OFFS);
+
+ block_hsize = arg->meas_window.h_size / CIF_ISP_HIST_COLUMN_NUM - 1;
+ block_vsize = arg->meas_window.v_size / CIF_ISP_HIST_ROW_NUM - 1;
+
+ rkisp1_iowrite32(params_vdev, block_hsize, CIF_ISP_HIST_H_SIZE);
+ rkisp1_iowrite32(params_vdev, block_vsize, CIF_ISP_HIST_V_SIZE);
+
+ weight = arg->hist_weight;
+ for (i = 0; i < ARRAY_SIZE(hist_weight_regs); ++i, weight += 4)
+ rkisp1_iowrite32(params_vdev, CIF_ISP_HIST_WEIGHT_SET(
+ weight[0], weight[1], weight[2], weight[3]),
+ hist_weight_regs[i]);
+}
+
+static void hst_enable(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_hst_config *arg, bool en)
+{
+ if (en) {
+ u32 hist_prop = rkisp1_ioread32(params_vdev, CIF_ISP_HIST_PROP);
+
+ hist_prop &= ~CIF_ISP_HIST_PROP_MODE_MASK;
+ hist_prop |= arg->mode;
+ isp_param_set_bits(params_vdev, CIF_ISP_HIST_PROP, hist_prop);
+ } else {
+ isp_param_clear_bits(params_vdev, CIF_ISP_HIST_PROP,
+ CIF_ISP_HIST_PROP_MODE_MASK);
+ }
+}
+
+static void afm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_afc_config *arg)
+{
+ int i;
+ size_t num_of_win = min_t(size_t, ARRAY_SIZE(arg->afm_win),
+ arg->num_afm_win);
+
+ /* Switch off to configure. Enabled during normal flow in frame isr. */
+ isp_param_clear_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+
+ for (i = 0; i < num_of_win; i++) {
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_offs) |
+ CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_offs),
+ CIF_ISP_AFM_LT_A + i * 8);
+ rkisp1_iowrite32(params_vdev,
+ CIF_ISP_AFM_WINDOW_X(arg->afm_win[i].h_size +
+ arg->afm_win[i].h_offs) |
+ CIF_ISP_AFM_WINDOW_Y(arg->afm_win[i].v_size +
+ arg->afm_win[i].v_offs),
+ CIF_ISP_AFM_RB_A + i * 8);
+ }
+ rkisp1_iowrite32(params_vdev, arg->thres, CIF_ISP_AFM_THRES);
+ rkisp1_iowrite32(params_vdev, arg->var_shift, CIF_ISP_AFM_VAR_SHIFT);
+}
+
+static void ie_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_ie_config *arg)
+{
+ u32 eff_ctrl;
+
+ eff_ctrl = rkisp1_ioread32(params_vdev, CIF_IMG_EFF_CTRL);
+ eff_ctrl &= ~CIF_IMG_EFF_CTRL_MODE_MASK;
+
+ if (params_vdev->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ eff_ctrl |= CIF_IMG_EFF_CTRL_YCBCR_FULL;
+
+ switch (arg->effect) {
+ case V4L2_COLORFX_SEPIA:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ case V4L2_COLORFX_SET_CBCR:
+ rkisp1_iowrite32(params_vdev, arg->eff_tint, CIF_IMG_EFF_TINT);
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
+ break;
+ /*
+ * Color selection is similar to water color(AQUA):
+ * grayscale + selected color w threshold
+ */
+ case V4L2_COLORFX_AQUA:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_COLOR_SEL;
+ rkisp1_iowrite32(params_vdev, arg->color_sel,
+ CIF_IMG_EFF_COLOR_SEL);
+ break;
+ case V4L2_COLORFX_EMBOSS:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_EMBOSS;
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_1,
+ CIF_IMG_EFF_MAT_1);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_2,
+ CIF_IMG_EFF_MAT_2);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_3,
+ CIF_IMG_EFF_MAT_3);
+ break;
+ case V4L2_COLORFX_SKETCH:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SKETCH;
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_3,
+ CIF_IMG_EFF_MAT_3);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_4,
+ CIF_IMG_EFF_MAT_4);
+ rkisp1_iowrite32(params_vdev, arg->eff_mat_5,
+ CIF_IMG_EFF_MAT_5);
+ break;
+ case V4L2_COLORFX_BW:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_BLACKWHITE;
+ break;
+ case V4L2_COLORFX_NEGATIVE:
+ eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_NEGATIVE;
+ break;
+ default:
+ break;
+ }
+
+ rkisp1_iowrite32(params_vdev, eff_ctrl, CIF_IMG_EFF_CTRL);
+}
+
+static void ie_enable(struct rkisp1_isp_params_vdev *params_vdev, bool en)
+{
+ if (en) {
+ isp_param_set_bits(params_vdev, CIF_ICCL, CIF_ICCL_IE_CLK);
+ rkisp1_iowrite32(params_vdev, CIF_IMG_EFF_CTRL_ENABLE,
+ CIF_IMG_EFF_CTRL);
+ isp_param_set_bits(params_vdev, CIF_IMG_EFF_CTRL,
+ CIF_IMG_EFF_CTRL_CFG_UPD);
+ } else {
+ /* Disable measurement */
+ isp_param_clear_bits(params_vdev, CIF_IMG_EFF_CTRL,
+ CIF_IMG_EFF_CTRL_ENABLE);
+ isp_param_clear_bits(params_vdev, CIF_ICCL, CIF_ICCL_IE_CLK);
+ }
+}
+
+static void csm_config(struct rkisp1_isp_params_vdev *params_vdev,
+ bool full_range)
+{
+ static const u16 full_range_coeff[] = {
+ 0x0026, 0x004b, 0x000f,
+ 0x01ea, 0x01d6, 0x0040,
+ 0x0040, 0x01ca, 0x01f6
+ };
+ static const u16 limited_range_coeff[] = {
+ 0x0021, 0x0040, 0x000d,
+ 0x01ed, 0x01db, 0x0038,
+ 0x0038, 0x01d1, 0x01f7,
+ };
+ int i;
+
+ if (full_range) {
+ for (i = 0; i < ARRAY_SIZE(full_range_coeff); i++)
+ rkisp1_iowrite32(params_vdev, full_range_coeff[i],
+ CIF_ISP_CC_COEFF_0 + i * 4);
+
+ isp_param_set_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ } else {
+ for (i = 0; i < ARRAY_SIZE(limited_range_coeff); i++)
+ rkisp1_iowrite32(params_vdev, limited_range_coeff[i],
+ CIF_ISP_CC_COEFF_0 + i * 4);
+
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
+ CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA);
+ }
+}
+
+/* ISP De-noise Pre-Filter(DPF) function */
+static void dpf_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpf_config *arg)
+{
+ unsigned int isp_dpf_mode;
+ unsigned int spatial_coeff;
+ unsigned int i;
+
+ switch (arg->gain.mode) {
+ case CIFISP_DPF_GAIN_USAGE_NF_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_NF_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_USE_NF_GAIN |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP |
+ CIF_ISP_DPF_MODE_LSC_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_AWB_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_AWB_LSC_GAINS:
+ isp_dpf_mode = CIF_ISP_DPF_MODE_LSC_GAIN_COMP |
+ CIF_ISP_DPF_MODE_AWB_GAIN_COMP;
+ break;
+ case CIFISP_DPF_GAIN_USAGE_DISABLED:
+ default:
+ isp_dpf_mode = 0;
+ break;
+ }
+
+ if (arg->nll.scale_mode == CIFISP_NLL_SCALE_LOGARITHMIC)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_NLL_SEGMENTATION;
+ if (arg->rb_flt.fltsize == CIFISP_DPF_RB_FILTERSIZE_9x9)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_RB_FLTSIZE_9x9;
+ if (!arg->rb_flt.r_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_R_FLT_DIS;
+ if (!arg->rb_flt.b_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_B_FLT_DIS;
+ if (!arg->g_flt.gb_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_GB_FLT_DIS;
+ if (!arg->g_flt.gr_enable)
+ isp_dpf_mode |= CIF_ISP_DPF_MODE_GR_FLT_DIS;
+
+ isp_param_set_bits(params_vdev, CIF_ISP_DPF_MODE, isp_dpf_mode);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_b_gain,
+ CIF_ISP_DPF_NF_GAIN_B);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_r_gain,
+ CIF_ISP_DPF_NF_GAIN_R);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_gb_gain,
+ CIF_ISP_DPF_NF_GAIN_GB);
+ rkisp1_iowrite32(params_vdev, arg->gain.nf_gr_gain,
+ CIF_ISP_DPF_NF_GAIN_GR);
+
+ for (i = 0; i < CIFISP_DPF_MAX_NLF_COEFFS; i++) {
+ rkisp1_iowrite32(params_vdev, arg->nll.coeff[i],
+ CIF_ISP_DPF_NULL_COEFF_0 + i * 4);
+ }
+
+ spatial_coeff = arg->g_flt.spatial_coeff[0] |
+ (arg->g_flt.spatial_coeff[1] << 8) |
+ (arg->g_flt.spatial_coeff[2] << 16) |
+ (arg->g_flt.spatial_coeff[3] << 24);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_G_1_4);
+
+ spatial_coeff = arg->g_flt.spatial_coeff[4] |
+ (arg->g_flt.spatial_coeff[5] << 8);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_G_5_6);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[0] |
+ (arg->rb_flt.spatial_coeff[1] << 8) |
+ (arg->rb_flt.spatial_coeff[2] << 16) |
+ (arg->rb_flt.spatial_coeff[3] << 24);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_RB_1_4);
+
+ spatial_coeff = arg->rb_flt.spatial_coeff[4] |
+ (arg->rb_flt.spatial_coeff[5] << 8);
+ rkisp1_iowrite32(params_vdev, spatial_coeff,
+ CIF_ISP_DPF_S_WEIGHT_RB_5_6);
+}
+
+static void dpf_strength_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct cifisp_dpf_strength_config *arg)
+{
+ rkisp1_iowrite32(params_vdev, arg->b, CIF_ISP_DPF_STRENGTH_B);
+ rkisp1_iowrite32(params_vdev, arg->g, CIF_ISP_DPF_STRENGTH_G);
+ rkisp1_iowrite32(params_vdev, arg->r, CIF_ISP_DPF_STRENGTH_R);
+}
+
+static __maybe_unused
+void __isp_isr_other_config(struct rkisp1_isp_params_vdev *params_vdev,
+ const struct rkisp1_isp_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ if ((module_en_update & CIFISP_MODULE_DPCC) ||
+ (module_cfg_update & CIFISP_MODULE_DPCC)) {
+ /*update dpc config */
+ if ((module_cfg_update & CIFISP_MODULE_DPCC))
+ dpcc_config(params_vdev,
+ &new_params->others.dpcc_config);
+
+ if (module_en_update & CIFISP_MODULE_DPCC) {
+ if (!!(module_ens & CIFISP_MODULE_DPCC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DPCC_MODE,
+ CIF_ISP_DPCC_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DPCC_MODE,
+ CIF_ISP_DPCC_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_BLS) ||
+ (module_cfg_update & CIFISP_MODULE_BLS)) {
+ /* update bls config */
+ if ((module_cfg_update & CIFISP_MODULE_BLS))
+ bls_config(params_vdev, &new_params->others.bls_config);
+
+ if (module_en_update & CIFISP_MODULE_BLS) {
+ if (!!(module_ens & CIFISP_MODULE_BLS))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_BLS_CTRL,
+ CIF_ISP_BLS_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_BLS_CTRL,
+ CIF_ISP_BLS_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_SDG) ||
+ (module_cfg_update & CIFISP_MODULE_SDG)) {
+ /* update sdg config */
+ if ((module_cfg_update & CIFISP_MODULE_SDG))
+ sdg_config(params_vdev, &new_params->others.sdg_config);
+
+ if (module_en_update & CIFISP_MODULE_SDG) {
+ if (!!(module_ens & CIFISP_MODULE_SDG))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_LSC) ||
+ (module_cfg_update & CIFISP_MODULE_LSC)) {
+ /* update lsc config */
+ if ((module_cfg_update & CIFISP_MODULE_LSC))
+ lsc_config(params_vdev, &new_params->others.lsc_config);
+
+ if (module_en_update & CIFISP_MODULE_LSC) {
+ if (!!(module_ens & CIFISP_MODULE_LSC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AWB_GAIN) ||
+ (module_cfg_update & CIFISP_MODULE_AWB_GAIN)) {
+ /* update awb gains */
+ if ((module_cfg_update & CIFISP_MODULE_AWB_GAIN))
+ awb_gain_config(params_vdev,
+ &new_params->others.awb_gain_config);
+
+ if (module_en_update & CIFISP_MODULE_AWB_GAIN) {
+ if (!!(module_ens & CIFISP_MODULE_AWB_GAIN))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_BDM) ||
+ (module_cfg_update & CIFISP_MODULE_BDM)) {
+ /* update bdm config */
+ if ((module_cfg_update & CIFISP_MODULE_BDM))
+ bdm_config(params_vdev, &new_params->others.bdm_config);
+
+ if (module_en_update & CIFISP_MODULE_BDM) {
+ if (!!(module_ens & CIFISP_MODULE_BDM))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_FLT) ||
+ (module_cfg_update & CIFISP_MODULE_FLT)) {
+ /* update filter config */
+ if ((module_cfg_update & CIFISP_MODULE_FLT))
+ flt_config(params_vdev, &new_params->others.flt_config);
+
+ if (module_en_update & CIFISP_MODULE_FLT) {
+ if (!!(module_ens & CIFISP_MODULE_FLT))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_FILT_MODE,
+ CIF_ISP_FLT_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_FILT_MODE,
+ CIF_ISP_FLT_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_CTK) ||
+ (module_cfg_update & CIFISP_MODULE_CTK)) {
+ /* update ctk config */
+ if ((module_cfg_update & CIFISP_MODULE_CTK))
+ ctk_config(params_vdev, &new_params->others.ctk_config);
+
+ if (module_en_update & CIFISP_MODULE_CTK)
+ ctk_enable(params_vdev,
+ !!(module_ens & CIFISP_MODULE_CTK));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_GOC) ||
+ (module_cfg_update & CIFISP_MODULE_GOC)) {
+ /* update goc config */
+ if ((module_cfg_update & CIFISP_MODULE_GOC))
+ goc_config(params_vdev, &new_params->others.goc_config);
+
+ if (module_en_update & CIFISP_MODULE_GOC) {
+ if (!!(module_ens & CIFISP_MODULE_GOC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_CPROC) ||
+ (module_cfg_update & CIFISP_MODULE_CPROC)) {
+ /* update cproc config */
+ if ((module_cfg_update & CIFISP_MODULE_CPROC)) {
+ cproc_config(params_vdev,
+ &new_params->others.cproc_config);
+
+ }
+
+ if (module_en_update & CIFISP_MODULE_CPROC) {
+ if (!!(module_ens & CIFISP_MODULE_CPROC))
+ isp_param_set_bits(params_vdev,
+ CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ }
+ }
+
+
+ if ((module_en_update & CIFISP_MODULE_IE) ||
+ (module_cfg_update & CIFISP_MODULE_IE)) {
+ /* update ie config */
+ if ((module_cfg_update & CIFISP_MODULE_IE))
+ ie_config(params_vdev, &new_params->others.ie_config);
+
+ if (module_en_update & CIFISP_MODULE_IE)
+ ie_enable(params_vdev,
+ !!(module_ens & CIFISP_MODULE_IE));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_DPF) ||
+ (module_cfg_update & CIFISP_MODULE_DPF)) {
+ /* update dpf config */
+ if ((module_cfg_update & CIFISP_MODULE_DPF))
+ dpf_config(params_vdev, &new_params->others.dpf_config);
+
+ if (module_en_update & CIFISP_MODULE_DPF) {
+ if (!!(module_ens & CIFISP_MODULE_DPF))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_DPF_STRENGTH) ||
+ (module_cfg_update & CIFISP_MODULE_DPF_STRENGTH)) {
+ /* update dpf strength config */
+ dpf_strength_config(params_vdev,
+ &new_params->others.dpf_strength_config);
+ }
+}
+
+static __maybe_unused
+void __isp_isr_meas_config(struct rkisp1_isp_params_vdev *params_vdev,
+ struct rkisp1_isp_params_cfg *new_params)
+{
+ unsigned int module_en_update, module_cfg_update, module_ens;
+
+ module_en_update = new_params->module_en_update;
+ module_cfg_update = new_params->module_cfg_update;
+ module_ens = new_params->module_ens;
+
+ if ((module_en_update & CIFISP_MODULE_AWB) ||
+ (module_cfg_update & CIFISP_MODULE_AWB)) {
+ /* update awb config */
+ if ((module_cfg_update & CIFISP_MODULE_AWB))
+ awb_meas_config(params_vdev,
+ &new_params->meas.awb_meas_config);
+
+ if (module_en_update & CIFISP_MODULE_AWB)
+ awb_meas_enable(params_vdev,
+ &new_params->meas.awb_meas_config,
+ !!(module_ens & CIFISP_MODULE_AWB));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AFC) ||
+ (module_cfg_update & CIFISP_MODULE_AFC)) {
+ /* update afc config */
+ if ((module_cfg_update & CIFISP_MODULE_AFC))
+ afm_config(params_vdev, &new_params->meas.afc_config);
+
+ if (module_en_update & CIFISP_MODULE_AFC) {
+ if (!!(module_ens & CIFISP_MODULE_AFC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_AFM_CTRL,
+ CIF_ISP_AFM_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_AFM_CTRL,
+ CIF_ISP_AFM_ENA);
+ }
+ }
+
+ if ((module_en_update & CIFISP_MODULE_HST) ||
+ (module_cfg_update & CIFISP_MODULE_HST)) {
+ /* update hst config */
+ if ((module_cfg_update & CIFISP_MODULE_HST))
+ hst_config(params_vdev, &new_params->meas.hst_config);
+
+ if (module_en_update & CIFISP_MODULE_HST)
+ hst_enable(params_vdev,
+ &new_params->meas.hst_config,
+ !!(module_ens & CIFISP_MODULE_HST));
+ }
+
+ if ((module_en_update & CIFISP_MODULE_AEC) ||
+ (module_cfg_update & CIFISP_MODULE_AEC)) {
+ /* update aec config */
+ if ((module_cfg_update & CIFISP_MODULE_AEC))
+ aec_config(params_vdev, &new_params->meas.aec_config);
+
+ if (module_en_update & CIFISP_MODULE_AEC) {
+ if (!!(module_ens & CIFISP_MODULE_AEC))
+ isp_param_set_bits(params_vdev,
+ CIF_ISP_EXP_CTRL,
+ CIF_ISP_EXP_ENA);
+ else
+ isp_param_clear_bits(params_vdev,
+ CIF_ISP_EXP_CTRL,
+ CIF_ISP_EXP_ENA);
+ }
+ }
+}
+
+void rkisp1_params_isr(struct rkisp1_isp_params_vdev *params_vdev, u32 isp_mis)
+{
+ struct rkisp1_isp_params_cfg *new_params;
+ struct rkisp1_buffer *cur_buf = NULL;
+
+ spin_lock(¶ms_vdev->config_lock);
+ if (!params_vdev->streamon) {
+ spin_unlock(¶ms_vdev->config_lock);
+ return;
+ }
+
+ /* get one empty buffer */
+ if (!list_empty(¶ms_vdev->params))
+ cur_buf = list_first_entry(¶ms_vdev->params,
+ struct rkisp1_buffer, queue);
+ spin_unlock(¶ms_vdev->config_lock);
+
+ if (!cur_buf)
+ return;
+
+ new_params = (struct rkisp1_isp_params_cfg *)(cur_buf->vaddr[0]);
+
+ if (isp_mis & CIF_ISP_FRAME) {
+ __isp_isr_other_config(params_vdev, new_params);
+ __isp_isr_meas_config(params_vdev, new_params);
+ spin_lock(¶ms_vdev->config_lock);
+ list_del(&cur_buf->queue);
+ spin_unlock(¶ms_vdev->config_lock);
+ vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+}
+
+static const struct cifisp_awb_meas_config awb_params_default_config = {
+ {
+ 0, 0, RKISP1_DEFAULT_WIDTH, RKISP1_DEFAULT_HEIGHT
+ },
+ CIFISP_AWB_MODE_YCBCR, 200, 30, 20, 20, 0, 128, 128
+};
+
+static const struct cifisp_aec_config aec_params_default_config = {
+ CIFISP_EXP_MEASURING_MODE_0,
+ CIFISP_EXP_CTRL_AUTOSTOP_0,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ }
+};
+
+static const struct cifisp_hst_config hst_params_default_config = {
+ CIFISP_HISTOGRAM_MODE_RGB_COMBINED,
+ 3,
+ {
+ RKISP1_DEFAULT_WIDTH >> 2, RKISP1_DEFAULT_HEIGHT >> 2,
+ RKISP1_DEFAULT_WIDTH >> 1, RKISP1_DEFAULT_HEIGHT >> 1
+ },
+ {
+ 0, /* To be filled in with 0x01 at runtime. */
+ }
+};
+
+static const struct cifisp_afc_config afc_params_default_config = {
+ 1,
+ {
+ {
+ 300, 225, 200, 150
+ }
+ },
+ 4,
+ 14
+};
+
+static
+void rkisp1_params_config_parameter(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct cifisp_hst_config hst = hst_params_default_config;
+
+ spin_lock(¶ms_vdev->config_lock);
+
+ awb_meas_config(params_vdev, &awb_params_default_config);
+ awb_meas_enable(params_vdev, &awb_params_default_config, true);
+
+ aec_config(params_vdev, &aec_params_default_config);
+ isp_param_set_bits(params_vdev, CIF_ISP_EXP_CTRL, CIF_ISP_EXP_ENA);
+
+ afm_config(params_vdev, &afc_params_default_config);
+ isp_param_set_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+
+ memset(hst.hist_weight, 0x01, sizeof(hst.hist_weight));
+ hst_config(params_vdev, &hst);
+ isp_param_set_bits(params_vdev, CIF_ISP_HIST_PROP,
+ ~CIF_ISP_HIST_PROP_MODE_MASK |
+ hst_params_default_config.mode);
+
+ /* set the range */
+ if (params_vdev->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ csm_config(params_vdev, true);
+ else
+ csm_config(params_vdev, false);
+
+ /* override the default things */
+ __isp_isr_other_config(params_vdev, ¶ms_vdev->cur_params);
+ __isp_isr_meas_config(params_vdev, ¶ms_vdev->cur_params);
+
+ spin_unlock(¶ms_vdev->config_lock);
+}
+
+/* Not called when the camera active, thus not isr protection. */
+void rkisp1_params_configure_isp(struct rkisp1_isp_params_vdev *params_vdev,
+ struct ispsd_in_fmt *in_fmt,
+ enum v4l2_quantization quantization)
+{
+ params_vdev->quantization = quantization;
+ params_vdev->raw_type = in_fmt->bayer_pat;
+ rkisp1_params_config_parameter(params_vdev);
+}
+
+/* Not called when the camera active, thus not isr protection. */
+void rkisp1_params_disable_isp(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ isp_param_clear_bits(params_vdev, CIF_ISP_DPCC_MODE, CIF_ISP_DPCC_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_LSC_CTRL,
+ CIF_ISP_LSC_CTRL_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_BLS_CTRL, CIF_ISP_BLS_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_DEMOSAIC,
+ CIF_ISP_DEMOSAIC_BYPASS);
+ isp_param_clear_bits(params_vdev, CIF_ISP_FILT_MODE, CIF_ISP_FLT_ENA);
+ awb_meas_enable(params_vdev, NULL, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_CTRL,
+ CIF_ISP_CTRL_ISP_AWB_ENA);
+ isp_param_clear_bits(params_vdev, CIF_ISP_EXP_CTRL, CIF_ISP_EXP_ENA);
+ ctk_enable(params_vdev, false);
+ isp_param_clear_bits(params_vdev, CIF_C_PROC_CTRL,
+ CIF_C_PROC_CTR_ENABLE);
+ hst_enable(params_vdev, NULL, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_AFM_CTRL, CIF_ISP_AFM_ENA);
+ ie_enable(params_vdev, false);
+ isp_param_clear_bits(params_vdev, CIF_ISP_DPF_MODE,
+ CIF_ISP_DPF_MODE_EN);
+}
+
+static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_params_vdev *params_vdev = video_get_drvdata(video);
+
+ if (f->index > 0 || f->type != video->queue->type)
+ return -EINVAL;
+
+ f->pixelformat = params_vdev->vdev_fmt.fmt.meta.dataformat;
+
+ return 0;
+}
+
+static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_isp_params_vdev *params_vdev = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ memset(meta, 0, sizeof(*meta));
+ meta->dataformat = params_vdev->vdev_fmt.fmt.meta.dataformat;
+ meta->buffersize = params_vdev->vdev_fmt.fmt.meta.buffersize;
+
+ return 0;
+}
+
+static int rkisp1_params_querycap(struct file *file,
+ void *priv, struct v4l2_capability *cap)
+{
+ struct video_device *vdev = video_devdata(file);
+
+ strcpy(cap->driver, DRIVER_NAME);
+ strlcpy(cap->card, vdev->name, sizeof(cap->card));
+ strlcpy(cap->bus_info, "platform: " DRIVER_NAME, sizeof(cap->bus_info));
+
+ return 0;
+}
+
+/* ISP params video device IOCTLs */
+static const struct v4l2_ioctl_ops rkisp1_params_ioctl = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
+ .vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_querycap = rkisp1_params_querycap
+};
+
+static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+
+ *num_buffers = clamp_t(u32, *num_buffers,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MIN,
+ RKISP1_ISP_PARAMS_REQ_BUFS_MAX);
+
+ *num_planes = 1;
+
+ sizes[0] = sizeof(struct rkisp1_isp_params_cfg);
+
+ INIT_LIST_HEAD(¶ms_vdev->params);
+ params_vdev->first_params = true;
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *params_buf = to_rkisp1_buffer(vbuf);
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+ struct rkisp1_isp_params_cfg *new_params;
+ unsigned long flags;
+
+ if (params_vdev->first_params) {
+ new_params = (struct rkisp1_isp_params_cfg *)
+ (vb2_plane_vaddr(vb, 0));
+ vb2_buffer_done(¶ms_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ params_vdev->first_params = false;
+ params_vdev->cur_params = *new_params;
+ return;
+ }
+
+ params_buf->vaddr[0] = vb2_plane_vaddr(vb, 0);
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ list_add_tail(¶ms_buf->queue, ¶ms_vdev->params);
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+}
+
+static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
+{
+ struct rkisp1_isp_params_vdev *params_vdev = vq->drv_priv;
+ struct rkisp1_buffer *buf;
+ unsigned long flags;
+ int i;
+
+ /* stop params input firstly */
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ params_vdev->streamon = false;
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+
+ for (i = 0; i < RKISP1_ISP_PARAMS_REQ_BUFS_MAX; i++) {
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ if (!list_empty(¶ms_vdev->params)) {
+ buf = list_first_entry(¶ms_vdev->params,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ spin_unlock_irqrestore(¶ms_vdev->config_lock,
+ flags);
+ } else {
+ spin_unlock_irqrestore(¶ms_vdev->config_lock,
+ flags);
+ break;
+ }
+
+ if (buf)
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ buf = NULL;
+ }
+}
+
+static int
+rkisp1_params_vb2_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct rkisp1_isp_params_vdev *params_vdev = queue->drv_priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(¶ms_vdev->config_lock, flags);
+ params_vdev->streamon = true;
+ spin_unlock_irqrestore(¶ms_vdev->config_lock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops rkisp1_params_vb2_ops = {
+ .queue_setup = rkisp1_params_vb2_queue_setup,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .buf_queue = rkisp1_params_vb2_buf_queue,
+ .start_streaming = rkisp1_params_vb2_start_streaming,
+ .stop_streaming = rkisp1_params_vb2_stop_streaming,
+
+};
+
+struct v4l2_file_operations rkisp1_params_fops = {
+ .mmap = vb2_fop_mmap,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release
+};
+
+static int
+rkisp1_params_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = queue_to_node(q);
+
+ q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = params_vdev;
+ q->ops = &rkisp1_params_vb2_ops;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_init_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ params_vdev->vdev_fmt.fmt.meta.dataformat =
+ V4L2_META_FMT_RK_ISP1_PARAMS;
+ params_vdev->vdev_fmt.fmt.meta.buffersize =
+ sizeof(struct rkisp1_isp_params_cfg);
+}
+
+int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev)
+{
+ int ret;
+ struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ params_vdev->dev = dev;
+ mutex_init(&node->vlock);
+ spin_lock_init(¶ms_vdev->config_lock);
+
+ strlcpy(vdev->name, "rkisp1-input-params", sizeof(vdev->name));
+
+ video_set_drvdata(vdev, params_vdev);
+ vdev->ioctl_ops = &rkisp1_params_ioctl;
+ vdev->fops = &rkisp1_params_fops;
+ vdev->release = video_device_release_empty;
+ /*
+ * Provide a mutex to v4l2 core. It will be used
+ * to protect all fops and v4l2 ioctls.
+ */
+ vdev->lock = &node->vlock;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->queue = &node->buf_queue;
+ vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
+ vdev->vfl_dir = VFL_DIR_TX;
+ rkisp1_params_init_vb2_queue(vdev->queue, params_vdev);
+ rkisp1_init_params_vdev(params_vdev);
+ video_set_drvdata(vdev, params_vdev);
+
+ node->pad.flags = MEDIA_PAD_FL_SOURCE;
+ vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret < 0)
+ goto err_release_queue;
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(&vdev->dev,
+ "could not register Video for Linux device\n");
+ goto err_cleanup_media_entity;
+ }
+ return 0;
+err_cleanup_media_entity:
+ media_entity_cleanup(&vdev->entity);
+err_release_queue:
+ vb2_queue_release(vdev->queue);
+ return ret;
+}
+
+void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev)
+{
+ struct rkisp1_vdev_node *node = ¶ms_vdev->vnode;
+ struct video_device *vdev = &node->vdev;
+
+ video_unregister_device(vdev);
+ media_entity_cleanup(&vdev->entity);
+ vb2_queue_release(vdev->queue);
+}
diff --git a/drivers/media/platform/rockchip/isp1/isp_params.h b/drivers/media/platform/rockchip/isp1/isp_params.h
new file mode 100644
index 000000000000..9ecdcd21525a
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/isp_params.h
@@ -0,0 +1,76 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _RKISP1_ISP_H
+#define _RKISP1_ISP_H
+
+#include <linux/rkisp1-config.h>
+#include "common.h"
+
+/*
+ * struct rkisp1_isp_subdev - ISP input parameters device
+ *
+ * @cur_params: Current ISP parameters
+ * @first_params: the first params should take effect immediately
+ */
+struct rkisp1_isp_params_vdev {
+ struct rkisp1_vdev_node vnode;
+ struct rkisp1_device *dev;
+
+ spinlock_t config_lock;
+ struct list_head params;
+ struct rkisp1_isp_params_cfg cur_params;
+ struct v4l2_format vdev_fmt;
+ bool streamon;
+ bool first_params;
+
+ enum v4l2_quantization quantization;
+ enum rkisp1_fmt_raw_pat_type raw_type;
+};
+
+/* config params before ISP streaming */
+void rkisp1_params_configure_isp(struct rkisp1_isp_params_vdev *params_vdev,
+ struct ispsd_in_fmt *in_fmt,
+ enum v4l2_quantization quantization);
+void rkisp1_params_disable_isp(struct rkisp1_isp_params_vdev *params_vdev);
+
+int rkisp1_register_params_vdev(struct rkisp1_isp_params_vdev *params_vdev,
+ struct v4l2_device *v4l2_dev,
+ struct rkisp1_device *dev);
+
+void rkisp1_unregister_params_vdev(struct rkisp1_isp_params_vdev *params_vdev);
+
+void rkisp1_params_isr(struct rkisp1_isp_params_vdev *params_vdev, u32 isp_mis);
+
+#endif /* _RKISP1_ISP_H */
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 09/16] media: rkisp1: add rockchip isp1 core driver
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (2 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 07/16] media: rkisp1: add ISP1 params driver Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 10/16] dt-bindings: Document the Rockchip ISP1 bindings Jacob Chen
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add the core driver for rockchip isp1.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Yichong Zhong <zyc@rock-chips.com>
Signed-off-by: Jacob Chen <cc@rock-chips.com>
Signed-off-by: Eddie Cai <eddie.cai.linux@gmail.com>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
Signed-off-by: Allon Huang <allon.huang@rock-chips.com>
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
drivers/media/platform/Kconfig | 10 +
drivers/media/platform/Makefile | 1 +
drivers/media/platform/rockchip/isp1/Makefile | 8 +
drivers/media/platform/rockchip/isp1/common.h | 137 ++++++
drivers/media/platform/rockchip/isp1/dev.c | 653 ++++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/dev.h | 120 +++++
6 files changed, 929 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/Makefile
create mode 100644 drivers/media/platform/rockchip/isp1/common.h
create mode 100644 drivers/media/platform/rockchip/isp1/dev.c
create mode 100644 drivers/media/platform/rockchip/isp1/dev.h
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index fd0c99859d6f..062fffc9ffb6 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -117,6 +117,16 @@ config VIDEO_QCOM_CAMSS
select VIDEOBUF2_DMA_SG
select V4L2_FWNODE
+config VIDEO_ROCKCHIP_ISP1
+ tristate "Rockchip Image Signal Processing v1 Unit driver"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ default n
+ ---help---
+ Support for ISP1 on the rockchip SoC.
+
config VIDEO_S3C_CAMIF
tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 003b0bb2cddf..d235908df63e 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
+obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip/isp1/
obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/
obj-y += omap/
diff --git a/drivers/media/platform/rockchip/isp1/Makefile b/drivers/media/platform/rockchip/isp1/Makefile
new file mode 100644
index 000000000000..18af64853734
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += video_rkisp1.o
+video_rkisp1-objs += rkisp1.o \
+ dev.o \
+ regs.o \
+ isp_stats.o \
+ isp_params.o \
+ mipi_dphy_sy.o \
+ capture.o
diff --git a/drivers/media/platform/rockchip/isp1/common.h b/drivers/media/platform/rockchip/isp1/common.h
new file mode 100644
index 000000000000..1adfb9039b60
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/common.h
@@ -0,0 +1,137 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _RKISP1_COMMON_H
+#define _RKISP1_COMMON_H
+
+#include <linux/mutex.h>
+#include <media/media-device.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+
+#define RKISP1_DEFAULT_WIDTH 800
+#define RKISP1_DEFAULT_HEIGHT 600
+
+#define RKISP1_MAX_STREAM 2
+#define RKISP1_STREAM_SP 0
+#define RKISP1_STREAM_MP 1
+
+#define RKISP1_PLANE_Y 0
+#define RKISP1_PLANE_CB 1
+#define RKISP1_PLANE_CR 2
+
+enum rkisp1_sd_type {
+ RKISP1_SD_SENSOR,
+ RKISP1_SD_PHY_CSI,
+ RKISP1_SD_VCM,
+ RKISP1_SD_FLASH,
+ RKISP1_SD_MAX,
+};
+
+/* One structure per video node */
+struct rkisp1_vdev_node {
+ struct vb2_queue buf_queue;
+ /* vfd lock */
+ struct mutex vlock;
+ struct video_device vdev;
+ struct media_pad pad;
+};
+
+enum rkisp1_fmt_pix_type {
+ FMT_YUV,
+ FMT_RGB,
+ FMT_BAYER,
+ FMT_JPEG,
+ FMT_MAX
+};
+
+enum rkisp1_fmt_raw_pat_type {
+ RAW_RGGB = 0,
+ RAW_GRBG,
+ RAW_GBRG,
+ RAW_BGGR,
+};
+
+enum rkisp1_state {
+ /* path not yet opened: */
+ RKISP1_STATE_DISABLED,
+ /* path opened and configured, ready for streaming: */
+ RKISP1_STATE_READY,
+ /* path is streaming: */
+ RKISP1_STATE_STREAMING
+};
+
+struct rkisp1_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ union {
+ u32 buff_addr[VIDEO_MAX_PLANES];
+ void *vaddr[VIDEO_MAX_PLANES];
+ };
+};
+
+struct rkisp1_dummy_buffer {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+extern int rkisp1_debug;
+
+static inline
+struct rkisp1_vdev_node *vdev_to_node(struct video_device *vdev)
+{
+ return container_of(vdev, struct rkisp1_vdev_node, vdev);
+}
+
+static inline struct rkisp1_vdev_node *queue_to_node(struct vb2_queue *q)
+{
+ return container_of(q, struct rkisp1_vdev_node, buf_queue);
+}
+
+static inline struct rkisp1_buffer *to_rkisp1_buffer(struct vb2_v4l2_buffer *vb)
+{
+ return container_of(vb, struct rkisp1_buffer, vb);
+}
+
+static inline struct vb2_queue *to_vb2_queue(struct file *file)
+{
+ struct rkisp1_vdev_node *vnode = video_drvdata(file);
+
+ return &vnode->buf_queue;
+}
+
+#endif /* _RKISP1_COMMON_H */
diff --git a/drivers/media/platform/rockchip/isp1/dev.c b/drivers/media/platform/rockchip/isp1/dev.c
new file mode 100644
index 000000000000..248751ca103b
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/dev.c
@@ -0,0 +1,653 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/pinctrl/consumer.h>
+#include "common.h"
+#include "regs.h"
+
+struct isp_match_data {
+ const char * const *clks;
+ int size;
+};
+
+int rkisp1_debug;
+module_param_named(debug, rkisp1_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+/***************************** pipeline operations*******************************/
+
+static int __isp_pipeline_prepare(struct rkisp1_pipeline *p,
+ struct media_entity *me)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ struct v4l2_subdev *sd;
+ int i;
+
+ p->num_subdevs = 0;
+ memset(p->subdevs, 0, sizeof(p->subdevs));
+
+ while (1) {
+ struct media_pad *pad = NULL;
+
+ /* Find remote source pad */
+ for (i = 0; i < me->num_pads; i++) {
+ struct media_pad *spad = &me->pads[i];
+
+ if (!(spad->flags & MEDIA_PAD_FL_SINK))
+ continue;
+ pad = media_entity_remote_pad(spad);
+ if (pad)
+ break;
+ }
+
+ if (!pad)
+ break;
+
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ if (sd != &dev->isp_sdev.sd)
+ p->subdevs[p->num_subdevs++] = sd;
+
+ me = &sd->entity;
+ if (me->num_pads == 1)
+ break;
+ }
+ return 0;
+}
+
+static int __subdev_set_power(struct v4l2_subdev *sd, int on)
+{
+ int ret;
+
+ if (!sd)
+ return -ENXIO;
+
+ ret = v4l2_subdev_call(sd, core, s_power, on);
+
+ return ret != -ENOIOCTLCMD ? ret : 0;
+}
+
+static int __isp_pipeline_s_power(struct rkisp1_pipeline *p, bool on)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ int i, ret;
+
+ if (on) {
+ __subdev_set_power(&dev->isp_sdev.sd, true);
+
+ for (i = p->num_subdevs - 1; i >= 0; --i) {
+ ret = __subdev_set_power(p->subdevs[i], true);
+ if (ret < 0 && ret != -ENXIO)
+ goto err_power_off;
+ }
+ } else {
+ for (i = 0; i < p->num_subdevs; ++i)
+ __subdev_set_power(p->subdevs[i], false);
+
+ __subdev_set_power(&dev->isp_sdev.sd, false);
+ }
+
+ return 0;
+
+err_power_off:
+ for (++i; i < p->num_subdevs; ++i)
+ __subdev_set_power(p->subdevs[i], false);
+ __subdev_set_power(&dev->isp_sdev.sd, true);
+ return ret;
+}
+
+static int rkisp1_pipeline_open(struct rkisp1_pipeline *p,
+ struct media_entity *me,
+ bool prepare)
+{
+ int ret;
+
+ if (WARN_ON(!p || !me))
+ return -EINVAL;
+ if (atomic_inc_return(&p->power_cnt) > 1)
+ return 0;
+
+ /* go through media graphic and get subdevs */
+ if (prepare)
+ __isp_pipeline_prepare(p, me);
+
+ if (!p->num_subdevs)
+ return -EINVAL;
+
+ ret = __isp_pipeline_s_power(p, 1);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int rkisp1_pipeline_close(struct rkisp1_pipeline *p)
+{
+ int ret;
+
+ if (atomic_dec_return(&p->power_cnt) > 0)
+ return 0;
+ ret = __isp_pipeline_s_power(p, 0);
+
+ return ret == -ENXIO ? 0 : ret;
+}
+
+/*
+ * stream-on order: isp_subdev, mipi dphy, sensor
+ * stream-off order: mipi dphy, sensor, isp_subdev
+ */
+static int rkisp1_pipeline_set_stream(struct rkisp1_pipeline *p, bool on)
+{
+ struct rkisp1_device *dev = container_of(p, struct rkisp1_device, pipe);
+ int i, ret;
+
+ if ((on && atomic_inc_return(&p->stream_cnt) > 1) ||
+ (!on && atomic_dec_return(&p->stream_cnt) > 0))
+ return 0;
+
+ if (on)
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, true);
+
+ /* phy -> sensor */
+ for (i = 0; i < p->num_subdevs; ++i) {
+ ret = v4l2_subdev_call(p->subdevs[i], video, s_stream, on);
+ if (on && ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+ goto err_stream_off;
+ }
+
+ if (!on)
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, false);
+
+ return 0;
+
+err_stream_off:
+ for (--i; i >= 0; --i)
+ v4l2_subdev_call(p->subdevs[i], video, s_stream, false);
+ v4l2_subdev_call(&dev->isp_sdev.sd, video, s_stream, false);
+ return ret;
+}
+
+/***************************** media controller *******************************/
+/* See http://opensource.rock-chips.com/wiki_Rockchip-isp1 for Topology */
+
+static int rkisp1_create_links(struct rkisp1_device *dev)
+{
+ struct media_entity *source, *sink;
+ unsigned int flags, s, pad;
+ int ret;
+
+ /* sensor links(or mipi-phy) */
+ for (s = 0; s < dev->num_sensors; ++s) {
+ struct rkisp1_sensor_info *sensor = &dev->sensors[s];
+
+ for (pad = 0; pad < sensor->sd->entity.num_pads; pad++)
+ if (sensor->sd->entity.pads[pad].flags &
+ MEDIA_PAD_FL_SOURCE)
+ break;
+
+ if (pad == sensor->sd->entity.num_pads) {
+ dev_err(dev->dev,
+ "failed to find src pad for %s\n",
+ sensor->sd->name);
+
+ return -ENXIO;
+ }
+
+ ret = media_create_pad_link(
+ &sensor->sd->entity, pad,
+ &dev->isp_sdev.sd.entity,
+ RKISP1_ISP_PAD_SINK + s,
+ s ? 0 : MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(dev->dev,
+ "failed to create link for %s\n",
+ sensor->sd->name);
+ return ret;
+ }
+ }
+
+ /* params links */
+ source = &dev->params_vdev.vnode.vdev.entity;
+ sink = &dev->isp_sdev.sd.entity;
+ flags = MEDIA_LNK_FL_ENABLED;
+ ret = media_create_pad_link(source, 0, sink,
+ RKISP1_ISP_PAD_SINK_PARAMS, flags);
+ if (ret < 0)
+ return ret;
+
+ /* create isp internal links */
+ /* SP links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stream[RKISP1_STREAM_SP].vnode.vdev.entity;
+ ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_PATH,
+ sink, 0, flags);
+ if (ret < 0)
+ return ret;
+
+ /* MP links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stream[RKISP1_STREAM_MP].vnode.vdev.entity;
+ ret = media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_PATH,
+ sink, 0, flags);
+ if (ret < 0)
+ return ret;
+
+ /* 3A stats links */
+ source = &dev->isp_sdev.sd.entity;
+ sink = &dev->stats_vdev.vnode.vdev.entity;
+ return media_create_pad_link(source, RKISP1_ISP_PAD_SOURCE_STATS,
+ sink, 0, flags);
+}
+
+static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct rkisp1_device *dev;
+ int ret;
+
+ dev = container_of(notifier, struct rkisp1_device, notifier);
+
+ mutex_lock(&dev->media_dev.graph_mutex);
+ ret = rkisp1_create_links(dev);
+ if (ret < 0)
+ goto unlock;
+ ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
+ if (ret < 0)
+ goto unlock;
+
+ v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
+
+unlock:
+ mutex_unlock(&dev->media_dev.graph_mutex);
+ return ret;
+}
+
+struct rkisp1_async_subdev {
+ struct v4l2_async_subdev asd;
+ struct v4l2_mbus_config mbus;
+};
+
+static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct rkisp1_device *isp_dev = container_of(notifier,
+ struct rkisp1_device, notifier);
+ struct rkisp1_async_subdev *s_asd = container_of(asd,
+ struct rkisp1_async_subdev, asd);
+
+ if (isp_dev->num_sensors == ARRAY_SIZE(isp_dev->sensors))
+ return -EBUSY;
+
+ isp_dev->sensors[isp_dev->num_sensors].mbus = s_asd->mbus;
+ isp_dev->sensors[isp_dev->num_sensors].sd = subdev;
+ ++isp_dev->num_sensors;
+
+ v4l2_dbg(1, rkisp1_debug, subdev, "Async registered subdev\n");
+
+ return 0;
+}
+
+static int rkisp1_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct rkisp1_async_subdev *rk_asd =
+ container_of(asd, struct rkisp1_async_subdev, asd);
+ struct v4l2_fwnode_bus_parallel *bus = &vep->bus.parallel;
+
+ /*
+ * MIPI sensor is linked with a mipi dphy and its media bus config can
+ * not be get in here
+ */
+ if (vep->bus_type != V4L2_MBUS_BT656 &&
+ vep->bus_type != V4L2_MBUS_PARALLEL)
+ return 0;
+
+ rk_asd->mbus.flags = bus->flags;
+ rk_asd->mbus.type = vep->bus_type;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
+ .bound = subdev_notifier_bound,
+ .complete = subdev_notifier_complete,
+};
+
+static int isp_subdev_notifier(struct rkisp1_device *isp_dev)
+{
+ struct v4l2_async_notifier *ntf = &isp_dev->notifier;
+ struct device *dev = isp_dev->dev;
+ int ret;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints(
+ dev, ntf, sizeof(struct rkisp1_async_subdev),
+ rkisp1_fwnode_parse);
+ if (ret < 0)
+ return ret;
+
+ if (!ntf->num_subdevs)
+ return -ENODEV; /* no endpoint */
+
+ ntf->ops = &subdev_notifier_ops;
+
+ return v4l2_async_notifier_register(&isp_dev->v4l2_dev, ntf);
+}
+
+/***************************** platform deive *******************************/
+
+static int rkisp1_register_platform_subdevs(struct rkisp1_device *dev)
+{
+ int ret;
+
+ ret = rkisp1_register_isp_subdev(dev, &dev->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = rkisp1_register_stream_vdevs(dev);
+ if (ret < 0)
+ goto err_unreg_isp_subdev;
+
+ ret = rkisp1_register_stats_vdev(&dev->stats_vdev, &dev->v4l2_dev, dev);
+ if (ret < 0)
+ goto err_unreg_stream_vdev;
+
+ ret = rkisp1_register_params_vdev(&dev->params_vdev, &dev->v4l2_dev,
+ dev);
+ if (ret < 0)
+ goto err_unreg_stats_vdev;
+
+ ret = isp_subdev_notifier(dev);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to register subdev notifier(%d)\n", ret);
+ goto err_unreg_params_vdev;
+ }
+
+ return 0;
+err_unreg_params_vdev:
+ rkisp1_unregister_params_vdev(&dev->params_vdev);
+err_unreg_stats_vdev:
+ rkisp1_unregister_stats_vdev(&dev->stats_vdev);
+err_unreg_stream_vdev:
+ rkisp1_unregister_stream_vdevs(dev);
+err_unreg_isp_subdev:
+ rkisp1_unregister_isp_subdev(dev);
+ return ret;
+}
+
+static const char * const rk3399_isp_clks[] = {
+ "clk_isp",
+ "aclk_isp",
+ "hclk_isp",
+ "aclk_isp_wrap",
+ "hclk_isp_wrap",
+};
+
+static const char * const rk3288_isp_clks[] = {
+ "clk_isp",
+ "aclk_isp",
+ "hclk_isp",
+ "pclk_isp_in",
+ "sclk_isp_jpe",
+};
+
+static const struct isp_match_data rk3288_isp_clk_data = {
+ .clks = rk3288_isp_clks,
+ .size = ARRAY_SIZE(rk3288_isp_clks),
+};
+
+static const struct isp_match_data rk3399_isp_clk_data = {
+ .clks = rk3399_isp_clks,
+ .size = ARRAY_SIZE(rk3399_isp_clks),
+};
+
+static const struct of_device_id rkisp1_plat_of_match[] = {
+ {
+ .compatible = "rockchip,rk3288-cif-isp",
+ .data = &rk3288_isp_clk_data,
+ }, {
+ .compatible = "rockchip,rk3399-cif-isp",
+ .data = &rk3399_isp_clk_data,
+ },
+ {},
+};
+
+static irqreturn_t rkisp1_irq_handler(int irq, void *ctx)
+{
+ struct device *dev = ctx;
+ struct rkisp1_device *rkisp1_dev = dev_get_drvdata(dev);
+ void __iomem *base = rkisp1_dev->base_addr;
+ unsigned int mis_val, i;
+
+ mis_val = readl(rkisp1_dev->base_addr + CIF_ISP_MIS);
+ if (mis_val)
+ rkisp1_isp_isr(mis_val, rkisp1_dev);
+
+ mis_val = readl(rkisp1_dev->base_addr + CIF_MIPI_MIS);
+ if (mis_val)
+ rkisp1_mipi_isr(mis_val, rkisp1_dev);
+
+ for (i = 0; i < RKISP1_MAX_STREAM; ++i) {
+ struct rkisp1_stream *stream = &rkisp1_dev->stream[i];
+
+ if (stream->ops->is_frame_end_int_masked(base))
+ rkisp1_mi_isr(stream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void rkisp1_disable_sys_clk(struct rkisp1_device *rkisp1_dev)
+{
+ int i;
+
+ for (i = rkisp1_dev->clk_size - 1; i >= 0; i--)
+ clk_disable_unprepare(rkisp1_dev->clks[i]);
+}
+
+static int rkisp1_enable_sys_clk(struct rkisp1_device *rkisp1_dev)
+{
+ int i, ret = -EINVAL;
+
+ for (i = 0; i < rkisp1_dev->clk_size; i++) {
+ ret = clk_prepare_enable(rkisp1_dev->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+ return 0;
+err:
+ for (--i; i >= 0; --i)
+ clk_disable_unprepare(rkisp1_dev->clks[i]);
+ return ret;
+}
+
+static int rkisp1_plat_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct v4l2_device *v4l2_dev;
+ struct rkisp1_device *isp_dev;
+ const struct isp_match_data *clk_data;
+
+ struct resource *res;
+ int i, ret, irq;
+
+ match = of_match_node(rkisp1_plat_of_match, node);
+ isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
+ if (!isp_dev)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, isp_dev);
+ isp_dev->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ isp_dev->base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(isp_dev->base_addr))
+ return PTR_ERR(isp_dev->base_addr);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_irq(dev, irq, rkisp1_irq_handler, IRQF_SHARED,
+ dev_driver_string(dev), dev);
+ if (ret < 0) {
+ dev_err(dev, "request irq failed: %d\n", ret);
+ return ret;
+ }
+
+ isp_dev->irq = irq;
+ clk_data = match->data;
+ for (i = 0; i < clk_data->size; i++) {
+ struct clk *clk = devm_clk_get(dev, clk_data->clks[i]);
+
+ if (IS_ERR(clk)) {
+ dev_err(dev, "failed to get %s\n", clk_data->clks[i]);
+ return PTR_ERR(clk);
+ }
+ isp_dev->clks[i] = clk;
+ }
+ isp_dev->clk_size = clk_data->size;
+
+ atomic_set(&isp_dev->pipe.power_cnt, 0);
+ atomic_set(&isp_dev->pipe.stream_cnt, 0);
+ isp_dev->pipe.open = rkisp1_pipeline_open;
+ isp_dev->pipe.close = rkisp1_pipeline_close;
+ isp_dev->pipe.set_stream = rkisp1_pipeline_set_stream;
+
+ rkisp1_stream_init(isp_dev, RKISP1_STREAM_SP);
+ rkisp1_stream_init(isp_dev, RKISP1_STREAM_MP);
+
+ strlcpy(isp_dev->media_dev.model, "rkisp1",
+ sizeof(isp_dev->media_dev.model));
+ isp_dev->media_dev.dev = &pdev->dev;
+ media_device_init(&isp_dev->media_dev);
+
+ v4l2_dev = &isp_dev->v4l2_dev;
+ v4l2_dev->mdev = &isp_dev->media_dev;
+ strlcpy(v4l2_dev->name, "rkisp1", sizeof(v4l2_dev->name));
+ v4l2_ctrl_handler_init(&isp_dev->ctrl_handler, 5);
+ v4l2_dev->ctrl_handler = &isp_dev->ctrl_handler;
+
+ ret = v4l2_device_register(isp_dev->dev, &isp_dev->v4l2_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = media_device_register(&isp_dev->media_dev);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
+ ret);
+ goto err_unreg_v4l2_dev;
+ }
+
+ /* create & register platefom subdev (from of_node) */
+ ret = rkisp1_register_platform_subdevs(isp_dev);
+ if (ret < 0)
+ goto err_unreg_media_dev;
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err_unreg_media_dev:
+ media_device_unregister(&isp_dev->media_dev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+ return ret;
+}
+
+static int rkisp1_plat_remove(struct platform_device *pdev)
+{
+ struct rkisp1_device *isp_dev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+ media_device_unregister(&isp_dev->media_dev);
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+ rkisp1_unregister_params_vdev(&isp_dev->params_vdev);
+ rkisp1_unregister_stats_vdev(&isp_dev->stats_vdev);
+ rkisp1_unregister_stream_vdevs(isp_dev);
+ rkisp1_unregister_isp_subdev(isp_dev);
+
+ return 0;
+}
+
+static int __maybe_unused rkisp1_runtime_suspend(struct device *dev)
+{
+ struct rkisp1_device *isp_dev = dev_get_drvdata(dev);
+
+ rkisp1_disable_sys_clk(isp_dev);
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused rkisp1_runtime_resume(struct device *dev)
+{
+ struct rkisp1_device *isp_dev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ return ret;
+ rkisp1_enable_sys_clk(isp_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rkisp1_plat_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(rkisp1_runtime_suspend, rkisp1_runtime_resume, NULL)
+};
+
+static struct platform_driver rkisp1_plat_drv = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(rkisp1_plat_of_match),
+ .pm = &rkisp1_plat_pm_ops,
+ },
+ .probe = rkisp1_plat_probe,
+ .remove = rkisp1_plat_remove,
+};
+
+module_platform_driver(rkisp1_plat_drv);
+MODULE_AUTHOR("Rockchip Camera/ISP team");
+MODULE_DESCRIPTION("Rockchip ISP1 platform driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/rockchip/isp1/dev.h b/drivers/media/platform/rockchip/isp1/dev.h
new file mode 100644
index 000000000000..f28cde364b8d
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/dev.h
@@ -0,0 +1,120 @@
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _RKISP1_DEV_H
+#define _RKISP1_DEV_H
+
+#include "capture.h"
+#include "rkisp1.h"
+#include "isp_params.h"
+#include "isp_stats.h"
+
+#define DRIVER_NAME "rkisp1"
+#define ISP_VDEV_NAME DRIVER_NAME "_ispdev"
+#define SP_VDEV_NAME DRIVER_NAME "_selfpath"
+#define MP_VDEV_NAME DRIVER_NAME "_mainpath"
+#define DMA_VDEV_NAME DRIVER_NAME "_dmapath"
+
+#define GRP_ID_SENSOR BIT(0)
+#define GRP_ID_MIPIPHY BIT(1)
+#define GRP_ID_ISP BIT(2)
+#define GRP_ID_ISP_MP BIT(3)
+#define GRP_ID_ISP_SP BIT(4)
+
+#define RKISP1_MAX_BUS_CLK 8
+#define RKISP1_MAX_SENSOR 2
+#define RKISP1_MAX_PIPELINE 4
+
+/*
+ * struct rkisp1_pipeline - An ISP hardware pipeline
+ *
+ * Capture device call other devices via pipeline
+ *
+ * @num_subdevs: number of linked subdevs
+ * @power_cnt: pipeline power count
+ * @stream_cnt: stream power count
+ */
+struct rkisp1_pipeline {
+ struct media_pipeline pipe;
+ int num_subdevs;
+ atomic_t power_cnt;
+ atomic_t stream_cnt;
+ struct v4l2_subdev *subdevs[RKISP1_MAX_PIPELINE];
+ int (*open)(struct rkisp1_pipeline *p,
+ struct media_entity *me, bool prepare);
+ int (*close)(struct rkisp1_pipeline *p);
+ int (*set_stream)(struct rkisp1_pipeline *p, bool on);
+};
+
+/*
+ * struct rkisp1_sensor_info - Sensor infomations
+ * @mbus: media bus configuration
+ */
+struct rkisp1_sensor_info {
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_config mbus;
+};
+
+/*
+ * struct rkisp1_device - ISP platform device
+ * @base_addr: base register address
+ * @active_sensor: sensor in-use, set when streaming on
+ * @isp_sdev: ISP sub-device
+ * @rkisp1_stream: capture video device
+ * @stats_vdev: ISP statistics output device
+ * @params_vdev: ISP input parameters device
+ */
+struct rkisp1_device {
+ void __iomem *base_addr;
+ int irq;
+ struct device *dev;
+ struct clk *clks[RKISP1_MAX_BUS_CLK];
+ int clk_size;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct media_device media_dev;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *subdevs[RKISP1_SD_MAX];
+ struct rkisp1_sensor_info *active_sensor;
+ struct rkisp1_sensor_info sensors[RKISP1_MAX_SENSOR];
+ int num_sensors;
+ struct rkisp1_isp_subdev isp_sdev;
+ struct rkisp1_stream stream[RKISP1_MAX_STREAM];
+ struct rkisp1_isp_stats_vdev stats_vdev;
+ struct rkisp1_isp_params_vdev params_vdev;
+ struct rkisp1_pipeline pipe;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+#endif
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 10/16] dt-bindings: Document the Rockchip ISP1 bindings
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (3 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 09/16] media: rkisp1: add rockchip isp1 core driver Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-20 18:14 ` Rob Herring
2017-12-18 12:14 ` [PATCH v4 11/16] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
` (5 subsequent siblings)
10 siblings, 1 reply; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add DT bindings documentation for Rockchip ISP1
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
.../devicetree/bindings/media/rockchip-isp1.txt | 69 ++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-isp1.txt
diff --git a/Documentation/devicetree/bindings/media/rockchip-isp1.txt b/Documentation/devicetree/bindings/media/rockchip-isp1.txt
new file mode 100644
index 000000000000..4631a4b7c88a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-isp1.txt
@@ -0,0 +1,69 @@
+Rockchip SoC Image Signal Processing unit v1
+----------------------------------------------
+
+Rockchip ISP1 is the Camera interface for the Rockchip series of SoCs
+which contains image processing, scaling, and compression funcitons.
+
+Required properties:
+- compatible: value should be one of the following
+ "rockchip,rk3288-cif-isp";
+ "rockchip,rk3399-cif-isp";
+- reg : offset and length of the register set for the device.
+- interrupts: should contain ISP interrupt.
+- clocks: phandle to the required clocks.
+- clock-names: required clock name.
+- iommus: required a iommu node.
+
+port node
+-------------------
+
+The device node should contain one 'port' child node with child 'endpoint'
+nodes, according to the bindings defined in Documentation/devicetree/bindings/
+media/video-interfaces.txt.
+
+- endpoint(parallel):
+ - remote-endpoint: Connecting to a sensor with a parallel video bus.
+ - parallel_bus properties: Refer to Documentation/devicetree/bindings/
+ media/video-interfaces.txt.
+- endpoint(mipi):
+ - remote-endpoint: Connecting to Rockchip MIPI-DPHY,
+ which is defined in rockchip-mipi-dphy.txt.
+
+The port node must contain at least one endpoint, either parallel or mipi.
+It could have multiple endpoints, but please note the hardware don't support
+two sensors work at a time, they are supposed to work asynchronously.
+
+Device node example
+-------------------
+
+ isp0: isp0 at ff910000 {
+ compatible = "rockchip,rk3399-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru SCLK_ISP0>,
+ <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>,
+ <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>;
+ clock-names = "clk_isp",
+ "aclk_isp", "aclk_isp_wrap",
+ "hclk_isp", "hclk_isp_wrap";
+ power-domains = <&power RK3399_PD_ISP0>;
+ iommus = <&isp0_mmu>;
+
+ port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* mipi */
+ isp0_mipi_in: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&dphy_rx0_out>;
+ };
+
+ /* parallel */
+ isp0_parallel_in: endpoint at 1 {
+ reg = <1>;
+ remote-endpoint = <&ov5640_out>;
+ bus-width = <8>;
+ };
+ };
+ };
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 11/16] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (4 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 10/16] dt-bindings: Document the Rockchip ISP1 bindings Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-20 18:16 ` Rob Herring
2017-12-18 12:14 ` [PATCH v4 12/16] ARM: dts: rockchip: add isp node for rk3288 Jacob Chen
` (4 subsequent siblings)
10 siblings, 1 reply; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add DT bindings documentation for Rockchip MIPI D-PHY RX
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
.../bindings/media/rockchip-mipi-dphy.txt | 88 ++++++++++++++++++++++
1 file changed, 88 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
diff --git a/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt b/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
new file mode 100644
index 000000000000..0571d7f35867
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
@@ -0,0 +1,88 @@
+Rockchip SoC MIPI RX D-PHY
+-------------------------------------------------------------
+
+Required properties:
+- compatible: value should be one of the following
+ "rockchip,rk3288-mipi-dphy"
+ "rockchip,rk3399-mipi-dphy"
+- clocks : list of clock specifiers, corresponding to entries in
+ clock-names property;
+- clock-names: required clock name.
+
+MIPI RX0 D-PHY use registers in "general register files", it
+should be a child of the GRF.
+MIPI TXRX D-PHY have its own registers, it must have a reg property.
+
+Optional properties:
+- reg: offset and length of the register set for the device.
+
+port node
+-------------------
+
+The device node should contain two 'port' child nodes, according to the bindings
+defined in Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+The first port show the sensors connected in this mipi-dphy.
+- endpoint:
+ - remote-endpoint: Linked to a sensor with a MIPI CSI-2 video bus.
+ - data-lanes : (required) an array specifying active physical MIPI-CSI2
+ data input lanes and their mapping to logical lanes; the
+ D-PHY can't reroute lanes, so the array's content should
+ be consecutive and only its length is meaningful.
+
+The port node must contain at least one endpoint. It could have multiple endpoints
+linked to different sensors, but please note that they are not supposed to be
+actived at the same time.
+
+The second port should be connected to isp node.
+- endpoint:
+ - remote-endpoint: Linked to Rockchip ISP1, which is defined
+ in rockchip-isp1.txt.
+
+Device node example
+-------------------
+
+grf: syscon at ff770000 {
+ compatible = "rockchip,rk3288-grf", "syscon", "simple-mfd";
+
+...
+
+ mipi_dphy_rx0: mipi-dphy-rx0 {
+ compatible = "rockchip,rk3399-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDPHY_REF>,
+ <&cru SCLK_DPHY_RX0_CFG>,
+ <&cru PCLK_VIO_GRF>;
+ clock-names = "dphy-ref", "dphy-cfg", "grf";
+ power-domains = <&power RK3399_PD_VIO>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port at 0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_in_wcam: endpoint at 0 {
+ reg = <0>;
+ remote-endpoint = <&wcam_out>;
+ data-lanes = <1 2>;
+ };
+ mipi_in_ucam: endpoint at 1 {
+ reg = <1>;
+ remote-endpoint = <&ucam_out>;
+ data-lanes = <1>;
+ };
+ };
+
+ port at 1 {
+ reg = <1>;
+
+ dphy_rx0_out: endpoint {
+ remote-endpoint = <&isp0_mipi_in>;
+ };
+ };
+ };
+ };
+};
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 12/16] ARM: dts: rockchip: add isp node for rk3288
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (5 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 11/16] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 13/16] ARM: dts: rockchip: add rx0 mipi-phy " Jacob Chen
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
rk3288 have a Embedded 13M ISP
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index f3e7f98c2724..ed0b17d1b116 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -962,6 +962,23 @@
status = "disabled";
};
+ isp: isp at ff910000 {
+ compatible = "rockchip,rk3288-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru SCLK_ISP>, <&cru ACLK_ISP>,
+ <&cru HCLK_ISP>, <&cru PCLK_ISP_IN>,
+ <&cru SCLK_ISP_JPE>;
+ clock-names = "clk_isp", "aclk_isp",
+ "hclk_isp", "pclk_isp_in",
+ "sclk_isp_jpe";
+ assigned-clocks = <&cru SCLK_ISP>, <&cru SCLK_ISP_JPE>;
+ assigned-clock-rates = <400000000>, <400000000>;
+ power-domains = <&power RK3288_PD_VIO>;
+ iommus = <&isp_mmu>;
+ status = "disabled";
+ };
+
isp_mmu: iommu at ff914000 {
compatible = "rockchip,iommu";
reg = <0x0 0xff914000 0x0 0x100>, <0x0 0xff915000 0x0 0x100>;
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 13/16] ARM: dts: rockchip: add rx0 mipi-phy for rk3288
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (6 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 12/16] ARM: dts: rockchip: add isp node for rk3288 Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 14/16] arm64: dts: rockchip: add isp0 node for rk3399 Jacob Chen
` (2 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
It's a Designware MIPI D-PHY, used by ISP in rk3288.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm/boot/dts/rk3288.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index ed0b17d1b116..aa9ad3a6e0a5 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -864,6 +864,13 @@
status = "disabled";
};
+ mipi_phy_rx0: mipi-phy-rx0 {
+ compatible = "rockchip,rk3288-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDSI_24M>, <&cru PCLK_MIPI_CSI>;
+ clock-names = "dphy-ref", "pclk";
+ status = "disabled";
+ };
+
io_domains: io-domains {
compatible = "rockchip,rk3288-io-voltage-domain";
status = "disabled";
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 14/16] arm64: dts: rockchip: add isp0 node for rk3399
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (7 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 13/16] ARM: dts: rockchip: add rx0 mipi-phy " Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 15/16] arm64: dts: rockchip: add rx0 mipi-phy " Jacob Chen
2017-12-18 12:14 ` [PATCH v4 16/16] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Shunqian Zheng <zhengsq@rock-chips.com>
rk3399 have two ISP, but we havn't test isp1, so just add isp0 at present.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index d340b58ab184..66a912fab5dd 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1588,6 +1588,21 @@
status = "disabled";
};
+ isp0: isp0 at ff910000 {
+ compatible = "rockchip,rk3399-cif-isp";
+ reg = <0x0 0xff910000 0x0 0x4000>;
+ interrupts = <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru SCLK_ISP0>,
+ <&cru ACLK_ISP0>, <&cru ACLK_ISP0_WRAPPER>,
+ <&cru HCLK_ISP0>, <&cru HCLK_ISP0_WRAPPER>;
+ clock-names = "clk_isp",
+ "aclk_isp", "aclk_isp_wrap",
+ "hclk_isp", "hclk_isp_wrap";
+ power-domains = <&power RK3399_PD_ISP0>;
+ iommus = <&isp0_mmu>;
+ status = "disabled";
+ };
+
isp0_mmu: iommu at ff914000 {
compatible = "rockchip,iommu";
reg = <0x0 0xff914000 0x0 0x100>, <0x0 0xff915000 0x0 0x100>;
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 15/16] arm64: dts: rockchip: add rx0 mipi-phy for rk3399
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (8 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 14/16] arm64: dts: rockchip: add isp0 node for rk3399 Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
2017-12-18 12:14 ` [PATCH v4 16/16] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Shunqian Zheng <zhengsq@rock-chips.com>
It's a Designware MIPI D-PHY, used for ISP0 in rk3399.
Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
arch/arm64/boot/dts/rockchip/rk3399.dtsi | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 66a912fab5dd..8ef321f03010 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1292,6 +1292,16 @@
status = "disabled";
};
+ mipi_dphy_rx0: mipi-dphy-rx0 {
+ compatible = "rockchip,rk3399-mipi-dphy";
+ clocks = <&cru SCLK_MIPIDPHY_REF>,
+ <&cru SCLK_DPHY_RX0_CFG>,
+ <&cru PCLK_VIO_GRF>;
+ clock-names = "dphy-ref", "dphy-cfg", "grf";
+ power-domains = <&power RK3399_PD_VIO>;
+ status = "disabled";
+ };
+
u2phy0: usb2-phy at e450 {
compatible = "rockchip,rk3399-usb2phy";
reg = <0xe450 0x10>;
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v4 16/16] MAINTAINERS: add entry for Rockchip ISP1 driver
2017-12-18 12:14 [PATCH v4 04/16] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
` (9 preceding siblings ...)
2017-12-18 12:14 ` [PATCH v4 15/16] arm64: dts: rockchip: add rx0 mipi-phy " Jacob Chen
@ 2017-12-18 12:14 ` Jacob Chen
10 siblings, 0 replies; 14+ messages in thread
From: Jacob Chen @ 2017-12-18 12:14 UTC (permalink / raw)
To: linux-arm-kernel
From: Jacob Chen <jacob2.chen@rock-chips.com>
Add MAINTAINERS entry for the rockchip isp1 driver.
This driver is maintained by rockchip officially and it
will be used for rockchip SoC on all linux-kernel based OS.
Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index b05bc2c5e85c..614196ed7265 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11665,6 +11665,16 @@ F: drivers/hid/hid-roccat*
F: include/linux/hid-roccat*
F: Documentation/ABI/*/sysfs-driver-hid-roccat*
+ROCKCHIP ISP V1 DRIVER
+M: Jacob chen <jacob2.chen@rock-chips.com>
+M: Shunqian Zheng <zhengsq@rock-chips.com>
+M: Yichong Zhong <zyc@rock-chips.com>
+L: linux-media at vger.kernel.org
+S: Maintained
+F: drivers/media/platform/rockchip/isp1/
+F: Documentation/devicetree/bindings/media/rockchip-isp1.txt
+F: Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
+
ROCKCHIP RASTER 2D GRAPHIC ACCELERATION UNIT DRIVER
M: Jacob chen <jacob2.chen@rock-chips.com>
L: linux-media at vger.kernel.org
--
2.15.1
^ permalink raw reply related [flat|nested] 14+ messages in thread