* [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
@ 2018-03-08 9:47 ` Jacob Chen
2018-05-16 5:20 ` Laurent Pinchart
2019-03-10 17:49 ` Laurent Pinchart
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
` (5 subsequent siblings)
6 siblings, 2 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Tomasz Figa <tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
.../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
.../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
2 files changed, 883 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
new file mode 100644
index 000000000000..32140960557a
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define RK3288_GRF_SOC_CON6 0x025c
+#define RK3288_GRF_SOC_CON8 0x0264
+#define RK3288_GRF_SOC_CON9 0x0268
+#define RK3288_GRF_SOC_CON10 0x026c
+#define RK3288_GRF_SOC_CON14 0x027c
+#define RK3288_GRF_SOC_STATUS21 0x02d4
+#define RK3288_GRF_IO_VSEL 0x0380
+#define RK3288_GRF_SOC_CON15 0x03a4
+
+#define RK3399_GRF_SOC_CON9 0x6224
+#define RK3399_GRF_SOC_CON21 0x6254
+#define RK3399_GRF_SOC_CON22 0x6258
+#define RK3399_GRF_SOC_CON23 0x625c
+#define RK3399_GRF_SOC_CON24 0x6260
+#define RK3399_GRF_SOC_CON25 0x6264
+#define RK3399_GRF_SOC_STATUS1 0xe2a4
+
+#define CLOCK_LANE_HS_RX_CONTROL 0x34
+#define LANE0_HS_RX_CONTROL 0x44
+#define LANE1_HS_RX_CONTROL 0x54
+#define LANE2_HS_RX_CONTROL 0x84
+#define LANE3_HS_RX_CONTROL 0x94
+#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
+
+/*
+ * CSI HOST
+ */
+#define CSIHOST_PHY_TEST_CTRL0 0x30
+#define CSIHOST_PHY_TEST_CTRL1 0x34
+#define CSIHOST_PHY_SHUTDOWNZ 0x08
+#define CSIHOST_DPHY_RSTZ 0x0c
+
+#define PHY_TESTEN_ADDR (0x1 << 16)
+#define PHY_TESTEN_DATA (0x0 << 16)
+#define PHY_TESTCLK (0x1 << 1)
+#define PHY_TESTCLR (0x1 << 0)
+#define THS_SETTLE_COUNTER_THRESHOLD 0x04
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+enum mipi_dphy_sy_pads {
+ MIPI_DPHY_SY_PAD_SINK = 0,
+ MIPI_DPHY_SY_PAD_SOURCE,
+ MIPI_DPHY_SY_PADS_NUM,
+};
+
+enum dphy_reg_id {
+ GRF_DPHY_RX0_TURNDISABLE = 0,
+ GRF_DPHY_RX0_FORCERXMODE,
+ GRF_DPHY_RX0_FORCETXSTOPMODE,
+ GRF_DPHY_RX0_ENABLE,
+ GRF_DPHY_RX0_TESTCLR,
+ GRF_DPHY_RX0_TESTCLK,
+ GRF_DPHY_RX0_TESTEN,
+ GRF_DPHY_RX0_TESTDIN,
+ GRF_DPHY_RX0_TURNREQUEST,
+ GRF_DPHY_RX0_TESTDOUT,
+ GRF_DPHY_TX0_TURNDISABLE,
+ GRF_DPHY_TX0_FORCERXMODE,
+ GRF_DPHY_TX0_FORCETXSTOPMODE,
+ GRF_DPHY_TX0_TURNREQUEST,
+ GRF_DPHY_TX1RX1_TURNDISABLE,
+ GRF_DPHY_TX1RX1_FORCERXMODE,
+ GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
+ GRF_DPHY_TX1RX1_ENABLE,
+ GRF_DPHY_TX1RX1_MASTERSLAVEZ,
+ GRF_DPHY_TX1RX1_BASEDIR,
+ GRF_DPHY_TX1RX1_ENABLECLK,
+ GRF_DPHY_TX1RX1_TURNREQUEST,
+ GRF_DPHY_RX1_SRC_SEL,
+ /* rk3288 only */
+ GRF_CON_DISABLE_ISP,
+ GRF_CON_ISP_DPHY_SEL,
+ GRF_DSI_CSI_TESTBUS_SEL,
+ GRF_DVP_V18SEL,
+ /* below is for rk3399 only */
+ GRF_DPHY_RX0_CLK_INV_SEL,
+ GRF_DPHY_RX1_CLK_INV_SEL,
+};
+
+struct dphy_reg {
+ u32 offset;
+ u32 mask;
+ u32 shift;
+};
+
+#define PHY_REG(_offset, _width, _shift) \
+ { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
+
+static const struct dphy_reg rk3399_grf_dphy_regs[] = {
+ [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
+ [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
+ [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
+ [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
+ [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
+ [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
+ [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
+ [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
+ [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
+ [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
+ [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
+ [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
+ [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
+ [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
+ [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
+ [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
+ [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
+ [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
+ [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
+ [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
+ [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
+ [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
+ [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
+ [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
+ [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
+};
+
+static const struct dphy_reg rk3288_grf_dphy_regs[] = {
+ [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
+ [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
+ [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
+ [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
+ [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
+ [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
+ [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
+ [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
+ [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
+ [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
+ [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
+ [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
+ [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
+ [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
+ [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
+ [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
+ [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
+ [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
+ [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
+ [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
+ [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
+ [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
+ [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
+ [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
+ [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
+ [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
+ [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
+};
+
+struct hsfreq_range {
+ u32 range_h;
+ u8 cfg_bit;
+};
+
+struct mipidphy_priv;
+
+struct dphy_drv_data {
+ const char * const *clks;
+ int num_clks;
+ const struct hsfreq_range *hsfreq_ranges;
+ int num_hsfreq_ranges;
+ const struct dphy_reg *regs;
+};
+
+struct sensor_async_subdev {
+ struct v4l2_async_subdev asd;
+ struct v4l2_mbus_config mbus;
+ int lanes;
+};
+
+#define MAX_DPHY_CLK 8
+#define MAX_DPHY_SENSORS 2
+
+struct mipidphy_sensor {
+ struct v4l2_subdev *sd;
+ struct v4l2_mbus_config mbus;
+ int lanes;
+};
+
+struct mipidphy_priv {
+ struct device *dev;
+ struct regmap *regmap_grf;
+ const struct dphy_reg *grf_regs;
+ struct clk *clks[MAX_DPHY_CLK];
+ const struct dphy_drv_data *drv_data;
+ u64 data_rate_mbps;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev sd;
+ struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
+ struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
+ int num_sensors;
+ bool is_streaming;
+ void __iomem *txrx_base_addr;
+ int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
+};
+
+static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct mipidphy_priv, sd);
+}
+
+static inline void write_grf_reg(struct mipidphy_priv *priv,
+ int index, u8 value)
+{
+ const struct dphy_reg *reg = &priv->grf_regs[index];
+ unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
+
+ WARN_ON(!reg->offset);
+ regmap_write(priv->regmap_grf, reg->offset, val);
+}
+
+static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
+ u8 test_code, u8 test_data)
+{
+ /*
+ * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
+ * is latched internally as the current test code. Test data is
+ * programmed internally by rising edge on TESTCLK.
+ */
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+}
+
+static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
+ unsigned char data)
+{
+ /*
+ * TESTEN =1,TESTDIN=addr
+ * TESTCLK=0
+ * TESTEN =0,TESTDIN=data
+ * TESTCLK=1
+ */
+ writel((PHY_TESTEN_ADDR | addr),
+ priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
+ writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ writel((PHY_TESTEN_DATA | data),
+ priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+}
+
+static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
+{
+ struct media_pad *local, *remote;
+ struct media_entity *sensor_me;
+
+ local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
+ remote = media_entity_remote_pad(local);
+ if (!remote) {
+ v4l2_warn(sd, "No link between dphy and sensor\n");
+ return NULL;
+ }
+
+ sensor_me = media_entity_remote_pad(local)->entity;
+ return media_entity_to_v4l2_subdev(sensor_me);
+}
+
+static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ int i;
+
+ for (i = 0; i < priv->num_sensors; ++i)
+ if (priv->sensors[i].sd == sd)
+ return &priv->sensors[i];
+
+ return NULL;
+}
+
+static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
+ int ret;
+
+ link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
+ if (!link_freq) {
+ v4l2_warn(sd, "No pixel rate control in subdev\n");
+ return -EPIPE;
+ }
+
+ qm.index = v4l2_ctrl_g_ctrl(link_freq);
+ ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
+ if (ret < 0) {
+ v4l2_err(sd, "Failed to get menu item\n");
+ return ret;
+ }
+
+ if (!qm.value) {
+ v4l2_err(sd, "Invalid link_freq\n");
+ return -EINVAL;
+ }
+ priv->data_rate_mbps = qm.value * 2;
+ do_div(priv->data_rate_mbps, 1000 * 1000);
+
+ return 0;
+}
+
+static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int ret = 0;
+
+ if (priv->is_streaming)
+ return 0;
+
+ ret = mipidphy_get_sensor_data_rate(sd);
+ if (ret < 0)
+ return ret;
+
+ priv->stream_on(priv, sd);
+
+ priv->is_streaming = true;
+
+ return 0;
+}
+
+static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+
+ priv->is_streaming = false;
+
+ return 0;
+}
+
+static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
+{
+ if (on)
+ return mipidphy_s_stream_start(sd);
+ else
+ return mipidphy_s_stream_stop(sd);
+}
+
+static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
+ struct v4l2_mbus_config *config)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+
+ *config = sensor->mbus;
+
+ return 0;
+}
+
+static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+
+ if (on)
+ return pm_runtime_get_sync(priv->dev);
+ else
+ return pm_runtime_put(priv->dev);
+}
+
+static int mipidphy_runtime_suspend(struct device *dev)
+{
+ struct media_entity *me = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int i, num_clks;
+
+ num_clks = priv->drv_data->num_clks;
+ for (i = num_clks - 1; i >= 0; i--)
+ clk_disable_unprepare(priv->clks[i]);
+
+ return 0;
+}
+
+static int mipidphy_runtime_resume(struct device *dev)
+{
+ struct media_entity *me = dev_get_drvdata(dev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+ struct mipidphy_priv *priv = to_dphy_priv(sd);
+ int i, num_clks, ret;
+
+ num_clks = priv->drv_data->num_clks;
+ for (i = 0; i < num_clks; i++) {
+ ret = clk_prepare_enable(priv->clks[i]);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (--i >= 0)
+ clk_disable_unprepare(priv->clks[i]);
+ return ret;
+}
+
+/* dphy accepts all fmt/size from sensor */
+static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_subdev *sensor = get_remote_sensor(sd);
+
+ /*
+ * Do not allow format changes and just relay whatever
+ * set currently in the sensor.
+ */
+ return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
+}
+
+static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
+ .set_fmt = mipidphy_get_set_fmt,
+ .get_fmt = mipidphy_get_set_fmt,
+};
+
+static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
+ .s_power = mipidphy_s_power,
+};
+
+static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
+ .g_mbus_config = mipidphy_g_mbus_config,
+ .s_stream = mipidphy_s_stream,
+};
+
+static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
+ .core = &mipidphy_core_ops,
+ .video = &mipidphy_video_ops,
+ .pad = &mipidphy_subdev_pad_ops,
+};
+
+/* These tables must be sorted by .range_h ascending. */
+static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
+ { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
+ { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
+ { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
+ { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
+ { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
+ { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
+ { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
+ { 999, 0x1a}
+};
+
+static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
+ { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
+ { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
+ { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
+ { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
+ { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
+ { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
+ { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
+ { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
+ {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
+ {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
+};
+
+static const char * const rk3399_mipidphy_clks[] = {
+ "dphy-ref",
+ "dphy-cfg",
+ "grf",
+};
+
+static const char * const rk3288_mipidphy_clks[] = {
+ "dphy-ref",
+ "pclk",
+};
+
+static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
+ int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
+ int i, hsfreq = 0;
+
+ for (i = 0; i < num_hsfreq_ranges; i++) {
+ if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
+ hsfreq = hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
+ /* Disable lan turn around, which is ignored in receive mode */
+ write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
+ write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
+
+ write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
+
+ /* dphy start */
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
+ usleep_range(100, 150);
+ write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
+ usleep_range(100, 150);
+
+ /* set clock lane */
+ /* HS hsfreq_range & lane 0 settle bypass */
+ mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane0 */
+ mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
+ /* HS RX Control of lane1 */
+ mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane2 */
+ mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
+ /* HS RX Control of lane3 */
+ mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
+ /* HS RX Data Lanes Settle State Time Control */
+ mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
+ THS_SETTLE_COUNTER_THRESHOLD);
+
+ /* Normal operation */
+ mipidphy0_wr_reg(priv, 0x0, 0);
+
+ return 0;
+}
+
+static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
+ struct v4l2_subdev *sd)
+{
+ struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
+ const struct dphy_drv_data *drv_data = priv->drv_data;
+ const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
+ int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
+ int i, hsfreq = 0;
+
+ for (i = 0; i < num_hsfreq_ranges; i++) {
+ if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
+ hsfreq = hsfreq_ranges[i].cfg_bit;
+ break;
+ }
+ }
+ write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
+ write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
+ write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
+ /* Disable lan turn around, which is ignored in receive mode */
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
+ write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
+ GENMASK(sensor->lanes - 1, 0));
+ /* dphy start */
+ writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
+ writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ usleep_range(100, 150);
+ writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
+ usleep_range(100, 150);
+
+ /* set clock lane */
+ mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
+ mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
+ mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
+ /* HS RX Data Lanes Settle State Time Control */
+ mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
+ THS_SETTLE_COUNTER_THRESHOLD);
+
+ /* Normal operation */
+ mipidphy1_wr_reg(priv, 0x0, 0);
+
+ return 0;
+}
+
+static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
+ .clks = rk3288_mipidphy_clks,
+ .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
+ .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
+ .regs = rk3288_grf_dphy_regs,
+};
+
+static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
+ .clks = rk3399_mipidphy_clks,
+ .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
+ .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
+ .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
+ .regs = rk3399_grf_dphy_regs,
+};
+
+static const struct of_device_id rockchip_mipidphy_match_id[] = {
+ {
+ .compatible = "rockchip,rk3399-mipi-dphy",
+ .data = &rk3399_mipidphy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3288-mipi-dphy",
+ .data = &rk3288_mipidphy_drv_data,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
+
+/* The .bound() notifier callback when a match is found */
+static int
+rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct mipidphy_priv *priv = container_of(notifier,
+ struct mipidphy_priv,
+ notifier);
+ struct sensor_async_subdev *s_asd = container_of(asd,
+ struct sensor_async_subdev, asd);
+ struct mipidphy_sensor *sensor;
+ unsigned int pad, ret;
+
+ if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
+ return -EBUSY;
+
+ sensor = &priv->sensors[priv->num_sensors++];
+ sensor->lanes = s_asd->lanes;
+ sensor->mbus = s_asd->mbus;
+ sensor->sd = sd;
+
+ 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(priv->dev,
+ "failed to find src pad for %s\n",
+ sensor->sd->name);
+
+ return -ENXIO;
+ }
+
+ ret = media_create_pad_link(
+ &sensor->sd->entity, pad,
+ &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
+ priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to create link for %s\n",
+ sensor->sd->name);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* The .unbind callback */
+static void
+rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct mipidphy_priv *priv = container_of(notifier,
+ struct mipidphy_priv,
+ notifier);
+ struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
+
+ sensor->sd = NULL;
+}
+
+static const struct
+v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
+ .bound = rockchip_mipidphy_notifier_bound,
+ .unbind = rockchip_mipidphy_notifier_unbind,
+};
+
+static int rockchip_mipidphy_fwnode_parse(struct device *dev,
+ struct v4l2_fwnode_endpoint *vep,
+ struct v4l2_async_subdev *asd)
+{
+ struct sensor_async_subdev *s_asd =
+ container_of(asd, struct sensor_async_subdev, asd);
+ struct v4l2_mbus_config *config = &s_asd->mbus;
+
+ if (vep->bus_type != V4L2_MBUS_CSI2) {
+ dev_err(dev, "Only CSI2 bus type is currently supported\n");
+ return -EINVAL;
+ }
+
+ if (vep->base.port != 0) {
+ dev_err(dev, "The PHY has only port 0\n");
+ return -EINVAL;
+ }
+
+ config->type = V4L2_MBUS_CSI2;
+ config->flags = vep->bus.mipi_csi2.flags;
+ s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
+
+ switch (vep->bus.mipi_csi2.num_data_lanes) {
+ case 1:
+ config->flags |= V4L2_MBUS_CSI2_1_LANE;
+ break;
+ case 2:
+ config->flags |= V4L2_MBUS_CSI2_2_LANE;
+ break;
+ case 3:
+ config->flags |= V4L2_MBUS_CSI2_3_LANE;
+ break;
+ case 4:
+ config->flags |= V4L2_MBUS_CSI2_4_LANE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
+{
+ int ret;
+
+ priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
+ MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
+ priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+
+ ret = media_entity_pads_init(&priv->sd.entity,
+ MIPI_DPHY_SY_PADS_NUM, priv->pads);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
+ priv->dev, &priv->notifier,
+ sizeof(struct sensor_async_subdev), 0,
+ rockchip_mipidphy_fwnode_parse);
+ if (ret < 0)
+ return ret;
+
+ if (!priv->notifier.num_subdevs)
+ return -ENODEV; /* no endpoint */
+
+ priv->sd.subdev_notifier = &priv->notifier;
+ priv->notifier.ops = &rockchip_mipidphy_async_ops;
+ ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
+ if (ret) {
+ dev_err(priv->dev,
+ "failed to register async notifier : %d\n", ret);
+ v4l2_async_notifier_cleanup(&priv->notifier);
+ return ret;
+ }
+
+ return v4l2_async_register_subdev(&priv->sd);
+}
+
+static int rockchip_mipidphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct v4l2_subdev *sd;
+ struct mipidphy_priv *priv;
+ struct regmap *grf;
+ struct resource *res;
+ const struct of_device_id *of_id;
+ const struct dphy_drv_data *drv_data;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ priv->dev = dev;
+
+ of_id = of_match_device(rockchip_mipidphy_match_id, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ grf = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(grf)) {
+ grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(dev, "Can't find GRF syscon\n");
+ return -ENODEV;
+ }
+ }
+ priv->regmap_grf = grf;
+
+ drv_data = of_id->data;
+ for (i = 0; i < drv_data->num_clks; i++) {
+ priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
+
+ if (IS_ERR(priv->clks[i])) {
+ dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
+ return PTR_ERR(priv->clks[i]);
+ }
+ }
+
+ priv->grf_regs = drv_data->regs;
+ priv->drv_data = drv_data;
+ priv->stream_on = mipidphy_txrx_stream_on;
+ priv->txrx_base_addr = NULL;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->txrx_base_addr = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->txrx_base_addr))
+ priv->stream_on = mipidphy_rx_stream_on;
+
+ sd = &priv->sd;
+ v4l2_subdev_init(sd, &mipidphy_subdev_ops);
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
+ sd->dev = dev;
+
+ platform_set_drvdata(pdev, &sd->entity);
+
+ ret = rockchip_mipidphy_media_init(priv);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+}
+
+static int rockchip_mipidphy_remove(struct platform_device *pdev)
+{
+ struct media_entity *me = platform_get_drvdata(pdev);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
+
+ media_entity_cleanup(&sd->entity);
+
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
+ mipidphy_runtime_resume, NULL)
+};
+
+static struct platform_driver rockchip_isp_mipidphy_driver = {
+ .probe = rockchip_mipidphy_probe,
+ .remove = rockchip_mipidphy_remove,
+ .driver = {
+ .name = "rockchip-sy-mipi-dphy",
+ .pm = &rockchip_mipidphy_pm_ops,
+ .of_match_table = rockchip_mipidphy_match_id,
+ },
+};
+
+module_platform_driver(rockchip_isp_mipidphy_driver);
+MODULE_AUTHOR("Rockchip Camera/ISP team");
+MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
new file mode 100644
index 000000000000..c558791064a2
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip MIPI Synopsys DPHY driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef __MIPI_DPHY_SY_H__
+#define __MIPI_DPHY_SY_H__
+
+#include <media/v4l2-subdev.h>
+
+void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
+
+#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
2.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
@ 2018-05-16 5:20 ` Laurent Pinchart
2018-05-16 14:39 ` Jacob Chen
2019-03-10 17:49 ` Laurent Pinchart
1 sibling, 1 reply; 51+ messages in thread
From: Laurent Pinchart @ 2018-05-16 5:20 UTC (permalink / raw)
To: Jacob Chen
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, mchehab,
linux-media, sakari.ailus, hans.verkuil, tfiga, zhengsq, zyc,
eddie.cai.linux, jeffy.chen, devicetree, heiko, Jacob Chen
Hi Jacob,
Thank you for the patch.
On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
Should this really be a subdev driver ? After a quick look at the code, the
only parameters you need to configure the PHY is the number of lanes and the
data rate. Implementing the whole subdev API seems overcomplicated to me,
especially given that the D-PHY doesn't deal with video streams as such, but
operates one level down. Shouldn't we model the D-PHY using the Linux PHY
framework ? I believe all the features you need are there except for a D-PHY-
specific configuration function that should be very easy to add.
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> 2 files changed, 883 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
> index 000000000000..32140960557a
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -0,0 +1,868 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define RK3288_GRF_SOC_CON6 0x025c
> +#define RK3288_GRF_SOC_CON8 0x0264
> +#define RK3288_GRF_SOC_CON9 0x0268
> +#define RK3288_GRF_SOC_CON10 0x026c
> +#define RK3288_GRF_SOC_CON14 0x027c
> +#define RK3288_GRF_SOC_STATUS21 0x02d4
> +#define RK3288_GRF_IO_VSEL 0x0380
> +#define RK3288_GRF_SOC_CON15 0x03a4
> +
> +#define RK3399_GRF_SOC_CON9 0x6224
> +#define RK3399_GRF_SOC_CON21 0x6254
> +#define RK3399_GRF_SOC_CON22 0x6258
> +#define RK3399_GRF_SOC_CON23 0x625c
> +#define RK3399_GRF_SOC_CON24 0x6260
> +#define RK3399_GRF_SOC_CON25 0x6264
> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> +
> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> +#define LANE0_HS_RX_CONTROL 0x44
> +#define LANE1_HS_RX_CONTROL 0x54
> +#define LANE2_HS_RX_CONTROL 0x84
> +#define LANE3_HS_RX_CONTROL 0x94
> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> +
> +/*
> + * CSI HOST
> + */
> +#define CSIHOST_PHY_TEST_CTRL0 0x30
> +#define CSIHOST_PHY_TEST_CTRL1 0x34
> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> +#define CSIHOST_DPHY_RSTZ 0x0c
> +
> +#define PHY_TESTEN_ADDR (0x1 << 16)
> +#define PHY_TESTEN_DATA (0x0 << 16)
> +#define PHY_TESTCLK (0x1 << 1)
> +#define PHY_TESTCLR (0x1 << 0)
> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> +
> +#define HIWORD_UPDATE(val, mask, shift) \
> + ((val) << (shift) | (mask) << ((shift) + 16))
> +
> +enum mipi_dphy_sy_pads {
> + MIPI_DPHY_SY_PAD_SINK = 0,
> + MIPI_DPHY_SY_PAD_SOURCE,
> + MIPI_DPHY_SY_PADS_NUM,
> +};
> +
> +enum dphy_reg_id {
> + GRF_DPHY_RX0_TURNDISABLE = 0,
> + GRF_DPHY_RX0_FORCERXMODE,
> + GRF_DPHY_RX0_FORCETXSTOPMODE,
> + GRF_DPHY_RX0_ENABLE,
> + GRF_DPHY_RX0_TESTCLR,
> + GRF_DPHY_RX0_TESTCLK,
> + GRF_DPHY_RX0_TESTEN,
> + GRF_DPHY_RX0_TESTDIN,
> + GRF_DPHY_RX0_TURNREQUEST,
> + GRF_DPHY_RX0_TESTDOUT,
> + GRF_DPHY_TX0_TURNDISABLE,
> + GRF_DPHY_TX0_FORCERXMODE,
> + GRF_DPHY_TX0_FORCETXSTOPMODE,
> + GRF_DPHY_TX0_TURNREQUEST,
> + GRF_DPHY_TX1RX1_TURNDISABLE,
> + GRF_DPHY_TX1RX1_FORCERXMODE,
> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> + GRF_DPHY_TX1RX1_ENABLE,
> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> + GRF_DPHY_TX1RX1_BASEDIR,
> + GRF_DPHY_TX1RX1_ENABLECLK,
> + GRF_DPHY_TX1RX1_TURNREQUEST,
> + GRF_DPHY_RX1_SRC_SEL,
> + /* rk3288 only */
> + GRF_CON_DISABLE_ISP,
> + GRF_CON_ISP_DPHY_SEL,
> + GRF_DSI_CSI_TESTBUS_SEL,
> + GRF_DVP_V18SEL,
> + /* below is for rk3399 only */
> + GRF_DPHY_RX0_CLK_INV_SEL,
> + GRF_DPHY_RX1_CLK_INV_SEL,
> +};
> +
> +struct dphy_reg {
> + u32 offset;
> + u32 mask;
> + u32 shift;
> +};
> +
> +#define PHY_REG(_offset, _width, _shift) \
> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> +
> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> +};
> +
> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> +};
> +
> +struct hsfreq_range {
> + u32 range_h;
> + u8 cfg_bit;
> +};
> +
> +struct mipidphy_priv;
> +
> +struct dphy_drv_data {
> + const char * const *clks;
> + int num_clks;
> + const struct hsfreq_range *hsfreq_ranges;
> + int num_hsfreq_ranges;
> + const struct dphy_reg *regs;
> +};
> +
> +struct sensor_async_subdev {
> + struct v4l2_async_subdev asd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +#define MAX_DPHY_CLK 8
> +#define MAX_DPHY_SENSORS 2
> +
> +struct mipidphy_sensor {
> + struct v4l2_subdev *sd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +struct mipidphy_priv {
> + struct device *dev;
> + struct regmap *regmap_grf;
> + const struct dphy_reg *grf_regs;
> + struct clk *clks[MAX_DPHY_CLK];
> + const struct dphy_drv_data *drv_data;
> + u64 data_rate_mbps;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
> + int num_sensors;
> + bool is_streaming;
> + void __iomem *txrx_base_addr;
> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> +};
> +
> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
> *subdev) +{
> + return container_of(subdev, struct mipidphy_priv, sd);
> +}
> +
> +static inline void write_grf_reg(struct mipidphy_priv *priv,
> + int index, u8 value)
> +{
> + const struct dphy_reg *reg = &priv->grf_regs[index];
> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> +
> + WARN_ON(!reg->offset);
> + regmap_write(priv->regmap_grf, reg->offset, val);
> +}
> +
> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> + u8 test_code, u8 test_data)
> +{
> + /*
> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> + * is latched internally as the current test code. Test data is
> + * programmed internally by rising edge on TESTCLK.
> + */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> +}
> +
> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
> addr, + unsigned char data)
> +{
> + /*
> + * TESTEN =1,TESTDIN=addr
> + * TESTCLK=0
> + * TESTEN =0,TESTDIN=data
> + * TESTCLK=1
> + */
> + writel((PHY_TESTEN_ADDR | addr),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel((PHY_TESTEN_DATA | data),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> +}
> +
> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> +{
> + struct media_pad *local, *remote;
> + struct media_entity *sensor_me;
> +
> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> + remote = media_entity_remote_pad(local);
> + if (!remote) {
> + v4l2_warn(sd, "No link between dphy and sensor\n");
> + return NULL;
> + }
> +
> + sensor_me = media_entity_remote_pad(local)->entity;
> + return media_entity_to_v4l2_subdev(sensor_me);
> +}
> +
> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + int i;
> +
> + for (i = 0; i < priv->num_sensors; ++i)
> + if (priv->sensors[i].sd == sd)
> + return &priv->sensors[i];
> +
> + return NULL;
> +}
> +
> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> + int ret;
> +
> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
> + if (!link_freq) {
> + v4l2_warn(sd, "No pixel rate control in subdev\n");
> + return -EPIPE;
> + }
> +
> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> + if (ret < 0) {
> + v4l2_err(sd, "Failed to get menu item\n");
> + return ret;
> + }
> +
> + if (!qm.value) {
> + v4l2_err(sd, "Invalid link_freq\n");
> + return -EINVAL;
> + }
> + priv->data_rate_mbps = qm.value * 2;
> + do_div(priv->data_rate_mbps, 1000 * 1000);
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int ret = 0;
> +
> + if (priv->is_streaming)
> + return 0;
> +
> + ret = mipidphy_get_sensor_data_rate(sd);
> + if (ret < 0)
> + return ret;
> +
> + priv->stream_on(priv, sd);
> +
> + priv->is_streaming = true;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + priv->is_streaming = false;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> +{
> + if (on)
> + return mipidphy_s_stream_start(sd);
> + else
> + return mipidphy_s_stream_stop(sd);
> +}
> +
> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> + struct v4l2_mbus_config *config)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> +
> + *config = sensor->mbus;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + if (on)
> + return pm_runtime_get_sync(priv->dev);
> + else
> + return pm_runtime_put(priv->dev);
> +}
> +
> +static int mipidphy_runtime_suspend(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = num_clks - 1; i >= 0; i--)
> + clk_disable_unprepare(priv->clks[i]);
> +
> + return 0;
> +}
> +
> +static int mipidphy_runtime_resume(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks, ret;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = 0; i < num_clks; i++) {
> + ret = clk_prepare_enable(priv->clks[i]);
> + if (ret < 0)
> + goto err;
> + }
> +
> + return 0;
> +err:
> + while (--i >= 0)
> + clk_disable_unprepare(priv->clks[i]);
> + return ret;
> +}
> +
> +/* dphy accepts all fmt/size from sensor */
> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> +
> + /*
> + * Do not allow format changes and just relay whatever
> + * set currently in the sensor.
> + */
> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
> +}
> +
> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> + .set_fmt = mipidphy_get_set_fmt,
> + .get_fmt = mipidphy_get_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> + .s_power = mipidphy_s_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> + .g_mbus_config = mipidphy_g_mbus_config,
> + .s_stream = mipidphy_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> + .core = &mipidphy_core_ops,
> + .video = &mipidphy_video_ops,
> + .pad = &mipidphy_subdev_pad_ops,
> +};
> +
> +/* These tables must be sorted by .range_h ascending. */
> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}
> +};
> +
> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> +};
> +
> +static const char * const rk3399_mipidphy_clks[] = {
> + "dphy-ref",
> + "dphy-cfg",
> + "grf",
> +};
> +
> +static const char * const rk3288_mipidphy_clks[] = {
> + "dphy-ref",
> + "pclk",
> +};
> +
> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> +
> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> +
> + /* dphy start */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> + usleep_range(100, 150);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + /* HS hsfreq_range & lane 0 settle bypass */
> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane0 */
> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + /* HS RX Control of lane1 */
> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane2 */
> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane3 */
> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy0_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> + GENMASK(sensor->lanes - 1, 0));
> + /* dphy start */
> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy1_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> + .clks = rk3288_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> + .regs = rk3288_grf_dphy_regs,
> +};
> +
> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> + .clks = rk3399_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> + .regs = rk3399_grf_dphy_regs,
> +};
> +
> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> + {
> + .compatible = "rockchip,rk3399-mipi-dphy",
> + .data = &rk3399_mipidphy_drv_data,
> + },
> + {
> + .compatible = "rockchip,rk3288-mipi-dphy",
> + .data = &rk3288_mipidphy_drv_data,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
> +
> +/* The .bound() notifier callback when a match is found */
> +static int
> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct sensor_async_subdev *s_asd = container_of(asd,
> + struct sensor_async_subdev, asd);
> + struct mipidphy_sensor *sensor;
> + unsigned int pad, ret;
> +
> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> + return -EBUSY;
> +
> + sensor = &priv->sensors[priv->num_sensors++];
> + sensor->lanes = s_asd->lanes;
> + sensor->mbus = s_asd->mbus;
> + sensor->sd = sd;
> +
> + 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(priv->dev,
> + "failed to find src pad for %s\n",
> + sensor->sd->name);
> +
> + return -ENXIO;
> + }
> +
> + ret = media_create_pad_link(
> + &sensor->sd->entity, pad,
> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to create link for %s\n",
> + sensor->sd->name);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* The .unbind callback */
> +static void
> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> +
> + sensor->sd = NULL;
> +}
> +
> +static const struct
> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> + .bound = rockchip_mipidphy_notifier_bound,
> + .unbind = rockchip_mipidphy_notifier_unbind,
> +};
> +
> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> + struct v4l2_fwnode_endpoint *vep,
> + struct v4l2_async_subdev *asd)
> +{
> + struct sensor_async_subdev *s_asd =
> + container_of(asd, struct sensor_async_subdev, asd);
> + struct v4l2_mbus_config *config = &s_asd->mbus;
> +
> + if (vep->bus_type != V4L2_MBUS_CSI2) {
> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> + return -EINVAL;
> + }
> +
> + if (vep->base.port != 0) {
> + dev_err(dev, "The PHY has only port 0\n");
> + return -EINVAL;
> + }
> +
> + config->type = V4L2_MBUS_CSI2;
> + config->flags = vep->bus.mipi_csi2.flags;
> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> +
> + switch (vep->bus.mipi_csi2.num_data_lanes) {
> + case 1:
> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> + break;
> + case 2:
> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> + break;
> + case 3:
> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> + break;
> + case 4:
> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> +{
> + int ret;
> +
> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> +
> + ret = media_entity_pads_init(&priv->sd.entity,
> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> + if (ret < 0)
> + return ret;
> +
> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> + priv->dev, &priv->notifier,
> + sizeof(struct sensor_async_subdev), 0,
> + rockchip_mipidphy_fwnode_parse);
> + if (ret < 0)
> + return ret;
> +
> + if (!priv->notifier.num_subdevs)
> + return -ENODEV; /* no endpoint */
> +
> + priv->sd.subdev_notifier = &priv->notifier;
> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to register async notifier : %d\n", ret);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + return v4l2_async_register_subdev(&priv->sd);
> +}
> +
> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct v4l2_subdev *sd;
> + struct mipidphy_priv *priv;
> + struct regmap *grf;
> + struct resource *res;
> + const struct of_device_id *of_id;
> + const struct dphy_drv_data *drv_data;
> + int i, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->dev = dev;
> +
> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + grf = syscon_node_to_regmap(dev->parent->of_node);
> + if (IS_ERR(grf)) {
> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(grf)) {
> + dev_err(dev, "Can't find GRF syscon\n");
> + return -ENODEV;
> + }
> + }
> + priv->regmap_grf = grf;
> +
> + drv_data = of_id->data;
> + for (i = 0; i < drv_data->num_clks; i++) {
> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> +
> + if (IS_ERR(priv->clks[i])) {
> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> + return PTR_ERR(priv->clks[i]);
> + }
> + }
> +
> + priv->grf_regs = drv_data->regs;
> + priv->drv_data = drv_data;
> + priv->stream_on = mipidphy_txrx_stream_on;
> + priv->txrx_base_addr = NULL;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
> + if (IS_ERR(priv->txrx_base_addr))
> + priv->stream_on = mipidphy_rx_stream_on;
> +
> + sd = &priv->sd;
> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> + sd->dev = dev;
> +
> + platform_set_drvdata(pdev, &sd->entity);
> +
> + ret = rockchip_mipidphy_media_init(priv);
> + if (ret < 0)
> + return ret;
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> +{
> + struct media_entity *me = platform_get_drvdata(pdev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> +
> + media_entity_cleanup(&sd->entity);
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> + mipidphy_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver rockchip_isp_mipidphy_driver = {
> + .probe = rockchip_mipidphy_probe,
> + .remove = rockchip_mipidphy_remove,
> + .driver = {
> + .name = "rockchip-sy-mipi-dphy",
> + .pm = &rockchip_mipidphy_pm_ops,
> + .of_match_table = rockchip_mipidphy_match_id,
> + },
> +};
> +
> +module_platform_driver(rockchip_isp_mipidphy_driver);
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
> index 000000000000..c558791064a2
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef __MIPI_DPHY_SY_H__
> +#define __MIPI_DPHY_SY_H__
> +
> +#include <media/v4l2-subdev.h>
> +
> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
> +
> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 5:20 ` Laurent Pinchart
@ 2018-05-16 14:39 ` Jacob Chen
2018-05-16 14:53 ` Jacob Chen
0 siblings, 1 reply; 51+ messages in thread
From: Jacob Chen @ 2018-05-16 14:39 UTC (permalink / raw)
To: Laurent Pinchart
Cc: open list:ARM/Rockchip SoC...,
linux-kernel, linux-arm-kernel, Mauro Carvalho Chehab,
Linux Media Mailing List, Sakari Ailus, Hans Verkuil,
Tomasz Figa, Shunqian Zheng, 钟以崇,
Eddie Cai, Jeffy Chen, devicetree, Heiko Stuebner, Xing Zheng
Hi Laurent,
2018-05-16 13:20 GMT+08:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
> Hi Jacob,
>
> Thank you for the patch.
>
> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
>> From: Jacob Chen <jacob2.chen@rock-chips.com>
>>
>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>
> Should this really be a subdev driver ? After a quick look at the code, the
> only parameters you need to configure the PHY is the number of lanes and the
> data rate. Implementing the whole subdev API seems overcomplicated to me,
> especially given that the D-PHY doesn't deal with video streams as such, but
> operates one level down. Shouldn't we model the D-PHY using the Linux PHY
> framework ? I believe all the features you need are there except for a D-PHY-
> specific configuration function that should be very easy to add.
>
It deserves a subdev driver since the ISP is not the only user.
Other driver, like VIP, use it too.
>> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
>> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
>> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
>> ---
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
>> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>> 2 files changed, 883 insertions(+)
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
>> index 000000000000..32140960557a
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>> @@ -0,0 +1,868 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-fwnode.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define RK3288_GRF_SOC_CON6 0x025c
>> +#define RK3288_GRF_SOC_CON8 0x0264
>> +#define RK3288_GRF_SOC_CON9 0x0268
>> +#define RK3288_GRF_SOC_CON10 0x026c
>> +#define RK3288_GRF_SOC_CON14 0x027c
>> +#define RK3288_GRF_SOC_STATUS21 0x02d4
>> +#define RK3288_GRF_IO_VSEL 0x0380
>> +#define RK3288_GRF_SOC_CON15 0x03a4
>> +
>> +#define RK3399_GRF_SOC_CON9 0x6224
>> +#define RK3399_GRF_SOC_CON21 0x6254
>> +#define RK3399_GRF_SOC_CON22 0x6258
>> +#define RK3399_GRF_SOC_CON23 0x625c
>> +#define RK3399_GRF_SOC_CON24 0x6260
>> +#define RK3399_GRF_SOC_CON25 0x6264
>> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
>> +
>> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
>> +#define LANE0_HS_RX_CONTROL 0x44
>> +#define LANE1_HS_RX_CONTROL 0x54
>> +#define LANE2_HS_RX_CONTROL 0x84
>> +#define LANE3_HS_RX_CONTROL 0x94
>> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
>> +
>> +/*
>> + * CSI HOST
>> + */
>> +#define CSIHOST_PHY_TEST_CTRL0 0x30
>> +#define CSIHOST_PHY_TEST_CTRL1 0x34
>> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
>> +#define CSIHOST_DPHY_RSTZ 0x0c
>> +
>> +#define PHY_TESTEN_ADDR (0x1 << 16)
>> +#define PHY_TESTEN_DATA (0x0 << 16)
>> +#define PHY_TESTCLK (0x1 << 1)
>> +#define PHY_TESTCLR (0x1 << 0)
>> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
>> +
>> +#define HIWORD_UPDATE(val, mask, shift) \
>> + ((val) << (shift) | (mask) << ((shift) + 16))
>> +
>> +enum mipi_dphy_sy_pads {
>> + MIPI_DPHY_SY_PAD_SINK = 0,
>> + MIPI_DPHY_SY_PAD_SOURCE,
>> + MIPI_DPHY_SY_PADS_NUM,
>> +};
>> +
>> +enum dphy_reg_id {
>> + GRF_DPHY_RX0_TURNDISABLE = 0,
>> + GRF_DPHY_RX0_FORCERXMODE,
>> + GRF_DPHY_RX0_FORCETXSTOPMODE,
>> + GRF_DPHY_RX0_ENABLE,
>> + GRF_DPHY_RX0_TESTCLR,
>> + GRF_DPHY_RX0_TESTCLK,
>> + GRF_DPHY_RX0_TESTEN,
>> + GRF_DPHY_RX0_TESTDIN,
>> + GRF_DPHY_RX0_TURNREQUEST,
>> + GRF_DPHY_RX0_TESTDOUT,
>> + GRF_DPHY_TX0_TURNDISABLE,
>> + GRF_DPHY_TX0_FORCERXMODE,
>> + GRF_DPHY_TX0_FORCETXSTOPMODE,
>> + GRF_DPHY_TX0_TURNREQUEST,
>> + GRF_DPHY_TX1RX1_TURNDISABLE,
>> + GRF_DPHY_TX1RX1_FORCERXMODE,
>> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
>> + GRF_DPHY_TX1RX1_ENABLE,
>> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
>> + GRF_DPHY_TX1RX1_BASEDIR,
>> + GRF_DPHY_TX1RX1_ENABLECLK,
>> + GRF_DPHY_TX1RX1_TURNREQUEST,
>> + GRF_DPHY_RX1_SRC_SEL,
>> + /* rk3288 only */
>> + GRF_CON_DISABLE_ISP,
>> + GRF_CON_ISP_DPHY_SEL,
>> + GRF_DSI_CSI_TESTBUS_SEL,
>> + GRF_DVP_V18SEL,
>> + /* below is for rk3399 only */
>> + GRF_DPHY_RX0_CLK_INV_SEL,
>> + GRF_DPHY_RX1_CLK_INV_SEL,
>> +};
>> +
>> +struct dphy_reg {
>> + u32 offset;
>> + u32 mask;
>> + u32 shift;
>> +};
>> +
>> +#define PHY_REG(_offset, _width, _shift) \
>> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
>> +
>> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
>> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
>> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
>> +};
>> +
>> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
>> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
>> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
>> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
>> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
>> +};
>> +
>> +struct hsfreq_range {
>> + u32 range_h;
>> + u8 cfg_bit;
>> +};
>> +
>> +struct mipidphy_priv;
>> +
>> +struct dphy_drv_data {
>> + const char * const *clks;
>> + int num_clks;
>> + const struct hsfreq_range *hsfreq_ranges;
>> + int num_hsfreq_ranges;
>> + const struct dphy_reg *regs;
>> +};
>> +
>> +struct sensor_async_subdev {
>> + struct v4l2_async_subdev asd;
>> + struct v4l2_mbus_config mbus;
>> + int lanes;
>> +};
>> +
>> +#define MAX_DPHY_CLK 8
>> +#define MAX_DPHY_SENSORS 2
>> +
>> +struct mipidphy_sensor {
>> + struct v4l2_subdev *sd;
>> + struct v4l2_mbus_config mbus;
>> + int lanes;
>> +};
>> +
>> +struct mipidphy_priv {
>> + struct device *dev;
>> + struct regmap *regmap_grf;
>> + const struct dphy_reg *grf_regs;
>> + struct clk *clks[MAX_DPHY_CLK];
>> + const struct dphy_drv_data *drv_data;
>> + u64 data_rate_mbps;
>> + struct v4l2_async_notifier notifier;
>> + struct v4l2_subdev sd;
>> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
>> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>> + int num_sensors;
>> + bool is_streaming;
>> + void __iomem *txrx_base_addr;
>> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
>> +};
>> +
>> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
>> *subdev) +{
>> + return container_of(subdev, struct mipidphy_priv, sd);
>> +}
>> +
>> +static inline void write_grf_reg(struct mipidphy_priv *priv,
>> + int index, u8 value)
>> +{
>> + const struct dphy_reg *reg = &priv->grf_regs[index];
>> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
>> +
>> + WARN_ON(!reg->offset);
>> + regmap_write(priv->regmap_grf, reg->offset, val);
>> +}
>> +
>> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
>> + u8 test_code, u8 test_data)
>> +{
>> + /*
>> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
>> + * is latched internally as the current test code. Test data is
>> + * programmed internally by rising edge on TESTCLK.
>> + */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> +}
>> +
>> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
>> addr, + unsigned char data)
>> +{
>> + /*
>> + * TESTEN =1,TESTDIN=addr
>> + * TESTCLK=0
>> + * TESTEN =0,TESTDIN=data
>> + * TESTCLK=1
>> + */
>> + writel((PHY_TESTEN_ADDR | addr),
>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + writel((PHY_TESTEN_DATA | data),
>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> +}
>> +
>> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
>> +{
>> + struct media_pad *local, *remote;
>> + struct media_entity *sensor_me;
>> +
>> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
>> + remote = media_entity_remote_pad(local);
>> + if (!remote) {
>> + v4l2_warn(sd, "No link between dphy and sensor\n");
>> + return NULL;
>> + }
>> +
>> + sensor_me = media_entity_remote_pad(local)->entity;
>> + return media_entity_to_v4l2_subdev(sensor_me);
>> +}
>> +
>> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < priv->num_sensors; ++i)
>> + if (priv->sensors[i].sd == sd)
>> + return &priv->sensors[i];
>> +
>> + return NULL;
>> +}
>> +
>> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct v4l2_ctrl *link_freq;
>> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
>> + int ret;
>> +
>> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>> + if (!link_freq) {
>> + v4l2_warn(sd, "No pixel rate control in subdev\n");
>> + return -EPIPE;
>> + }
>> +
>> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
>> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
>> + if (ret < 0) {
>> + v4l2_err(sd, "Failed to get menu item\n");
>> + return ret;
>> + }
>> +
>> + if (!qm.value) {
>> + v4l2_err(sd, "Invalid link_freq\n");
>> + return -EINVAL;
>> + }
>> + priv->data_rate_mbps = qm.value * 2;
>> + do_div(priv->data_rate_mbps, 1000 * 1000);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int ret = 0;
>> +
>> + if (priv->is_streaming)
>> + return 0;
>> +
>> + ret = mipidphy_get_sensor_data_rate(sd);
>> + if (ret < 0)
>> + return ret;
>> +
>> + priv->stream_on(priv, sd);
>> +
>> + priv->is_streaming = true;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> + priv->is_streaming = false;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> + if (on)
>> + return mipidphy_s_stream_start(sd);
>> + else
>> + return mipidphy_s_stream_stop(sd);
>> +}
>> +
>> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
>> + struct v4l2_mbus_config *config)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> +
>> + *config = sensor->mbus;
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> +
>> + if (on)
>> + return pm_runtime_get_sync(priv->dev);
>> + else
>> + return pm_runtime_put(priv->dev);
>> +}
>> +
>> +static int mipidphy_runtime_suspend(struct device *dev)
>> +{
>> + struct media_entity *me = dev_get_drvdata(dev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int i, num_clks;
>> +
>> + num_clks = priv->drv_data->num_clks;
>> + for (i = num_clks - 1; i >= 0; i--)
>> + clk_disable_unprepare(priv->clks[i]);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_runtime_resume(struct device *dev)
>> +{
>> + struct media_entity *me = dev_get_drvdata(dev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>> + int i, num_clks, ret;
>> +
>> + num_clks = priv->drv_data->num_clks;
>> + for (i = 0; i < num_clks; i++) {
>> + ret = clk_prepare_enable(priv->clks[i]);
>> + if (ret < 0)
>> + goto err;
>> + }
>> +
>> + return 0;
>> +err:
>> + while (--i >= 0)
>> + clk_disable_unprepare(priv->clks[i]);
>> + return ret;
>> +}
>> +
>> +/* dphy accepts all fmt/size from sensor */
>> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_pad_config *cfg,
>> + struct v4l2_subdev_format *fmt)
>> +{
>> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
>> +
>> + /*
>> + * Do not allow format changes and just relay whatever
>> + * set currently in the sensor.
>> + */
>> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>> +}
>> +
>> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
>> + .set_fmt = mipidphy_get_set_fmt,
>> + .get_fmt = mipidphy_get_set_fmt,
>> +};
>> +
>> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
>> + .s_power = mipidphy_s_power,
>> +};
>> +
>> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
>> + .g_mbus_config = mipidphy_g_mbus_config,
>> + .s_stream = mipidphy_s_stream,
>> +};
>> +
>> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
>> + .core = &mipidphy_core_ops,
>> + .video = &mipidphy_video_ops,
>> + .pad = &mipidphy_subdev_pad_ops,
>> +};
>> +
>> +/* These tables must be sorted by .range_h ascending. */
>> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> + { 999, 0x1a}
>> +};
>> +
>> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
>> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
>> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
>> +};
>> +
>> +static const char * const rk3399_mipidphy_clks[] = {
>> + "dphy-ref",
>> + "dphy-cfg",
>> + "grf",
>> +};
>> +
>> +static const char * const rk3288_mipidphy_clks[] = {
>> + "dphy-ref",
>> + "pclk",
>> +};
>> +
>> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> + int i, hsfreq = 0;
>> +
>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>> + break;
>> + }
>> + }
>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
>> + /* Disable lan turn around, which is ignored in receive mode */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
>> +
>> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
>> +
>> + /* dphy start */
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
>> + usleep_range(100, 150);
>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
>> + usleep_range(100, 150);
>> +
>> + /* set clock lane */
>> + /* HS hsfreq_range & lane 0 settle bypass */
>> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane0 */
>> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> + /* HS RX Control of lane1 */
>> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane2 */
>> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> + /* HS RX Control of lane3 */
>> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> + /* HS RX Data Lanes Settle State Time Control */
>> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> + THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> + /* Normal operation */
>> + mipidphy0_wr_reg(priv, 0x0, 0);
>> +
>> + return 0;
>> +}
>> +
>> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
>> + struct v4l2_subdev *sd)
>> +{
>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>> + int i, hsfreq = 0;
>> +
>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>> + break;
>> + }
>> + }
>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
>> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
>> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
>> + /* Disable lan turn around, which is ignored in receive mode */
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
>> + GENMASK(sensor->lanes - 1, 0));
>> + /* dphy start */
>> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
>> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + usleep_range(100, 150);
>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>> + usleep_range(100, 150);
>> +
>> + /* set clock lane */
>> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>> + /* HS RX Data Lanes Settle State Time Control */
>> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>> + THS_SETTLE_COUNTER_THRESHOLD);
>> +
>> + /* Normal operation */
>> + mipidphy1_wr_reg(priv, 0x0, 0);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
>> + .clks = rk3288_mipidphy_clks,
>> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
>> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
>> + .regs = rk3288_grf_dphy_regs,
>> +};
>> +
>> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
>> + .clks = rk3399_mipidphy_clks,
>> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
>> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
>> + .regs = rk3399_grf_dphy_regs,
>> +};
>> +
>> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
>> + {
>> + .compatible = "rockchip,rk3399-mipi-dphy",
>> + .data = &rk3399_mipidphy_drv_data,
>> + },
>> + {
>> + .compatible = "rockchip,rk3288-mipi-dphy",
>> + .data = &rk3288_mipidphy_drv_data,
>> + },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>> +
>> +/* The .bound() notifier callback when a match is found */
>> +static int
>> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
>> + struct v4l2_subdev *sd,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct mipidphy_priv *priv = container_of(notifier,
>> + struct mipidphy_priv,
>> + notifier);
>> + struct sensor_async_subdev *s_asd = container_of(asd,
>> + struct sensor_async_subdev, asd);
>> + struct mipidphy_sensor *sensor;
>> + unsigned int pad, ret;
>> +
>> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
>> + return -EBUSY;
>> +
>> + sensor = &priv->sensors[priv->num_sensors++];
>> + sensor->lanes = s_asd->lanes;
>> + sensor->mbus = s_asd->mbus;
>> + sensor->sd = sd;
>> +
>> + 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(priv->dev,
>> + "failed to find src pad for %s\n",
>> + sensor->sd->name);
>> +
>> + return -ENXIO;
>> + }
>> +
>> + ret = media_create_pad_link(
>> + &sensor->sd->entity, pad,
>> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
>> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
>> + if (ret) {
>> + dev_err(priv->dev,
>> + "failed to create link for %s\n",
>> + sensor->sd->name);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/* The .unbind callback */
>> +static void
>> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
>> + struct v4l2_subdev *sd,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct mipidphy_priv *priv = container_of(notifier,
>> + struct mipidphy_priv,
>> + notifier);
>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
>> +
>> + sensor->sd = NULL;
>> +}
>> +
>> +static const struct
>> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
>> + .bound = rockchip_mipidphy_notifier_bound,
>> + .unbind = rockchip_mipidphy_notifier_unbind,
>> +};
>> +
>> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
>> + struct v4l2_fwnode_endpoint *vep,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct sensor_async_subdev *s_asd =
>> + container_of(asd, struct sensor_async_subdev, asd);
>> + struct v4l2_mbus_config *config = &s_asd->mbus;
>> +
>> + if (vep->bus_type != V4L2_MBUS_CSI2) {
>> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
>> + return -EINVAL;
>> + }
>> +
>> + if (vep->base.port != 0) {
>> + dev_err(dev, "The PHY has only port 0\n");
>> + return -EINVAL;
>> + }
>> +
>> + config->type = V4L2_MBUS_CSI2;
>> + config->flags = vep->bus.mipi_csi2.flags;
>> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>> +
>> + switch (vep->bus.mipi_csi2.num_data_lanes) {
>> + case 1:
>> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
>> + break;
>> + case 2:
>> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
>> + break;
>> + case 3:
>> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
>> + break;
>> + case 4:
>> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
>> +{
>> + int ret;
>> +
>> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
>> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
>> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
>> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
>> +
>> + ret = media_entity_pads_init(&priv->sd.entity,
>> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
>> + priv->dev, &priv->notifier,
>> + sizeof(struct sensor_async_subdev), 0,
>> + rockchip_mipidphy_fwnode_parse);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (!priv->notifier.num_subdevs)
>> + return -ENODEV; /* no endpoint */
>> +
>> + priv->sd.subdev_notifier = &priv->notifier;
>> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
>> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
>> + if (ret) {
>> + dev_err(priv->dev,
>> + "failed to register async notifier : %d\n", ret);
>> + v4l2_async_notifier_cleanup(&priv->notifier);
>> + return ret;
>> + }
>> +
>> + return v4l2_async_register_subdev(&priv->sd);
>> +}
>> +
>> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct v4l2_subdev *sd;
>> + struct mipidphy_priv *priv;
>> + struct regmap *grf;
>> + struct resource *res;
>> + const struct of_device_id *of_id;
>> + const struct dphy_drv_data *drv_data;
>> + int i, ret;
>> +
>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>> + if (!priv)
>> + return -ENOMEM;
>> + priv->dev = dev;
>> +
>> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
>> + if (!of_id)
>> + return -EINVAL;
>> +
>> + grf = syscon_node_to_regmap(dev->parent->of_node);
>> + if (IS_ERR(grf)) {
>> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
>> + "rockchip,grf");
>> + if (IS_ERR(grf)) {
>> + dev_err(dev, "Can't find GRF syscon\n");
>> + return -ENODEV;
>> + }
>> + }
>> + priv->regmap_grf = grf;
>> +
>> + drv_data = of_id->data;
>> + for (i = 0; i < drv_data->num_clks; i++) {
>> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
>> +
>> + if (IS_ERR(priv->clks[i])) {
>> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
>> + return PTR_ERR(priv->clks[i]);
>> + }
>> + }
>> +
>> + priv->grf_regs = drv_data->regs;
>> + priv->drv_data = drv_data;
>> + priv->stream_on = mipidphy_txrx_stream_on;
>> + priv->txrx_base_addr = NULL;
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>> + if (IS_ERR(priv->txrx_base_addr))
>> + priv->stream_on = mipidphy_rx_stream_on;
>> +
>> + sd = &priv->sd;
>> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
>> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
>> + sd->dev = dev;
>> +
>> + platform_set_drvdata(pdev, &sd->entity);
>> +
>> + ret = rockchip_mipidphy_media_init(priv);
>> + if (ret < 0)
>> + return ret;
>> +
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
>> +{
>> + struct media_entity *me = platform_get_drvdata(pdev);
>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>> +
>> + media_entity_cleanup(&sd->entity);
>> +
>> + pm_runtime_disable(&pdev->dev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
>> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
>> + mipidphy_runtime_resume, NULL)
>> +};
>> +
>> +static struct platform_driver rockchip_isp_mipidphy_driver = {
>> + .probe = rockchip_mipidphy_probe,
>> + .remove = rockchip_mipidphy_remove,
>> + .driver = {
>> + .name = "rockchip-sy-mipi-dphy",
>> + .pm = &rockchip_mipidphy_pm_ops,
>> + .of_match_table = rockchip_mipidphy_match_id,
>> + },
>> +};
>> +
>> +module_platform_driver(rockchip_isp_mipidphy_driver);
>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
>> +MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
>> index 000000000000..c558791064a2
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>> @@ -0,0 +1,15 @@
>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>> +/*
>> + * Rockchip MIPI Synopsys DPHY driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#ifndef __MIPI_DPHY_SY_H__
>> +#define __MIPI_DPHY_SY_H__
>> +
>> +#include <media/v4l2-subdev.h>
>> +
>> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>> +
>> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>
>
> --
> Regards,
>
> Laurent Pinchart
>
>
>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 14:39 ` Jacob Chen
@ 2018-05-16 14:53 ` Jacob Chen
2018-05-16 15:15 ` Tomasz Figa
0 siblings, 1 reply; 51+ messages in thread
From: Jacob Chen @ 2018-05-16 14:53 UTC (permalink / raw)
To: Laurent Pinchart
Cc: open list:ARM/Rockchip SoC...,
linux-kernel, linux-arm-kernel, Mauro Carvalho Chehab,
Linux Media Mailing List, Sakari Ailus, Hans Verkuil,
Tomasz Figa, Shunqian Zheng, 钟以崇,
Eddie Cai, Jeffy Chen, devicetree, Heiko Stuebner, Xing Zheng
2018-05-16 22:39 GMT+08:00 Jacob Chen <jacobchen110@gmail.com>:
> Hi Laurent,
>
> 2018-05-16 13:20 GMT+08:00 Laurent Pinchart <laurent.pinchart@ideasonboard.com>:
>> Hi Jacob,
>>
>> Thank you for the patch.
>>
>> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
>>> From: Jacob Chen <jacob2.chen@rock-chips.com>
>>>
>>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>>
>> Should this really be a subdev driver ? After a quick look at the code, the
>> only parameters you need to configure the PHY is the number of lanes and the
>> data rate. Implementing the whole subdev API seems overcomplicated to me,
>> especially given that the D-PHY doesn't deal with video streams as such, but
>> operates one level down. Shouldn't we model the D-PHY using the Linux PHY
>> framework ? I believe all the features you need are there except for a D-PHY-
>> specific configuration function that should be very easy to add.
>>
>
> It deserves a subdev driver since the ISP is not the only user.
> Other driver, like VIP, use it too.
>
>
For example, if there are two sensors connected to a rk3399 board.
Sensor1 --> DPHY1
Sensor2 --> DPHY2
With a subdev phy driver, i can choose either ISP or VIP for
sensor1/sensor2 by enable/disable media link in the run time.
1.
Sensor1 --> DPHY1 ---> VIP
Sensor2 --> DPHY2 ---> ISP1
2.
Sensor1 --> DPHY1 ---> ISP1
Sensor2 --> DPHY2 ---> VIP
>>> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
>>> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
>>> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
>>> ---
>>> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 ++++++++++++++++++
>>> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
>>> 2 files changed, 883 insertions(+)
>>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>>
>>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c new file mode 100644
>>> index 000000000000..32140960557a
>>> --- /dev/null
>>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
>>> @@ -0,0 +1,868 @@
>>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>>> +/*
>>> + * Rockchip MIPI Synopsys DPHY driver
>>> + *
>>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_runtime.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/mfd/syscon.h>
>>> +#include <media/media-entity.h>
>>> +#include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-fwnode.h>
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +#define RK3288_GRF_SOC_CON6 0x025c
>>> +#define RK3288_GRF_SOC_CON8 0x0264
>>> +#define RK3288_GRF_SOC_CON9 0x0268
>>> +#define RK3288_GRF_SOC_CON10 0x026c
>>> +#define RK3288_GRF_SOC_CON14 0x027c
>>> +#define RK3288_GRF_SOC_STATUS21 0x02d4
>>> +#define RK3288_GRF_IO_VSEL 0x0380
>>> +#define RK3288_GRF_SOC_CON15 0x03a4
>>> +
>>> +#define RK3399_GRF_SOC_CON9 0x6224
>>> +#define RK3399_GRF_SOC_CON21 0x6254
>>> +#define RK3399_GRF_SOC_CON22 0x6258
>>> +#define RK3399_GRF_SOC_CON23 0x625c
>>> +#define RK3399_GRF_SOC_CON24 0x6260
>>> +#define RK3399_GRF_SOC_CON25 0x6264
>>> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
>>> +
>>> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
>>> +#define LANE0_HS_RX_CONTROL 0x44
>>> +#define LANE1_HS_RX_CONTROL 0x54
>>> +#define LANE2_HS_RX_CONTROL 0x84
>>> +#define LANE3_HS_RX_CONTROL 0x94
>>> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
>>> +
>>> +/*
>>> + * CSI HOST
>>> + */
>>> +#define CSIHOST_PHY_TEST_CTRL0 0x30
>>> +#define CSIHOST_PHY_TEST_CTRL1 0x34
>>> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
>>> +#define CSIHOST_DPHY_RSTZ 0x0c
>>> +
>>> +#define PHY_TESTEN_ADDR (0x1 << 16)
>>> +#define PHY_TESTEN_DATA (0x0 << 16)
>>> +#define PHY_TESTCLK (0x1 << 1)
>>> +#define PHY_TESTCLR (0x1 << 0)
>>> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
>>> +
>>> +#define HIWORD_UPDATE(val, mask, shift) \
>>> + ((val) << (shift) | (mask) << ((shift) + 16))
>>> +
>>> +enum mipi_dphy_sy_pads {
>>> + MIPI_DPHY_SY_PAD_SINK = 0,
>>> + MIPI_DPHY_SY_PAD_SOURCE,
>>> + MIPI_DPHY_SY_PADS_NUM,
>>> +};
>>> +
>>> +enum dphy_reg_id {
>>> + GRF_DPHY_RX0_TURNDISABLE = 0,
>>> + GRF_DPHY_RX0_FORCERXMODE,
>>> + GRF_DPHY_RX0_FORCETXSTOPMODE,
>>> + GRF_DPHY_RX0_ENABLE,
>>> + GRF_DPHY_RX0_TESTCLR,
>>> + GRF_DPHY_RX0_TESTCLK,
>>> + GRF_DPHY_RX0_TESTEN,
>>> + GRF_DPHY_RX0_TESTDIN,
>>> + GRF_DPHY_RX0_TURNREQUEST,
>>> + GRF_DPHY_RX0_TESTDOUT,
>>> + GRF_DPHY_TX0_TURNDISABLE,
>>> + GRF_DPHY_TX0_FORCERXMODE,
>>> + GRF_DPHY_TX0_FORCETXSTOPMODE,
>>> + GRF_DPHY_TX0_TURNREQUEST,
>>> + GRF_DPHY_TX1RX1_TURNDISABLE,
>>> + GRF_DPHY_TX1RX1_FORCERXMODE,
>>> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
>>> + GRF_DPHY_TX1RX1_ENABLE,
>>> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
>>> + GRF_DPHY_TX1RX1_BASEDIR,
>>> + GRF_DPHY_TX1RX1_ENABLECLK,
>>> + GRF_DPHY_TX1RX1_TURNREQUEST,
>>> + GRF_DPHY_RX1_SRC_SEL,
>>> + /* rk3288 only */
>>> + GRF_CON_DISABLE_ISP,
>>> + GRF_CON_ISP_DPHY_SEL,
>>> + GRF_DSI_CSI_TESTBUS_SEL,
>>> + GRF_DVP_V18SEL,
>>> + /* below is for rk3399 only */
>>> + GRF_DPHY_RX0_CLK_INV_SEL,
>>> + GRF_DPHY_RX1_CLK_INV_SEL,
>>> +};
>>> +
>>> +struct dphy_reg {
>>> + u32 offset;
>>> + u32 mask;
>>> + u32 shift;
>>> +};
>>> +
>>> +#define PHY_REG(_offset, _width, _shift) \
>>> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
>>> +
>>> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
>>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
>>> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
>>> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
>>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
>>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
>>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
>>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
>>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
>>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
>>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
>>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
>>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
>>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
>>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
>>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
>>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
>>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
>>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
>>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
>>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
>>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
>>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
>>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
>>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
>>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
>>> +};
>>> +
>>> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
>>> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
>>> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
>>> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
>>> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
>>> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
>>> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
>>> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
>>> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
>>> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
>>> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
>>> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
>>> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
>>> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
>>> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
>>> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
>>> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
>>> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
>>> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
>>> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
>>> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
>>> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
>>> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
>>> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
>>> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
>>> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
>>> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
>>> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
>>> +};
>>> +
>>> +struct hsfreq_range {
>>> + u32 range_h;
>>> + u8 cfg_bit;
>>> +};
>>> +
>>> +struct mipidphy_priv;
>>> +
>>> +struct dphy_drv_data {
>>> + const char * const *clks;
>>> + int num_clks;
>>> + const struct hsfreq_range *hsfreq_ranges;
>>> + int num_hsfreq_ranges;
>>> + const struct dphy_reg *regs;
>>> +};
>>> +
>>> +struct sensor_async_subdev {
>>> + struct v4l2_async_subdev asd;
>>> + struct v4l2_mbus_config mbus;
>>> + int lanes;
>>> +};
>>> +
>>> +#define MAX_DPHY_CLK 8
>>> +#define MAX_DPHY_SENSORS 2
>>> +
>>> +struct mipidphy_sensor {
>>> + struct v4l2_subdev *sd;
>>> + struct v4l2_mbus_config mbus;
>>> + int lanes;
>>> +};
>>> +
>>> +struct mipidphy_priv {
>>> + struct device *dev;
>>> + struct regmap *regmap_grf;
>>> + const struct dphy_reg *grf_regs;
>>> + struct clk *clks[MAX_DPHY_CLK];
>>> + const struct dphy_drv_data *drv_data;
>>> + u64 data_rate_mbps;
>>> + struct v4l2_async_notifier notifier;
>>> + struct v4l2_subdev sd;
>>> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
>>> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>>> + int num_sensors;
>>> + bool is_streaming;
>>> + void __iomem *txrx_base_addr;
>>> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
>>> +};
>>> +
>>> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev
>>> *subdev) +{
>>> + return container_of(subdev, struct mipidphy_priv, sd);
>>> +}
>>> +
>>> +static inline void write_grf_reg(struct mipidphy_priv *priv,
>>> + int index, u8 value)
>>> +{
>>> + const struct dphy_reg *reg = &priv->grf_regs[index];
>>> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
>>> +
>>> + WARN_ON(!reg->offset);
>>> + regmap_write(priv->regmap_grf, reg->offset, val);
>>> +}
>>> +
>>> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
>>> + u8 test_code, u8 test_data)
>>> +{
>>> + /*
>>> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
>>> + * is latched internally as the current test code. Test data is
>>> + * programmed internally by rising edge on TESTCLK.
>>> + */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> +}
>>> +
>>> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char
>>> addr, + unsigned char data)
>>> +{
>>> + /*
>>> + * TESTEN =1,TESTDIN=addr
>>> + * TESTCLK=0
>>> + * TESTEN =0,TESTDIN=data
>>> + * TESTCLK=1
>>> + */
>>> + writel((PHY_TESTEN_ADDR | addr),
>>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>>> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + writel((PHY_TESTEN_DATA | data),
>>> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> +}
>>> +
>>> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
>>> +{
>>> + struct media_pad *local, *remote;
>>> + struct media_entity *sensor_me;
>>> +
>>> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
>>> + remote = media_entity_remote_pad(local);
>>> + if (!remote) {
>>> + v4l2_warn(sd, "No link between dphy and sensor\n");
>>> + return NULL;
>>> + }
>>> +
>>> + sensor_me = media_entity_remote_pad(local)->entity;
>>> + return media_entity_to_v4l2_subdev(sensor_me);
>>> +}
>>> +
>>> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < priv->num_sensors; ++i)
>>> + if (priv->sensors[i].sd == sd)
>>> + return &priv->sensors[i];
>>> +
>>> + return NULL;
>>> +}
>>> +
>>> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct v4l2_ctrl *link_freq;
>>> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
>>> + int ret;
>>> +
>>> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>>> + if (!link_freq) {
>>> + v4l2_warn(sd, "No pixel rate control in subdev\n");
>>> + return -EPIPE;
>>> + }
>>> +
>>> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
>>> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
>>> + if (ret < 0) {
>>> + v4l2_err(sd, "Failed to get menu item\n");
>>> + return ret;
>>> + }
>>> +
>>> + if (!qm.value) {
>>> + v4l2_err(sd, "Invalid link_freq\n");
>>> + return -EINVAL;
>>> + }
>>> + priv->data_rate_mbps = qm.value * 2;
>>> + do_div(priv->data_rate_mbps, 1000 * 1000);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int ret = 0;
>>> +
>>> + if (priv->is_streaming)
>>> + return 0;
>>> +
>>> + ret = mipidphy_get_sensor_data_rate(sd);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + priv->stream_on(priv, sd);
>>> +
>>> + priv->is_streaming = true;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> +
>>> + priv->is_streaming = false;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
>>> +{
>>> + if (on)
>>> + return mipidphy_s_stream_start(sd);
>>> + else
>>> + return mipidphy_s_stream_stop(sd);
>>> +}
>>> +
>>> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
>>> + struct v4l2_mbus_config *config)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> +
>>> + *config = sensor->mbus;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
>>> +{
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> +
>>> + if (on)
>>> + return pm_runtime_get_sync(priv->dev);
>>> + else
>>> + return pm_runtime_put(priv->dev);
>>> +}
>>> +
>>> +static int mipidphy_runtime_suspend(struct device *dev)
>>> +{
>>> + struct media_entity *me = dev_get_drvdata(dev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int i, num_clks;
>>> +
>>> + num_clks = priv->drv_data->num_clks;
>>> + for (i = num_clks - 1; i >= 0; i--)
>>> + clk_disable_unprepare(priv->clks[i]);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_runtime_resume(struct device *dev)
>>> +{
>>> + struct media_entity *me = dev_get_drvdata(dev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> + struct mipidphy_priv *priv = to_dphy_priv(sd);
>>> + int i, num_clks, ret;
>>> +
>>> + num_clks = priv->drv_data->num_clks;
>>> + for (i = 0; i < num_clks; i++) {
>>> + ret = clk_prepare_enable(priv->clks[i]);
>>> + if (ret < 0)
>>> + goto err;
>>> + }
>>> +
>>> + return 0;
>>> +err:
>>> + while (--i >= 0)
>>> + clk_disable_unprepare(priv->clks[i]);
>>> + return ret;
>>> +}
>>> +
>>> +/* dphy accepts all fmt/size from sensor */
>>> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
>>> + struct v4l2_subdev_pad_config *cfg,
>>> + struct v4l2_subdev_format *fmt)
>>> +{
>>> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
>>> +
>>> + /*
>>> + * Do not allow format changes and just relay whatever
>>> + * set currently in the sensor.
>>> + */
>>> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>>> +}
>>> +
>>> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
>>> + .set_fmt = mipidphy_get_set_fmt,
>>> + .get_fmt = mipidphy_get_set_fmt,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
>>> + .s_power = mipidphy_s_power,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
>>> + .g_mbus_config = mipidphy_g_mbus_config,
>>> + .s_stream = mipidphy_s_stream,
>>> +};
>>> +
>>> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
>>> + .core = &mipidphy_core_ops,
>>> + .video = &mipidphy_video_ops,
>>> + .pad = &mipidphy_subdev_pad_ops,
>>> +};
>>> +
>>> +/* These tables must be sorted by .range_h ascending. */
>>> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
>>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>>> + { 999, 0x1a}
>>> +};
>>> +
>>> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
>>> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
>>> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
>>> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
>>> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
>>> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
>>> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
>>> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
>>> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
>>> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
>>> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
>>> +};
>>> +
>>> +static const char * const rk3399_mipidphy_clks[] = {
>>> + "dphy-ref",
>>> + "dphy-cfg",
>>> + "grf",
>>> +};
>>> +
>>> +static const char * const rk3288_mipidphy_clks[] = {
>>> + "dphy-ref",
>>> + "pclk",
>>> +};
>>> +
>>> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>>> + int i, hsfreq = 0;
>>> +
>>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>>> + break;
>>> + }
>>> + }
>>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
>>> + /* Disable lan turn around, which is ignored in receive mode */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
>>> +
>>> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
>>> +
>>> + /* dphy start */
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
>>> + usleep_range(100, 150);
>>> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
>>> + usleep_range(100, 150);
>>> +
>>> + /* set clock lane */
>>> + /* HS hsfreq_range & lane 0 settle bypass */
>>> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane0 */
>>> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>>> + /* HS RX Control of lane1 */
>>> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane2 */
>>> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>>> + /* HS RX Control of lane3 */
>>> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>>> + /* HS RX Data Lanes Settle State Time Control */
>>> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>>> + THS_SETTLE_COUNTER_THRESHOLD);
>>> +
>>> + /* Normal operation */
>>> + mipidphy0_wr_reg(priv, 0x0, 0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
>>> + struct v4l2_subdev *sd)
>>> +{
>>> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
>>> + const struct dphy_drv_data *drv_data = priv->drv_data;
>>> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
>>> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
>>> + int i, hsfreq = 0;
>>> +
>>> + for (i = 0; i < num_hsfreq_ranges; i++) {
>>> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
>>> + hsfreq = hsfreq_ranges[i].cfg_bit;
>>> + break;
>>> + }
>>> + }
>>> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
>>> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
>>> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
>>> + /* Disable lan turn around, which is ignored in receive mode */
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
>>> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
>>> + GENMASK(sensor->lanes - 1, 0));
>>> + /* dphy start */
>>> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
>>> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + usleep_range(100, 150);
>>> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
>>> + usleep_range(100, 150);
>>> +
>>> + /* set clock lane */
>>> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
>>> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
>>> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
>>> + /* HS RX Data Lanes Settle State Time Control */
>>> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
>>> + THS_SETTLE_COUNTER_THRESHOLD);
>>> +
>>> + /* Normal operation */
>>> + mipidphy1_wr_reg(priv, 0x0, 0);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
>>> + .clks = rk3288_mipidphy_clks,
>>> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
>>> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
>>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
>>> + .regs = rk3288_grf_dphy_regs,
>>> +};
>>> +
>>> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
>>> + .clks = rk3399_mipidphy_clks,
>>> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
>>> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
>>> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
>>> + .regs = rk3399_grf_dphy_regs,
>>> +};
>>> +
>>> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
>>> + {
>>> + .compatible = "rockchip,rk3399-mipi-dphy",
>>> + .data = &rk3399_mipidphy_drv_data,
>>> + },
>>> + {
>>> + .compatible = "rockchip,rk3288-mipi-dphy",
>>> + .data = &rk3288_mipidphy_drv_data,
>>> + },
>>> + {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>>> +
>>> +/* The .bound() notifier callback when a match is found */
>>> +static int
>>> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
>>> + struct v4l2_subdev *sd,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct mipidphy_priv *priv = container_of(notifier,
>>> + struct mipidphy_priv,
>>> + notifier);
>>> + struct sensor_async_subdev *s_asd = container_of(asd,
>>> + struct sensor_async_subdev, asd);
>>> + struct mipidphy_sensor *sensor;
>>> + unsigned int pad, ret;
>>> +
>>> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
>>> + return -EBUSY;
>>> +
>>> + sensor = &priv->sensors[priv->num_sensors++];
>>> + sensor->lanes = s_asd->lanes;
>>> + sensor->mbus = s_asd->mbus;
>>> + sensor->sd = sd;
>>> +
>>> + 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(priv->dev,
>>> + "failed to find src pad for %s\n",
>>> + sensor->sd->name);
>>> +
>>> + return -ENXIO;
>>> + }
>>> +
>>> + ret = media_create_pad_link(
>>> + &sensor->sd->entity, pad,
>>> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
>>> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
>>> + if (ret) {
>>> + dev_err(priv->dev,
>>> + "failed to create link for %s\n",
>>> + sensor->sd->name);
>>> + return ret;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/* The .unbind callback */
>>> +static void
>>> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
>>> + struct v4l2_subdev *sd,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct mipidphy_priv *priv = container_of(notifier,
>>> + struct mipidphy_priv,
>>> + notifier);
>>> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
>>> +
>>> + sensor->sd = NULL;
>>> +}
>>> +
>>> +static const struct
>>> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
>>> + .bound = rockchip_mipidphy_notifier_bound,
>>> + .unbind = rockchip_mipidphy_notifier_unbind,
>>> +};
>>> +
>>> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
>>> + struct v4l2_fwnode_endpoint *vep,
>>> + struct v4l2_async_subdev *asd)
>>> +{
>>> + struct sensor_async_subdev *s_asd =
>>> + container_of(asd, struct sensor_async_subdev, asd);
>>> + struct v4l2_mbus_config *config = &s_asd->mbus;
>>> +
>>> + if (vep->bus_type != V4L2_MBUS_CSI2) {
>>> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + if (vep->base.port != 0) {
>>> + dev_err(dev, "The PHY has only port 0\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + config->type = V4L2_MBUS_CSI2;
>>> + config->flags = vep->bus.mipi_csi2.flags;
>>> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>>> +
>>> + switch (vep->bus.mipi_csi2.num_data_lanes) {
>>> + case 1:
>>> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
>>> + break;
>>> + case 2:
>>> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
>>> + break;
>>> + case 3:
>>> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
>>> + break;
>>> + case 4:
>>> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
>>> + break;
>>> + default:
>>> + return -EINVAL;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
>>> +{
>>> + int ret;
>>> +
>>> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
>>> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
>>> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
>>> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
>>> +
>>> + ret = media_entity_pads_init(&priv->sd.entity,
>>> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
>>> + priv->dev, &priv->notifier,
>>> + sizeof(struct sensor_async_subdev), 0,
>>> + rockchip_mipidphy_fwnode_parse);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + if (!priv->notifier.num_subdevs)
>>> + return -ENODEV; /* no endpoint */
>>> +
>>> + priv->sd.subdev_notifier = &priv->notifier;
>>> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
>>> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
>>> + if (ret) {
>>> + dev_err(priv->dev,
>>> + "failed to register async notifier : %d\n", ret);
>>> + v4l2_async_notifier_cleanup(&priv->notifier);
>>> + return ret;
>>> + }
>>> +
>>> + return v4l2_async_register_subdev(&priv->sd);
>>> +}
>>> +
>>> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
>>> +{
>>> + struct device *dev = &pdev->dev;
>>> + struct v4l2_subdev *sd;
>>> + struct mipidphy_priv *priv;
>>> + struct regmap *grf;
>>> + struct resource *res;
>>> + const struct of_device_id *of_id;
>>> + const struct dphy_drv_data *drv_data;
>>> + int i, ret;
>>> +
>>> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> + priv->dev = dev;
>>> +
>>> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
>>> + if (!of_id)
>>> + return -EINVAL;
>>> +
>>> + grf = syscon_node_to_regmap(dev->parent->of_node);
>>> + if (IS_ERR(grf)) {
>>> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
>>> + "rockchip,grf");
>>> + if (IS_ERR(grf)) {
>>> + dev_err(dev, "Can't find GRF syscon\n");
>>> + return -ENODEV;
>>> + }
>>> + }
>>> + priv->regmap_grf = grf;
>>> +
>>> + drv_data = of_id->data;
>>> + for (i = 0; i < drv_data->num_clks; i++) {
>>> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
>>> +
>>> + if (IS_ERR(priv->clks[i])) {
>>> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
>>> + return PTR_ERR(priv->clks[i]);
>>> + }
>>> + }
>>> +
>>> + priv->grf_regs = drv_data->regs;
>>> + priv->drv_data = drv_data;
>>> + priv->stream_on = mipidphy_txrx_stream_on;
>>> + priv->txrx_base_addr = NULL;
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>>> + if (IS_ERR(priv->txrx_base_addr))
>>> + priv->stream_on = mipidphy_rx_stream_on;
>>> +
>>> + sd = &priv->sd;
>>> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
>>> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>>> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
>>> + sd->dev = dev;
>>> +
>>> + platform_set_drvdata(pdev, &sd->entity);
>>> +
>>> + ret = rockchip_mipidphy_media_init(priv);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + pm_runtime_enable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
>>> +{
>>> + struct media_entity *me = platform_get_drvdata(pdev);
>>> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
>>> +
>>> + media_entity_cleanup(&sd->entity);
>>> +
>>> + pm_runtime_disable(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
>>> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
>>> + mipidphy_runtime_resume, NULL)
>>> +};
>>> +
>>> +static struct platform_driver rockchip_isp_mipidphy_driver = {
>>> + .probe = rockchip_mipidphy_probe,
>>> + .remove = rockchip_mipidphy_remove,
>>> + .driver = {
>>> + .name = "rockchip-sy-mipi-dphy",
>>> + .pm = &rockchip_mipidphy_pm_ops,
>>> + .of_match_table = rockchip_mipidphy_match_id,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(rockchip_isp_mipidphy_driver);
>>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>>> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
>>> +MODULE_LICENSE("Dual BSD/GPL");
>>> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>> b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h new file mode 100644
>>> index 000000000000..c558791064a2
>>> --- /dev/null
>>> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>>> @@ -0,0 +1,15 @@
>>> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
>>> +/*
>>> + * Rockchip MIPI Synopsys DPHY driver
>>> + *
>>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>>> + */
>>> +
>>> +#ifndef __MIPI_DPHY_SY_H__
>>> +#define __MIPI_DPHY_SY_H__
>>> +
>>> +#include <media/v4l2-subdev.h>
>>> +
>>> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>>> +
>>> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>>
>>
>> --
>> Regards,
>>
>> Laurent Pinchart
>>
>>
>>
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-05-16 14:53 ` Jacob Chen
@ 2018-05-16 15:15 ` Tomasz Figa
0 siblings, 0 replies; 51+ messages in thread
From: Tomasz Figa @ 2018-05-16 15:15 UTC (permalink / raw)
To: jacobchen110
Cc: Laurent Pinchart, open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, 钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner, zhengxing
Hi Jacob,
On Wed, May 16, 2018 at 11:54 PM Jacob Chen <jacobchen110@gmail.com> wrote:
> 2018-05-16 22:39 GMT+08:00 Jacob Chen <jacobchen110@gmail.com>:
> > Hi Laurent,
> >
> > 2018-05-16 13:20 GMT+08:00 Laurent Pinchart <
laurent.pinchart@ideasonboard.com>:
> >> Hi Jacob,
> >>
> >> Thank you for the patch.
> >>
> >> On Thursday, 8 March 2018 11:47:54 EEST Jacob Chen wrote:
> >>> From: Jacob Chen <jacob2.chen@rock-chips.com>
> >>>
> >>> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY
driver
> >>
> >> Should this really be a subdev driver ? After a quick look at the
code, the
> >> only parameters you need to configure the PHY is the number of lanes
and the
> >> data rate. Implementing the whole subdev API seems overcomplicated to
me,
> >> especially given that the D-PHY doesn't deal with video streams as
such, but
> >> operates one level down. Shouldn't we model the D-PHY using the Linux
PHY
> >> framework ? I believe all the features you need are there except for a
D-PHY-
> >> specific configuration function that should be very easy to add.
> >>
> >
> > It deserves a subdev driver since the ISP is not the only user.
> > Other driver, like VIP, use it too.
> >
> >
> For example, if there are two sensors connected to a rk3399 board.
> Sensor1 --> DPHY1
> Sensor2 --> DPHY2
> With a subdev phy driver, i can choose either ISP or VIP for
> sensor1/sensor2 by enable/disable media link in the run time.
> 1.
> Sensor1 --> DPHY1 ---> VIP
> Sensor2 --> DPHY2 ---> ISP1
> 2.
> Sensor1 --> DPHY1 ---> ISP1
> Sensor2 --> DPHY2 ---> VIP
What is VIP?
Also, if we model the DPHY using the PHY interface, it will be still
possible to achieve the same, just by toggling the link between sensor and
VIP or ISP1:
1.
Sensor1 -------|~|--- VIP
\ | (PHY interface)
\ DPHY1
\ | (PHY interface)
\---| |-- ISP1
Sensor2 -------| |-- VIP
\ | (PHY interface)
\ DPHY2
\ | (PHY interface)
\---|~|-- ISP1
2.
Sensor1 -------| |-- VIP
\ | (PHY interface)
\ DPHY1
\ | (PHY interface)
\---|~|-- ISP1
Sensor2 -------|~|-- VIP
\ | (PHY interface)
\ DPHY2
\ | (PHY interface)
\---| |-- ISP1
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2018-05-16 5:20 ` Laurent Pinchart
@ 2019-03-10 17:49 ` Laurent Pinchart
2019-03-11 9:37 ` Tomasz Figa
1 sibling, 1 reply; 51+ messages in thread
From: Laurent Pinchart @ 2019-03-10 17:49 UTC (permalink / raw)
To: Jacob Chen
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, mchehab,
linux-media, sakari.ailus, hans.verkuil, tfiga, zhengsq, zyc,
eddie.cai.linux, jeffy.chen, devicetree, heiko, Jacob Chen
Hi Jacob,
A few more comments on the code this time.
First of all, this has bit-rotten a bit and doesn't compile. The
following patch fixes it. Feel free to squash it into this patch (no
need to credit me or add my SoB line).
commit 297399bb5e3ac8d50f27f1c911fe7e5f26983e56
Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Date: Sat Mar 2 02:18:19 2019 +0200
media: rkisp1: Fix compilation errors
The code has bit-rotten since March 2018, fix compilation errors.
The new V4L2 async notifier API requires notifiers to be initialized by
a call to v4l2_async_notifier_init() before being used, do so.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
index 32140960557a..8a7d070314c9 100644
--- a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
+++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
@@ -697,7 +697,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
container_of(asd, struct sensor_async_subdev, asd);
struct v4l2_mbus_config *config = &s_asd->mbus;
- if (vep->bus_type != V4L2_MBUS_CSI2) {
+ if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
dev_err(dev, "Only CSI2 bus type is currently supported\n");
return -EINVAL;
}
@@ -707,7 +707,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
return -EINVAL;
}
- config->type = V4L2_MBUS_CSI2;
+ config->type = V4L2_MBUS_CSI2_DPHY;
config->flags = vep->bus.mipi_csi2.flags;
s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
@@ -745,6 +745,8 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
if (ret < 0)
return ret;
+ v4l2_async_notifier_init(&priv->notifier);
+
ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
priv->dev, &priv->notifier,
sizeof(struct sensor_async_subdev), 0,
@@ -752,7 +754,7 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
if (ret < 0)
return ret;
- if (!priv->notifier.num_subdevs)
+ if (list_empty(&priv->notifier.asd_list))
return -ENODEV; /* no endpoint */
priv->sd.subdev_notifier = &priv->notifier;
Then, please see below for additional comments.
On Thu, Mar 08, 2018 at 05:47:54PM +0800, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
>
> Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> ---
> .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
> .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> 2 files changed, 883 insertions(+)
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> new file mode 100644
> index 000000000000..32140960557a
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -0,0 +1,868 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <media/media-entity.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-fwnode.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define RK3288_GRF_SOC_CON6 0x025c
> +#define RK3288_GRF_SOC_CON8 0x0264
> +#define RK3288_GRF_SOC_CON9 0x0268
> +#define RK3288_GRF_SOC_CON10 0x026c
> +#define RK3288_GRF_SOC_CON14 0x027c
> +#define RK3288_GRF_SOC_STATUS21 0x02d4
> +#define RK3288_GRF_IO_VSEL 0x0380
> +#define RK3288_GRF_SOC_CON15 0x03a4
> +
> +#define RK3399_GRF_SOC_CON9 0x6224
> +#define RK3399_GRF_SOC_CON21 0x6254
> +#define RK3399_GRF_SOC_CON22 0x6258
> +#define RK3399_GRF_SOC_CON23 0x625c
> +#define RK3399_GRF_SOC_CON24 0x6260
> +#define RK3399_GRF_SOC_CON25 0x6264
> +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> +
> +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> +#define LANE0_HS_RX_CONTROL 0x44
> +#define LANE1_HS_RX_CONTROL 0x54
> +#define LANE2_HS_RX_CONTROL 0x84
> +#define LANE3_HS_RX_CONTROL 0x94
> +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> +
> +/*
> + * CSI HOST
> + */
> +#define CSIHOST_PHY_TEST_CTRL0 0x30
> +#define CSIHOST_PHY_TEST_CTRL1 0x34
> +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> +#define CSIHOST_DPHY_RSTZ 0x0c
> +
> +#define PHY_TESTEN_ADDR (0x1 << 16)
> +#define PHY_TESTEN_DATA (0x0 << 16)
> +#define PHY_TESTCLK (0x1 << 1)
> +#define PHY_TESTCLR (0x1 << 0)
> +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> +
> +#define HIWORD_UPDATE(val, mask, shift) \
> + ((val) << (shift) | (mask) << ((shift) + 16))
> +
> +enum mipi_dphy_sy_pads {
> + MIPI_DPHY_SY_PAD_SINK = 0,
> + MIPI_DPHY_SY_PAD_SOURCE,
> + MIPI_DPHY_SY_PADS_NUM,
> +};
> +
> +enum dphy_reg_id {
> + GRF_DPHY_RX0_TURNDISABLE = 0,
> + GRF_DPHY_RX0_FORCERXMODE,
> + GRF_DPHY_RX0_FORCETXSTOPMODE,
> + GRF_DPHY_RX0_ENABLE,
> + GRF_DPHY_RX0_TESTCLR,
> + GRF_DPHY_RX0_TESTCLK,
> + GRF_DPHY_RX0_TESTEN,
> + GRF_DPHY_RX0_TESTDIN,
> + GRF_DPHY_RX0_TURNREQUEST,
> + GRF_DPHY_RX0_TESTDOUT,
> + GRF_DPHY_TX0_TURNDISABLE,
> + GRF_DPHY_TX0_FORCERXMODE,
> + GRF_DPHY_TX0_FORCETXSTOPMODE,
> + GRF_DPHY_TX0_TURNREQUEST,
> + GRF_DPHY_TX1RX1_TURNDISABLE,
> + GRF_DPHY_TX1RX1_FORCERXMODE,
> + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> + GRF_DPHY_TX1RX1_ENABLE,
> + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> + GRF_DPHY_TX1RX1_BASEDIR,
> + GRF_DPHY_TX1RX1_ENABLECLK,
> + GRF_DPHY_TX1RX1_TURNREQUEST,
> + GRF_DPHY_RX1_SRC_SEL,
> + /* rk3288 only */
> + GRF_CON_DISABLE_ISP,
> + GRF_CON_ISP_DPHY_SEL,
> + GRF_DSI_CSI_TESTBUS_SEL,
> + GRF_DVP_V18SEL,
> + /* below is for rk3399 only */
> + GRF_DPHY_RX0_CLK_INV_SEL,
> + GRF_DPHY_RX1_CLK_INV_SEL,
> +};
> +
> +struct dphy_reg {
> + u32 offset;
> + u32 mask;
> + u32 shift;
> +};
> +
> +#define PHY_REG(_offset, _width, _shift) \
> + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> +
> +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> +};
> +
> +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> +};
> +
> +struct hsfreq_range {
> + u32 range_h;
> + u8 cfg_bit;
> +};
> +
> +struct mipidphy_priv;
> +
> +struct dphy_drv_data {
> + const char * const *clks;
> + int num_clks;
> + const struct hsfreq_range *hsfreq_ranges;
> + int num_hsfreq_ranges;
> + const struct dphy_reg *regs;
> +};
> +
> +struct sensor_async_subdev {
> + struct v4l2_async_subdev asd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +#define MAX_DPHY_CLK 8
> +#define MAX_DPHY_SENSORS 2
> +
> +struct mipidphy_sensor {
> + struct v4l2_subdev *sd;
> + struct v4l2_mbus_config mbus;
> + int lanes;
> +};
> +
> +struct mipidphy_priv {
> + struct device *dev;
> + struct regmap *regmap_grf;
> + const struct dphy_reg *grf_regs;
> + struct clk *clks[MAX_DPHY_CLK];
> + const struct dphy_drv_data *drv_data;
> + u64 data_rate_mbps;
> + struct v4l2_async_notifier notifier;
> + struct v4l2_subdev sd;
> + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
Should we really hardcode the maximum number of sensors ? Wouldn't it be
better to allocate this dynamically ?
> + int num_sensors;
> + bool is_streaming;
> + void __iomem *txrx_base_addr;
> + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> +};
> +
> +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
> +{
> + return container_of(subdev, struct mipidphy_priv, sd);
> +}
> +
> +static inline void write_grf_reg(struct mipidphy_priv *priv,
> + int index, u8 value)
> +{
> + const struct dphy_reg *reg = &priv->grf_regs[index];
> + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> +
> + WARN_ON(!reg->offset);
> + regmap_write(priv->regmap_grf, reg->offset, val);
> +}
> +
> +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> + u8 test_code, u8 test_data)
> +{
> + /*
> + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> + * is latched internally as the current test code. Test data is
> + * programmed internally by rising edge on TESTCLK.
> + */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> +}
> +
> +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
> + unsigned char data)
> +{
> + /*
> + * TESTEN =1,TESTDIN=addr
> + * TESTCLK=0
> + * TESTEN =0,TESTDIN=data
> + * TESTCLK=1
> + */
> + writel((PHY_TESTEN_ADDR | addr),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel((PHY_TESTEN_DATA | data),
> + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> +}
> +
> +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> +{
> + struct media_pad *local, *remote;
> + struct media_entity *sensor_me;
> +
> + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> + remote = media_entity_remote_pad(local);
> + if (!remote) {
> + v4l2_warn(sd, "No link between dphy and sensor\n");
> + return NULL;
> + }
> +
> + sensor_me = media_entity_remote_pad(local)->entity;
> + return media_entity_to_v4l2_subdev(sensor_me);
You could call this at the beginning of mipidphy_s_stream_start() and
pass the sensor pointer to mipidphy_get_sensor_data_rate() and the
.stream_on() operations to avoid multiple costly lookups, or possibly
cache it in the mipidphy_priv structure (in that case I'd reset it to
NULL at stream off time).
> +}
> +
> +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + int i;
> +
> + for (i = 0; i < priv->num_sensors; ++i)
> + if (priv->sensors[i].sd == sd)
> + return &priv->sensors[i];
> +
> + return NULL;
> +}
> +
> +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct v4l2_ctrl *link_freq;
> + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> + int ret;
> +
> + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
The correct control for this is V4L2_CID_PIXEL_RATE. You will have to
divide it by the number of lanes to get the data rate per lane, but
there will be no need to multiply it by 2 as below.
> + if (!link_freq) {
> + v4l2_warn(sd, "No pixel rate control in subdev\n");
> + return -EPIPE;
> + }
> +
> + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> + if (ret < 0) {
> + v4l2_err(sd, "Failed to get menu item\n");
> + return ret;
> + }
> +
> + if (!qm.value) {
> + v4l2_err(sd, "Invalid link_freq\n");
> + return -EINVAL;
> + }
> + priv->data_rate_mbps = qm.value * 2;
> + do_div(priv->data_rate_mbps, 1000 * 1000);
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int ret = 0;
> +
> + if (priv->is_streaming)
> + return 0;
> +
> + ret = mipidphy_get_sensor_data_rate(sd);
> + if (ret < 0)
> + return ret;
> +
> + priv->stream_on(priv, sd);
> +
> + priv->is_streaming = true;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + priv->is_streaming = false;
> +
> + return 0;
> +}
> +
> +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> +{
> + if (on)
> + return mipidphy_s_stream_start(sd);
> + else
> + return mipidphy_s_stream_stop(sd);
> +}
> +
> +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> + struct v4l2_mbus_config *config)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> +
> + *config = sensor->mbus;
> +
> + return 0;
> +}
This seems like a hack :-(
> +
> +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> +
> + if (on)
> + return pm_runtime_get_sync(priv->dev);
> + else
> + return pm_runtime_put(priv->dev);
> +}
> +
> +static int mipidphy_runtime_suspend(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = num_clks - 1; i >= 0; i--)
> + clk_disable_unprepare(priv->clks[i]);
> +
> + return 0;
> +}
> +
> +static int mipidphy_runtime_resume(struct device *dev)
> +{
> + struct media_entity *me = dev_get_drvdata(dev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> + struct mipidphy_priv *priv = to_dphy_priv(sd);
> + int i, num_clks, ret;
> +
> + num_clks = priv->drv_data->num_clks;
> + for (i = 0; i < num_clks; i++) {
> + ret = clk_prepare_enable(priv->clks[i]);
> + if (ret < 0)
> + goto err;
> + }
> +
> + return 0;
> +err:
> + while (--i >= 0)
> + clk_disable_unprepare(priv->clks[i]);
> + return ret;
> +}
> +
> +/* dphy accepts all fmt/size from sensor */
> +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> + struct v4l2_subdev_pad_config *cfg,
> + struct v4l2_subdev_format *fmt)
> +{
> + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> +
> + /*
> + * Do not allow format changes and just relay whatever
> + * set currently in the sensor.
> + */
> + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
It's userspace responsibility to propagate formats through the pipeline
when using the MC API, you shouldn't access the format of the source
subdev in this driver.
> +}
> +
> +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> + .set_fmt = mipidphy_get_set_fmt,
> + .get_fmt = mipidphy_get_set_fmt,
> +};
> +
> +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> + .s_power = mipidphy_s_power,
> +};
> +
> +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> + .g_mbus_config = mipidphy_g_mbus_config,
> + .s_stream = mipidphy_s_stream,
> +};
> +
> +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> + .core = &mipidphy_core_ops,
> + .video = &mipidphy_video_ops,
> + .pad = &mipidphy_subdev_pad_ops,
> +};
> +
> +/* These tables must be sorted by .range_h ascending. */
> +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}
> +};
> +
> +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> +};
> +
> +static const char * const rk3399_mipidphy_clks[] = {
> + "dphy-ref",
> + "dphy-cfg",
> + "grf",
> +};
> +
> +static const char * const rk3288_mipidphy_clks[] = {
> + "dphy-ref",
> + "pclk",
> +};
> +
> +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> +
> + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> +
> + /* dphy start */
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> + usleep_range(100, 150);
> + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + /* HS hsfreq_range & lane 0 settle bypass */
> + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane0 */
> + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + /* HS RX Control of lane1 */
> + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane2 */
> + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + /* HS RX Control of lane3 */
> + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy0_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> + struct v4l2_subdev *sd)
> +{
> + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> + const struct dphy_drv_data *drv_data = priv->drv_data;
> + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> + int i, hsfreq = 0;
> +
> + for (i = 0; i < num_hsfreq_ranges; i++) {
> + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> + hsfreq = hsfreq_ranges[i].cfg_bit;
> + break;
> + }
> + }
> + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> + /* Disable lan turn around, which is ignored in receive mode */
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> + GENMASK(sensor->lanes - 1, 0));
> + /* dphy start */
> + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> + usleep_range(100, 150);
> +
> + /* set clock lane */
> + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> + /* HS RX Data Lanes Settle State Time Control */
> + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> + THS_SETTLE_COUNTER_THRESHOLD);
> +
> + /* Normal operation */
> + mipidphy1_wr_reg(priv, 0x0, 0);
> +
> + return 0;
> +}
> +
> +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> + .clks = rk3288_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> + .regs = rk3288_grf_dphy_regs,
> +};
> +
> +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> + .clks = rk3399_mipidphy_clks,
> + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> + .regs = rk3399_grf_dphy_regs,
> +};
> +
> +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> + {
> + .compatible = "rockchip,rk3399-mipi-dphy",
> + .data = &rk3399_mipidphy_drv_data,
> + },
> + {
> + .compatible = "rockchip,rk3288-mipi-dphy",
> + .data = &rk3288_mipidphy_drv_data,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
You can mode this just above the probe function to group it with the
code that uses it.
> +
> +/* The .bound() notifier callback when a match is found */
> +static int
> +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct sensor_async_subdev *s_asd = container_of(asd,
> + struct sensor_async_subdev, asd);
> + struct mipidphy_sensor *sensor;
> + unsigned int pad, ret;
> +
> + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> + return -EBUSY;
> +
> + sensor = &priv->sensors[priv->num_sensors++];
> + sensor->lanes = s_asd->lanes;
> + sensor->mbus = s_asd->mbus;
> + sensor->sd = sd;
> +
> + 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(priv->dev,
> + "failed to find src pad for %s\n",
> + sensor->sd->name);
> +
> + return -ENXIO;
> + }
> +
> + ret = media_create_pad_link(
> + &sensor->sd->entity, pad,
> + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to create link for %s\n",
> + sensor->sd->name);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* The .unbind callback */
> +static void
> +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> + struct v4l2_subdev *sd,
> + struct v4l2_async_subdev *asd)
> +{
> + struct mipidphy_priv *priv = container_of(notifier,
> + struct mipidphy_priv,
> + notifier);
> + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> +
> + sensor->sd = NULL;
> +}
> +
> +static const struct
> +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> + .bound = rockchip_mipidphy_notifier_bound,
> + .unbind = rockchip_mipidphy_notifier_unbind,
> +};
> +
> +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> + struct v4l2_fwnode_endpoint *vep,
> + struct v4l2_async_subdev *asd)
> +{
> + struct sensor_async_subdev *s_asd =
> + container_of(asd, struct sensor_async_subdev, asd);
> + struct v4l2_mbus_config *config = &s_asd->mbus;
> +
> + if (vep->bus_type != V4L2_MBUS_CSI2) {
> + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> + return -EINVAL;
> + }
> +
> + if (vep->base.port != 0) {
> + dev_err(dev, "The PHY has only port 0\n");
> + return -EINVAL;
> + }
> +
> + config->type = V4L2_MBUS_CSI2;
> + config->flags = vep->bus.mipi_csi2.flags;
> + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> +
> + switch (vep->bus.mipi_csi2.num_data_lanes) {
> + case 1:
> + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> + break;
> + case 2:
> + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> + break;
> + case 3:
> + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> + break;
> + case 4:
> + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> +{
> + int ret;
> +
> + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> +
> + ret = media_entity_pads_init(&priv->sd.entity,
> + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> + if (ret < 0)
> + return ret;
> +
> + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> + priv->dev, &priv->notifier,
> + sizeof(struct sensor_async_subdev), 0,
> + rockchip_mipidphy_fwnode_parse);
> + if (ret < 0)
> + return ret;
> +
> + if (!priv->notifier.num_subdevs)
> + return -ENODEV; /* no endpoint */
> +
> + priv->sd.subdev_notifier = &priv->notifier;
> + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> + if (ret) {
> + dev_err(priv->dev,
> + "failed to register async notifier : %d\n", ret);
> + v4l2_async_notifier_cleanup(&priv->notifier);
> + return ret;
> + }
> +
> + return v4l2_async_register_subdev(&priv->sd);
> +}
> +
> +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct v4l2_subdev *sd;
> + struct mipidphy_priv *priv;
> + struct regmap *grf;
> + struct resource *res;
> + const struct of_device_id *of_id;
> + const struct dphy_drv_data *drv_data;
> + int i, ret;
> +
> + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + return -ENOMEM;
> + priv->dev = dev;
> +
> + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> + if (!of_id)
> + return -EINVAL;
> +
> + grf = syscon_node_to_regmap(dev->parent->of_node);
> + if (IS_ERR(grf)) {
> + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> + "rockchip,grf");
> + if (IS_ERR(grf)) {
> + dev_err(dev, "Can't find GRF syscon\n");
> + return -ENODEV;
> + }
> + }
> + priv->regmap_grf = grf;
> +
> + drv_data = of_id->data;
> + for (i = 0; i < drv_data->num_clks; i++) {
> + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> +
> + if (IS_ERR(priv->clks[i])) {
> + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> + return PTR_ERR(priv->clks[i]);
> + }
> + }
> +
> + priv->grf_regs = drv_data->regs;
> + priv->drv_data = drv_data;
> + priv->stream_on = mipidphy_txrx_stream_on;
> + priv->txrx_base_addr = NULL;
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
This will result in an error being printed to the kernel log if res is
NULL. The following (untested) code should fix it.
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
priv->txrx_base_addr = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->txrx_base_addr))
return PTR_ERR(priv->txrx_base_addr);
priv->stream_on = mipidphy_txrx_stream_on;
} else {
priv->txrx_base_addr = NULL;
priv->stream_on = mipidphy_rx_stream_on;
}
Furthermore, txrx_base_addr seems to be used to access the CSIHOST
registers, part of the CSI-2 receiver (CSI host in the system connection
description of the RK3288 datasheet, in the MIPI CSI PHY section), not
the PHY itself. I'm afraid you'll have to redesign this to split the
code between those two components. One option would be to have a CSI
host DT node with a reg resource for the CSIHOST registers, modeled as a
V4L2 subdev, and pointing to the DPHY using a phy-handle property. In
the rkisp1 driver, I would then extract the CSI-2 receiver code to a
separate subdev, and handle the PHY from that subdev, also with a
phy-handle property in the rkisp1 DT node.
> + if (IS_ERR(priv->txrx_base_addr))
> + priv->stream_on = mipidphy_rx_stream_on;
> +
> + sd = &priv->sd;
> + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> + sd->dev = dev;
> +
> + platform_set_drvdata(pdev, &sd->entity);
> +
> + ret = rockchip_mipidphy_media_init(priv);
> + if (ret < 0)
> + return ret;
> +
> + pm_runtime_enable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> +{
> + struct media_entity *me = platform_get_drvdata(pdev);
> + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> +
> + media_entity_cleanup(&sd->entity);
> +
> + pm_runtime_disable(&pdev->dev);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> + mipidphy_runtime_resume, NULL)
> +};
> +
> +static struct platform_driver rockchip_isp_mipidphy_driver = {
> + .probe = rockchip_mipidphy_probe,
> + .remove = rockchip_mipidphy_remove,
> + .driver = {
> + .name = "rockchip-sy-mipi-dphy",
> + .pm = &rockchip_mipidphy_pm_ops,
> + .of_match_table = rockchip_mipidphy_match_id,
Too much indentation.
> + },
> +};
> +
> +module_platform_driver(rockchip_isp_mipidphy_driver);
dev.c also has a module_platform_driver(). As both are compiled in the
same module, this results in a link error when compiling the drivers as
a module. I would recommend separating it into two modules to fix this.
> +MODULE_AUTHOR("Rockchip Camera/ISP team");
> +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> new file mode 100644
> index 000000000000..c558791064a2
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> @@ -0,0 +1,15 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip MIPI Synopsys DPHY driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + */
> +
> +#ifndef __MIPI_DPHY_SY_H__
> +#define __MIPI_DPHY_SY_H__
> +
> +#include <media/v4l2-subdev.h>
> +
> +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
This function doesn't seem to be used or defined anywhere, you can drop
this file.
> +
> +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
--
Regards,
Laurent Pinchart
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver
2019-03-10 17:49 ` Laurent Pinchart
@ 2019-03-11 9:37 ` Tomasz Figa
0 siblings, 0 replies; 51+ messages in thread
From: Tomasz Figa @ 2019-03-11 9:37 UTC (permalink / raw)
To: Laurent Pinchart
Cc: open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, 钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner,
胡克俊
Hi Laurent,
On Mon, Mar 11, 2019 at 2:49 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Jacob,
>
> A few more comments on the code this time.
>
> First of all, this has bit-rotten a bit and doesn't compile. The
> following patch fixes it. Feel free to squash it into this patch (no
> need to credit me or add my SoB line).
Thanks a lot.
-Jacob, who's not working on this driver anymore.
+胡克俊 (William), who's been looking into reviving this series.
>
> commit 297399bb5e3ac8d50f27f1c911fe7e5f26983e56
> Author: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Date: Sat Mar 2 02:18:19 2019 +0200
>
> media: rkisp1: Fix compilation errors
>
> The code has bit-rotten since March 2018, fix compilation errors.
>
> The new V4L2 async notifier API requires notifiers to be initialized by
> a call to v4l2_async_notifier_init() before being used, do so.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> index 32140960557a..8a7d070314c9 100644
> --- a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> @@ -697,7 +697,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> container_of(asd, struct sensor_async_subdev, asd);
> struct v4l2_mbus_config *config = &s_asd->mbus;
>
> - if (vep->bus_type != V4L2_MBUS_CSI2) {
> + if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) {
> dev_err(dev, "Only CSI2 bus type is currently supported\n");
> return -EINVAL;
> }
> @@ -707,7 +707,7 @@ static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> return -EINVAL;
> }
>
> - config->type = V4L2_MBUS_CSI2;
> + config->type = V4L2_MBUS_CSI2_DPHY;
> config->flags = vep->bus.mipi_csi2.flags;
> s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
>
> @@ -745,6 +745,8 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> if (ret < 0)
> return ret;
>
> + v4l2_async_notifier_init(&priv->notifier);
> +
> ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> priv->dev, &priv->notifier,
> sizeof(struct sensor_async_subdev), 0,
> @@ -752,7 +754,7 @@ static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> if (ret < 0)
> return ret;
>
> - if (!priv->notifier.num_subdevs)
> + if (list_empty(&priv->notifier.asd_list))
> return -ENODEV; /* no endpoint */
>
> priv->sd.subdev_notifier = &priv->notifier;
>
>
> Then, please see below for additional comments.
>
> On Thu, Mar 08, 2018 at 05:47:54PM +0800, Jacob Chen wrote:
> > From: Jacob Chen <jacob2.chen@rock-chips.com>
> >
> > This commit adds a subdev driver for Rockchip MIPI Synopsys DPHY driver
> >
> > Signed-off-by: Jacob Chen <jacob2.chen@rock-chips.com>
> > Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> > Signed-off-by: Tomasz Figa <tfiga@chromium.org>
> > ---
> > .../media/platform/rockchip/isp1/mipi_dphy_sy.c | 868 +++++++++++++++++++++
> > .../media/platform/rockchip/isp1/mipi_dphy_sy.h | 15 +
> > 2 files changed, 883 insertions(+)
> > create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > create mode 100644 drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> >
> > diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > new file mode 100644
> > index 000000000000..32140960557a
> > --- /dev/null
> > +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c
> > @@ -0,0 +1,868 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Rockchip MIPI Synopsys DPHY driver
> > + *
> > + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> > + */
> > +
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/module.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/regmap.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <media/media-entity.h>
> > +#include <media/v4l2-ctrls.h>
> > +#include <media/v4l2-fwnode.h>
> > +#include <media/v4l2-subdev.h>
> > +
> > +#define RK3288_GRF_SOC_CON6 0x025c
> > +#define RK3288_GRF_SOC_CON8 0x0264
> > +#define RK3288_GRF_SOC_CON9 0x0268
> > +#define RK3288_GRF_SOC_CON10 0x026c
> > +#define RK3288_GRF_SOC_CON14 0x027c
> > +#define RK3288_GRF_SOC_STATUS21 0x02d4
> > +#define RK3288_GRF_IO_VSEL 0x0380
> > +#define RK3288_GRF_SOC_CON15 0x03a4
> > +
> > +#define RK3399_GRF_SOC_CON9 0x6224
> > +#define RK3399_GRF_SOC_CON21 0x6254
> > +#define RK3399_GRF_SOC_CON22 0x6258
> > +#define RK3399_GRF_SOC_CON23 0x625c
> > +#define RK3399_GRF_SOC_CON24 0x6260
> > +#define RK3399_GRF_SOC_CON25 0x6264
> > +#define RK3399_GRF_SOC_STATUS1 0xe2a4
> > +
> > +#define CLOCK_LANE_HS_RX_CONTROL 0x34
> > +#define LANE0_HS_RX_CONTROL 0x44
> > +#define LANE1_HS_RX_CONTROL 0x54
> > +#define LANE2_HS_RX_CONTROL 0x84
> > +#define LANE3_HS_RX_CONTROL 0x94
> > +#define HS_RX_DATA_LANES_THS_SETTLE_CONTROL 0x75
> > +
> > +/*
> > + * CSI HOST
> > + */
> > +#define CSIHOST_PHY_TEST_CTRL0 0x30
> > +#define CSIHOST_PHY_TEST_CTRL1 0x34
> > +#define CSIHOST_PHY_SHUTDOWNZ 0x08
> > +#define CSIHOST_DPHY_RSTZ 0x0c
> > +
> > +#define PHY_TESTEN_ADDR (0x1 << 16)
> > +#define PHY_TESTEN_DATA (0x0 << 16)
> > +#define PHY_TESTCLK (0x1 << 1)
> > +#define PHY_TESTCLR (0x1 << 0)
> > +#define THS_SETTLE_COUNTER_THRESHOLD 0x04
> > +
> > +#define HIWORD_UPDATE(val, mask, shift) \
> > + ((val) << (shift) | (mask) << ((shift) + 16))
> > +
> > +enum mipi_dphy_sy_pads {
> > + MIPI_DPHY_SY_PAD_SINK = 0,
> > + MIPI_DPHY_SY_PAD_SOURCE,
> > + MIPI_DPHY_SY_PADS_NUM,
> > +};
> > +
> > +enum dphy_reg_id {
> > + GRF_DPHY_RX0_TURNDISABLE = 0,
> > + GRF_DPHY_RX0_FORCERXMODE,
> > + GRF_DPHY_RX0_FORCETXSTOPMODE,
> > + GRF_DPHY_RX0_ENABLE,
> > + GRF_DPHY_RX0_TESTCLR,
> > + GRF_DPHY_RX0_TESTCLK,
> > + GRF_DPHY_RX0_TESTEN,
> > + GRF_DPHY_RX0_TESTDIN,
> > + GRF_DPHY_RX0_TURNREQUEST,
> > + GRF_DPHY_RX0_TESTDOUT,
> > + GRF_DPHY_TX0_TURNDISABLE,
> > + GRF_DPHY_TX0_FORCERXMODE,
> > + GRF_DPHY_TX0_FORCETXSTOPMODE,
> > + GRF_DPHY_TX0_TURNREQUEST,
> > + GRF_DPHY_TX1RX1_TURNDISABLE,
> > + GRF_DPHY_TX1RX1_FORCERXMODE,
> > + GRF_DPHY_TX1RX1_FORCETXSTOPMODE,
> > + GRF_DPHY_TX1RX1_ENABLE,
> > + GRF_DPHY_TX1RX1_MASTERSLAVEZ,
> > + GRF_DPHY_TX1RX1_BASEDIR,
> > + GRF_DPHY_TX1RX1_ENABLECLK,
> > + GRF_DPHY_TX1RX1_TURNREQUEST,
> > + GRF_DPHY_RX1_SRC_SEL,
> > + /* rk3288 only */
> > + GRF_CON_DISABLE_ISP,
> > + GRF_CON_ISP_DPHY_SEL,
> > + GRF_DSI_CSI_TESTBUS_SEL,
> > + GRF_DVP_V18SEL,
> > + /* below is for rk3399 only */
> > + GRF_DPHY_RX0_CLK_INV_SEL,
> > + GRF_DPHY_RX1_CLK_INV_SEL,
> > +};
> > +
> > +struct dphy_reg {
> > + u32 offset;
> > + u32 mask;
> > + u32 shift;
> > +};
> > +
> > +#define PHY_REG(_offset, _width, _shift) \
> > + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, }
> > +
> > +static const struct dphy_reg rk3399_grf_dphy_regs[] = {
> > + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0),
> > + [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10),
> > + [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11),
> > + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0),
> > + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4),
> > + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8),
> > + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12),
> > + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0),
> > + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4),
> > + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8),
> > + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12),
> > + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0),
> > + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4),
> > + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8),
> > + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12),
> > + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0),
> > + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4),
> > + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5),
> > + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6),
> > + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7),
> > + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0),
> > + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8),
> > + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9),
> > + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10),
> > + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0),
> > +};
> > +
> > +static const struct dphy_reg rk3288_grf_dphy_regs[] = {
> > + [GRF_CON_DISABLE_ISP] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 0),
> > + [GRF_CON_ISP_DPHY_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 1),
> > + [GRF_DSI_CSI_TESTBUS_SEL] = PHY_REG(RK3288_GRF_SOC_CON6, 1, 14),
> > + [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 0),
> > + [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 4),
> > + [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON8, 4, 8),
> > + [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 0),
> > + [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 4),
> > + [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 8),
> > + [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON9, 4, 12),
> > + [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 0),
> > + [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 4),
> > + [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 8),
> > + [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3288_GRF_SOC_CON10, 4, 12),
> > + [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 0),
> > + [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 1),
> > + [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 2),
> > + [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3288_GRF_SOC_CON14, 8, 3),
> > + [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 12),
> > + [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 13),
> > + [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 14),
> > + [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3288_GRF_SOC_CON14, 1, 15),
> > + [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 0),
> > + [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 4, 4),
> > + [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3288_GRF_SOC_CON15, 3, 8),
> > + [GRF_DVP_V18SEL] = PHY_REG(RK3288_GRF_IO_VSEL, 1, 1),
> > + [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3288_GRF_SOC_STATUS21, 8, 0),
> > +};
> > +
> > +struct hsfreq_range {
> > + u32 range_h;
> > + u8 cfg_bit;
> > +};
> > +
> > +struct mipidphy_priv;
> > +
> > +struct dphy_drv_data {
> > + const char * const *clks;
> > + int num_clks;
> > + const struct hsfreq_range *hsfreq_ranges;
> > + int num_hsfreq_ranges;
> > + const struct dphy_reg *regs;
> > +};
> > +
> > +struct sensor_async_subdev {
> > + struct v4l2_async_subdev asd;
> > + struct v4l2_mbus_config mbus;
> > + int lanes;
> > +};
> > +
> > +#define MAX_DPHY_CLK 8
> > +#define MAX_DPHY_SENSORS 2
> > +
> > +struct mipidphy_sensor {
> > + struct v4l2_subdev *sd;
> > + struct v4l2_mbus_config mbus;
> > + int lanes;
> > +};
> > +
> > +struct mipidphy_priv {
> > + struct device *dev;
> > + struct regmap *regmap_grf;
> > + const struct dphy_reg *grf_regs;
> > + struct clk *clks[MAX_DPHY_CLK];
> > + const struct dphy_drv_data *drv_data;
> > + u64 data_rate_mbps;
> > + struct v4l2_async_notifier notifier;
> > + struct v4l2_subdev sd;
> > + struct media_pad pads[MIPI_DPHY_SY_PADS_NUM];
> > + struct mipidphy_sensor sensors[MAX_DPHY_SENSORS];
>
> Should we really hardcode the maximum number of sensors ? Wouldn't it be
> better to allocate this dynamically ?
>
> > + int num_sensors;
> > + bool is_streaming;
> > + void __iomem *txrx_base_addr;
> > + int (*stream_on)(struct mipidphy_priv *priv, struct v4l2_subdev *sd);
> > +};
> > +
> > +static inline struct mipidphy_priv *to_dphy_priv(struct v4l2_subdev *subdev)
> > +{
> > + return container_of(subdev, struct mipidphy_priv, sd);
> > +}
> > +
> > +static inline void write_grf_reg(struct mipidphy_priv *priv,
> > + int index, u8 value)
> > +{
> > + const struct dphy_reg *reg = &priv->grf_regs[index];
> > + unsigned int val = HIWORD_UPDATE(value, reg->mask, reg->shift);
> > +
> > + WARN_ON(!reg->offset);
> > + regmap_write(priv->regmap_grf, reg->offset, val);
> > +}
> > +
> > +static void mipidphy0_wr_reg(struct mipidphy_priv *priv,
> > + u8 test_code, u8 test_data)
> > +{
> > + /*
> > + * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content
> > + * is latched internally as the current test code. Test data is
> > + * programmed internally by rising edge on TESTCLK.
> > + */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_code);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTEN, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTDIN, test_data);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > +}
> > +
> > +static void mipidphy1_wr_reg(struct mipidphy_priv *priv, unsigned char addr,
> > + unsigned char data)
> > +{
> > + /*
> > + * TESTEN =1,TESTDIN=addr
> > + * TESTCLK=0
> > + * TESTEN =0,TESTDIN=data
> > + * TESTCLK=1
> > + */
> > + writel((PHY_TESTEN_ADDR | addr),
> > + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> > + writel(0x00, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + writel((PHY_TESTEN_DATA | data),
> > + priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL1);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > +}
> > +
> > +static struct v4l2_subdev *get_remote_sensor(struct v4l2_subdev *sd)
> > +{
> > + struct media_pad *local, *remote;
> > + struct media_entity *sensor_me;
> > +
> > + local = &sd->entity.pads[MIPI_DPHY_SY_PAD_SINK];
> > + remote = media_entity_remote_pad(local);
> > + if (!remote) {
> > + v4l2_warn(sd, "No link between dphy and sensor\n");
> > + return NULL;
> > + }
> > +
> > + sensor_me = media_entity_remote_pad(local)->entity;
> > + return media_entity_to_v4l2_subdev(sensor_me);
>
> You could call this at the beginning of mipidphy_s_stream_start() and
> pass the sensor pointer to mipidphy_get_sensor_data_rate() and the
> .stream_on() operations to avoid multiple costly lookups, or possibly
> cache it in the mipidphy_priv structure (in that case I'd reset it to
> NULL at stream off time).
>
> > +}
> > +
> > +static struct mipidphy_sensor *sd_to_sensor(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + int i;
> > +
> > + for (i = 0; i < priv->num_sensors; ++i)
> > + if (priv->sensors[i].sd == sd)
> > + return &priv->sensors[i];
> > +
> > + return NULL;
> > +}
> > +
> > +static int mipidphy_get_sensor_data_rate(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct v4l2_ctrl *link_freq;
> > + struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
> > + int ret;
> > +
> > + link_freq = v4l2_ctrl_find(sensor_sd->ctrl_handler, V4L2_CID_LINK_FREQ);
>
> The correct control for this is V4L2_CID_PIXEL_RATE. You will have to
> divide it by the number of lanes to get the data rate per lane, but
> there will be no need to multiply it by 2 as below.
>
> > + if (!link_freq) {
> > + v4l2_warn(sd, "No pixel rate control in subdev\n");
> > + return -EPIPE;
> > + }
> > +
> > + qm.index = v4l2_ctrl_g_ctrl(link_freq);
> > + ret = v4l2_querymenu(sensor_sd->ctrl_handler, &qm);
> > + if (ret < 0) {
> > + v4l2_err(sd, "Failed to get menu item\n");
> > + return ret;
> > + }
> > +
> > + if (!qm.value) {
> > + v4l2_err(sd, "Invalid link_freq\n");
> > + return -EINVAL;
> > + }
> > + priv->data_rate_mbps = qm.value * 2;
> > + do_div(priv->data_rate_mbps, 1000 * 1000);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream_start(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int ret = 0;
> > +
> > + if (priv->is_streaming)
> > + return 0;
> > +
> > + ret = mipidphy_get_sensor_data_rate(sd);
> > + if (ret < 0)
> > + return ret;
> > +
> > + priv->stream_on(priv, sd);
> > +
> > + priv->is_streaming = true;
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream_stop(struct v4l2_subdev *sd)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > +
> > + priv->is_streaming = false;
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_s_stream(struct v4l2_subdev *sd, int on)
> > +{
> > + if (on)
> > + return mipidphy_s_stream_start(sd);
> > + else
> > + return mipidphy_s_stream_stop(sd);
> > +}
> > +
> > +static int mipidphy_g_mbus_config(struct v4l2_subdev *sd,
> > + struct v4l2_mbus_config *config)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > +
> > + *config = sensor->mbus;
> > +
> > + return 0;
> > +}
>
> This seems like a hack :-(
>
> > +
> > +static int mipidphy_s_power(struct v4l2_subdev *sd, int on)
> > +{
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > +
> > + if (on)
> > + return pm_runtime_get_sync(priv->dev);
> > + else
> > + return pm_runtime_put(priv->dev);
> > +}
> > +
> > +static int mipidphy_runtime_suspend(struct device *dev)
> > +{
> > + struct media_entity *me = dev_get_drvdata(dev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int i, num_clks;
> > +
> > + num_clks = priv->drv_data->num_clks;
> > + for (i = num_clks - 1; i >= 0; i--)
> > + clk_disable_unprepare(priv->clks[i]);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_runtime_resume(struct device *dev)
> > +{
> > + struct media_entity *me = dev_get_drvdata(dev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > + struct mipidphy_priv *priv = to_dphy_priv(sd);
> > + int i, num_clks, ret;
> > +
> > + num_clks = priv->drv_data->num_clks;
> > + for (i = 0; i < num_clks; i++) {
> > + ret = clk_prepare_enable(priv->clks[i]);
> > + if (ret < 0)
> > + goto err;
> > + }
> > +
> > + return 0;
> > +err:
> > + while (--i >= 0)
> > + clk_disable_unprepare(priv->clks[i]);
> > + return ret;
> > +}
> > +
> > +/* dphy accepts all fmt/size from sensor */
> > +static int mipidphy_get_set_fmt(struct v4l2_subdev *sd,
> > + struct v4l2_subdev_pad_config *cfg,
> > + struct v4l2_subdev_format *fmt)
> > +{
> > + struct v4l2_subdev *sensor = get_remote_sensor(sd);
> > +
> > + /*
> > + * Do not allow format changes and just relay whatever
> > + * set currently in the sensor.
> > + */
> > + return v4l2_subdev_call(sensor, pad, get_fmt, NULL, fmt);
>
> It's userspace responsibility to propagate formats through the pipeline
> when using the MC API, you shouldn't access the format of the source
> subdev in this driver.
>
> > +}
> > +
> > +static const struct v4l2_subdev_pad_ops mipidphy_subdev_pad_ops = {
> > + .set_fmt = mipidphy_get_set_fmt,
> > + .get_fmt = mipidphy_get_set_fmt,
> > +};
> > +
> > +static const struct v4l2_subdev_core_ops mipidphy_core_ops = {
> > + .s_power = mipidphy_s_power,
> > +};
> > +
> > +static const struct v4l2_subdev_video_ops mipidphy_video_ops = {
> > + .g_mbus_config = mipidphy_g_mbus_config,
> > + .s_stream = mipidphy_s_stream,
> > +};
> > +
> > +static const struct v4l2_subdev_ops mipidphy_subdev_ops = {
> > + .core = &mipidphy_core_ops,
> > + .video = &mipidphy_video_ops,
> > + .pad = &mipidphy_subdev_pad_ops,
> > +};
> > +
> > +/* These tables must be sorted by .range_h ascending. */
> > +static const struct hsfreq_range rk3288_mipidphy_hsfreq_ranges[] = {
> > + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> > + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> > + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> > + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> > + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> > + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> > + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> > + { 999, 0x1a}
> > +};
> > +
> > +static const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = {
> > + { 89, 0x00}, { 99, 0x10}, { 109, 0x20}, { 129, 0x01},
> > + { 139, 0x11}, { 149, 0x21}, { 169, 0x02}, { 179, 0x12},
> > + { 199, 0x22}, { 219, 0x03}, { 239, 0x13}, { 249, 0x23},
> > + { 269, 0x04}, { 299, 0x14}, { 329, 0x05}, { 359, 0x15},
> > + { 399, 0x25}, { 449, 0x06}, { 499, 0x16}, { 549, 0x07},
> > + { 599, 0x17}, { 649, 0x08}, { 699, 0x18}, { 749, 0x09},
> > + { 799, 0x19}, { 849, 0x29}, { 899, 0x39}, { 949, 0x0a},
> > + { 999, 0x1a}, {1049, 0x2a}, {1099, 0x3a}, {1149, 0x0b},
> > + {1199, 0x1b}, {1249, 0x2b}, {1299, 0x3b}, {1349, 0x0c},
> > + {1399, 0x1c}, {1449, 0x2c}, {1500, 0x3c}
> > +};
> > +
> > +static const char * const rk3399_mipidphy_clks[] = {
> > + "dphy-ref",
> > + "dphy-cfg",
> > + "grf",
> > +};
> > +
> > +static const char * const rk3288_mipidphy_clks[] = {
> > + "dphy-ref",
> > + "pclk",
> > +};
> > +
> > +static int mipidphy_rx_stream_on(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > + const struct dphy_drv_data *drv_data = priv->drv_data;
> > + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> > + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> > + int i, hsfreq = 0;
> > +
> > + for (i = 0; i < num_hsfreq_ranges; i++) {
> > + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> > + hsfreq = hsfreq_ranges[i].cfg_bit;
> > + break;
> > + }
> > + }
> > + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_FORCERXMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0);
> > + /* Disable lan turn around, which is ignored in receive mode */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TURNREQUEST, 0);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf);
> > +
> > + write_grf_reg(priv, GRF_DPHY_RX0_ENABLE, GENMASK(sensor->lanes - 1, 0));
> > +
> > + /* dphy start */
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLK, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 1);
> > + usleep_range(100, 150);
> > + write_grf_reg(priv, GRF_DPHY_RX0_TESTCLR, 0);
> > + usleep_range(100, 150);
> > +
> > + /* set clock lane */
> > + /* HS hsfreq_range & lane 0 settle bypass */
> > + mipidphy0_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane0 */
> > + mipidphy0_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> > + /* HS RX Control of lane1 */
> > + mipidphy0_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane2 */
> > + mipidphy0_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> > + /* HS RX Control of lane3 */
> > + mipidphy0_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> > + /* HS RX Data Lanes Settle State Time Control */
> > + mipidphy0_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> > + THS_SETTLE_COUNTER_THRESHOLD);
> > +
> > + /* Normal operation */
> > + mipidphy0_wr_reg(priv, 0x0, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static int mipidphy_txrx_stream_on(struct mipidphy_priv *priv,
> > + struct v4l2_subdev *sd)
> > +{
> > + struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sensor_sd);
> > + const struct dphy_drv_data *drv_data = priv->drv_data;
> > + const struct hsfreq_range *hsfreq_ranges = drv_data->hsfreq_ranges;
> > + int num_hsfreq_ranges = drv_data->num_hsfreq_ranges;
> > + int i, hsfreq = 0;
> > +
> > + for (i = 0; i < num_hsfreq_ranges; i++) {
> > + if (hsfreq_ranges[i].range_h >= priv->data_rate_mbps) {
> > + hsfreq = hsfreq_ranges[i].cfg_bit;
> > + break;
> > + }
> > + }
> > + write_grf_reg(priv, GRF_CON_ISP_DPHY_SEL, 1);
> > + write_grf_reg(priv, GRF_DSI_CSI_TESTBUS_SEL, 1);
> > + write_grf_reg(priv, GRF_DPHY_RX1_SRC_SEL, 1);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_MASTERSLAVEZ, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_BASEDIR, 1);
> > + /* Disable lan turn around, which is ignored in receive mode */
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCERXMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNREQUEST, 0);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_TURNDISABLE, 0xf);
> > + write_grf_reg(priv, GRF_DPHY_TX1RX1_ENABLE,
> > + GENMASK(sensor->lanes - 1, 0));
> > + /* dphy start */
> > + writel(0, priv->txrx_base_addr + CSIHOST_PHY_SHUTDOWNZ);
> > + writel(0, priv->txrx_base_addr + CSIHOST_DPHY_RSTZ);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + writel(PHY_TESTCLR, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + usleep_range(100, 150);
> > + writel(PHY_TESTCLK, priv->txrx_base_addr + CSIHOST_PHY_TEST_CTRL0);
> > + usleep_range(100, 150);
> > +
> > + /* set clock lane */
> > + mipidphy1_wr_reg(priv, CLOCK_LANE_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE0_HS_RX_CONTROL, hsfreq << 1);
> > + mipidphy1_wr_reg(priv, LANE1_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE2_HS_RX_CONTROL, 0);
> > + mipidphy1_wr_reg(priv, LANE3_HS_RX_CONTROL, 0);
> > + /* HS RX Data Lanes Settle State Time Control */
> > + mipidphy1_wr_reg(priv, HS_RX_DATA_LANES_THS_SETTLE_CONTROL,
> > + THS_SETTLE_COUNTER_THRESHOLD);
> > +
> > + /* Normal operation */
> > + mipidphy1_wr_reg(priv, 0x0, 0);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct dphy_drv_data rk3288_mipidphy_drv_data = {
> > + .clks = rk3288_mipidphy_clks,
> > + .num_clks = ARRAY_SIZE(rk3288_mipidphy_clks),
> > + .hsfreq_ranges = rk3288_mipidphy_hsfreq_ranges,
> > + .num_hsfreq_ranges = ARRAY_SIZE(rk3288_mipidphy_hsfreq_ranges),
> > + .regs = rk3288_grf_dphy_regs,
> > +};
> > +
> > +static const struct dphy_drv_data rk3399_mipidphy_drv_data = {
> > + .clks = rk3399_mipidphy_clks,
> > + .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks),
> > + .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges,
> > + .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges),
> > + .regs = rk3399_grf_dphy_regs,
> > +};
> > +
> > +static const struct of_device_id rockchip_mipidphy_match_id[] = {
> > + {
> > + .compatible = "rockchip,rk3399-mipi-dphy",
> > + .data = &rk3399_mipidphy_drv_data,
> > + },
> > + {
> > + .compatible = "rockchip,rk3288-mipi-dphy",
> > + .data = &rk3288_mipidphy_drv_data,
> > + },
> > + {}
> > +};
> > +MODULE_DEVICE_TABLE(of, rockchip_mipidphy_match_id);
>
> You can mode this just above the probe function to group it with the
> code that uses it.
>
> > +
> > +/* The .bound() notifier callback when a match is found */
> > +static int
> > +rockchip_mipidphy_notifier_bound(struct v4l2_async_notifier *notifier,
> > + struct v4l2_subdev *sd,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct mipidphy_priv *priv = container_of(notifier,
> > + struct mipidphy_priv,
> > + notifier);
> > + struct sensor_async_subdev *s_asd = container_of(asd,
> > + struct sensor_async_subdev, asd);
> > + struct mipidphy_sensor *sensor;
> > + unsigned int pad, ret;
> > +
> > + if (priv->num_sensors == ARRAY_SIZE(priv->sensors))
> > + return -EBUSY;
> > +
> > + sensor = &priv->sensors[priv->num_sensors++];
> > + sensor->lanes = s_asd->lanes;
> > + sensor->mbus = s_asd->mbus;
> > + sensor->sd = sd;
> > +
> > + 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(priv->dev,
> > + "failed to find src pad for %s\n",
> > + sensor->sd->name);
> > +
> > + return -ENXIO;
> > + }
> > +
> > + ret = media_create_pad_link(
> > + &sensor->sd->entity, pad,
> > + &priv->sd.entity, MIPI_DPHY_SY_PAD_SINK,
> > + priv->num_sensors != 1 ? 0 : MEDIA_LNK_FL_ENABLED);
> > + if (ret) {
> > + dev_err(priv->dev,
> > + "failed to create link for %s\n",
> > + sensor->sd->name);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* The .unbind callback */
> > +static void
> > +rockchip_mipidphy_notifier_unbind(struct v4l2_async_notifier *notifier,
> > + struct v4l2_subdev *sd,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct mipidphy_priv *priv = container_of(notifier,
> > + struct mipidphy_priv,
> > + notifier);
> > + struct mipidphy_sensor *sensor = sd_to_sensor(priv, sd);
> > +
> > + sensor->sd = NULL;
> > +}
> > +
> > +static const struct
> > +v4l2_async_notifier_operations rockchip_mipidphy_async_ops = {
> > + .bound = rockchip_mipidphy_notifier_bound,
> > + .unbind = rockchip_mipidphy_notifier_unbind,
> > +};
> > +
> > +static int rockchip_mipidphy_fwnode_parse(struct device *dev,
> > + struct v4l2_fwnode_endpoint *vep,
> > + struct v4l2_async_subdev *asd)
> > +{
> > + struct sensor_async_subdev *s_asd =
> > + container_of(asd, struct sensor_async_subdev, asd);
> > + struct v4l2_mbus_config *config = &s_asd->mbus;
> > +
> > + if (vep->bus_type != V4L2_MBUS_CSI2) {
> > + dev_err(dev, "Only CSI2 bus type is currently supported\n");
> > + return -EINVAL;
> > + }
> > +
> > + if (vep->base.port != 0) {
> > + dev_err(dev, "The PHY has only port 0\n");
> > + return -EINVAL;
> > + }
> > +
> > + config->type = V4L2_MBUS_CSI2;
> > + config->flags = vep->bus.mipi_csi2.flags;
> > + s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
> > +
> > + switch (vep->bus.mipi_csi2.num_data_lanes) {
> > + case 1:
> > + config->flags |= V4L2_MBUS_CSI2_1_LANE;
> > + break;
> > + case 2:
> > + config->flags |= V4L2_MBUS_CSI2_2_LANE;
> > + break;
> > + case 3:
> > + config->flags |= V4L2_MBUS_CSI2_3_LANE;
> > + break;
> > + case 4:
> > + config->flags |= V4L2_MBUS_CSI2_4_LANE;
> > + break;
> > + default:
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int rockchip_mipidphy_media_init(struct mipidphy_priv *priv)
> > +{
> > + int ret;
> > +
> > + priv->pads[MIPI_DPHY_SY_PAD_SOURCE].flags =
> > + MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT;
> > + priv->pads[MIPI_DPHY_SY_PAD_SINK].flags =
> > + MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
> > +
> > + ret = media_entity_pads_init(&priv->sd.entity,
> > + MIPI_DPHY_SY_PADS_NUM, priv->pads);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = v4l2_async_notifier_parse_fwnode_endpoints_by_port(
> > + priv->dev, &priv->notifier,
> > + sizeof(struct sensor_async_subdev), 0,
> > + rockchip_mipidphy_fwnode_parse);
> > + if (ret < 0)
> > + return ret;
> > +
> > + if (!priv->notifier.num_subdevs)
> > + return -ENODEV; /* no endpoint */
> > +
> > + priv->sd.subdev_notifier = &priv->notifier;
> > + priv->notifier.ops = &rockchip_mipidphy_async_ops;
> > + ret = v4l2_async_subdev_notifier_register(&priv->sd, &priv->notifier);
> > + if (ret) {
> > + dev_err(priv->dev,
> > + "failed to register async notifier : %d\n", ret);
> > + v4l2_async_notifier_cleanup(&priv->notifier);
> > + return ret;
> > + }
> > +
> > + return v4l2_async_register_subdev(&priv->sd);
> > +}
> > +
> > +static int rockchip_mipidphy_probe(struct platform_device *pdev)
> > +{
> > + struct device *dev = &pdev->dev;
> > + struct v4l2_subdev *sd;
> > + struct mipidphy_priv *priv;
> > + struct regmap *grf;
> > + struct resource *res;
> > + const struct of_device_id *of_id;
> > + const struct dphy_drv_data *drv_data;
> > + int i, ret;
> > +
> > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> > + if (!priv)
> > + return -ENOMEM;
> > + priv->dev = dev;
> > +
> > + of_id = of_match_device(rockchip_mipidphy_match_id, dev);
> > + if (!of_id)
> > + return -EINVAL;
> > +
> > + grf = syscon_node_to_regmap(dev->parent->of_node);
> > + if (IS_ERR(grf)) {
> > + grf = syscon_regmap_lookup_by_phandle(dev->of_node,
> > + "rockchip,grf");
> > + if (IS_ERR(grf)) {
> > + dev_err(dev, "Can't find GRF syscon\n");
> > + return -ENODEV;
> > + }
> > + }
> > + priv->regmap_grf = grf;
> > +
> > + drv_data = of_id->data;
> > + for (i = 0; i < drv_data->num_clks; i++) {
> > + priv->clks[i] = devm_clk_get(dev, drv_data->clks[i]);
> > +
> > + if (IS_ERR(priv->clks[i])) {
> > + dev_err(dev, "Failed to get %s\n", drv_data->clks[i]);
> > + return PTR_ERR(priv->clks[i]);
> > + }
> > + }
> > +
> > + priv->grf_regs = drv_data->regs;
> > + priv->drv_data = drv_data;
> > + priv->stream_on = mipidphy_txrx_stream_on;
> > + priv->txrx_base_addr = NULL;
> > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + priv->txrx_base_addr = devm_ioremap_resource(dev, res);
>
> This will result in an error being printed to the kernel log if res is
> NULL. The following (untested) code should fix it.
>
> res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> if (res) {
> priv->txrx_base_addr = devm_ioremap_resource(dev, res);
> if (IS_ERR(priv->txrx_base_addr))
> return PTR_ERR(priv->txrx_base_addr);
>
> priv->stream_on = mipidphy_txrx_stream_on;
> } else {
> priv->txrx_base_addr = NULL;
> priv->stream_on = mipidphy_rx_stream_on;
> }
>
> Furthermore, txrx_base_addr seems to be used to access the CSIHOST
> registers, part of the CSI-2 receiver (CSI host in the system connection
> description of the RK3288 datasheet, in the MIPI CSI PHY section), not
> the PHY itself. I'm afraid you'll have to redesign this to split the
> code between those two components. One option would be to have a CSI
> host DT node with a reg resource for the CSIHOST registers, modeled as a
> V4L2 subdev, and pointing to the DPHY using a phy-handle property. In
> the rkisp1 driver, I would then extract the CSI-2 receiver code to a
> separate subdev, and handle the PHY from that subdev, also with a
> phy-handle property in the rkisp1 DT node.
>
> > + if (IS_ERR(priv->txrx_base_addr))
> > + priv->stream_on = mipidphy_rx_stream_on;
> > +
> > + sd = &priv->sd;
> > + v4l2_subdev_init(sd, &mipidphy_subdev_ops);
> > + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > + snprintf(sd->name, sizeof(sd->name), "rockchip-sy-mipi-dphy");
> > + sd->dev = dev;
> > +
> > + platform_set_drvdata(pdev, &sd->entity);
> > +
> > + ret = rockchip_mipidphy_media_init(priv);
> > + if (ret < 0)
> > + return ret;
> > +
> > + pm_runtime_enable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static int rockchip_mipidphy_remove(struct platform_device *pdev)
> > +{
> > + struct media_entity *me = platform_get_drvdata(pdev);
> > + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
> > +
> > + media_entity_cleanup(&sd->entity);
> > +
> > + pm_runtime_disable(&pdev->dev);
> > +
> > + return 0;
> > +}
> > +
> > +static const struct dev_pm_ops rockchip_mipidphy_pm_ops = {
> > + SET_RUNTIME_PM_OPS(mipidphy_runtime_suspend,
> > + mipidphy_runtime_resume, NULL)
> > +};
> > +
> > +static struct platform_driver rockchip_isp_mipidphy_driver = {
> > + .probe = rockchip_mipidphy_probe,
> > + .remove = rockchip_mipidphy_remove,
> > + .driver = {
> > + .name = "rockchip-sy-mipi-dphy",
> > + .pm = &rockchip_mipidphy_pm_ops,
> > + .of_match_table = rockchip_mipidphy_match_id,
>
> Too much indentation.
>
> > + },
> > +};
> > +
> > +module_platform_driver(rockchip_isp_mipidphy_driver);
>
> dev.c also has a module_platform_driver(). As both are compiled in the
> same module, this results in a link error when compiling the drivers as
> a module. I would recommend separating it into two modules to fix this.
>
> > +MODULE_AUTHOR("Rockchip Camera/ISP team");
> > +MODULE_DESCRIPTION("Rockchip MIPI DPHY driver");
> > +MODULE_LICENSE("Dual BSD/GPL");
> > diff --git a/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> > new file mode 100644
> > index 000000000000..c558791064a2
> > --- /dev/null
> > +++ b/drivers/media/platform/rockchip/isp1/mipi_dphy_sy.h
> > @@ -0,0 +1,15 @@
> > +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> > +/*
> > + * Rockchip MIPI Synopsys DPHY driver
> > + *
> > + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> > + */
> > +
> > +#ifndef __MIPI_DPHY_SY_H__
> > +#define __MIPI_DPHY_SY_H__
> > +
> > +#include <media/v4l2-subdev.h>
> > +
> > +void rkisp1_set_mipi_dphy_sy_lanes(struct v4l2_subdev *dphy, int lanes);
>
> This function doesn't seem to be used or defined anywhere, you can drop
> this file.
>
> > +
> > +#endif /* __RKISP1_MIPI_DPHY_SY_H__ */
>
> --
> Regards,
>
> Laurent Pinchart
^ permalink raw reply [flat|nested] 51+ messages in thread
* [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 13:57 ` Hans Verkuil
2018-05-03 9:09 ` Baruch Siach
2018-03-08 9:47 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Jacob Chen
` (4 subsequent siblings)
6 siblings, 2 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w, Jacob Chen,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA, Allon Huang,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Add the subdev driver for rockchip isp1.
Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Yichong Zhong <zyc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Jacob Chen <cc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Eddie Cai <eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Allon Huang <allon.huang-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Tomasz Figa <tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
drivers/media/platform/rockchip/isp1/rkisp1.c | 1177 +++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/rkisp1.h | 105 +++
2 files changed, 1282 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..bb16c8118c16
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
@@ -0,0 +1,1177 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#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;
+
+ 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;
+
+ 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;
+ 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;
+ }
+
+ 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_IE_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;
+
+ 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;
+ }
+
+ 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;
+}
+
+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 *isp_dev = sd_to_isp_dev(sd);
+ int ret;
+
+ v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
+
+ if (on) {
+ ret = pm_runtime_get_sync(isp_dev->dev);
+ if (ret < 0)
+ return ret;
+
+ rkisp1_config_clk(isp_dev);
+ } else {
+ ret = pm_runtime_put(isp_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..f0bbcbf0c827
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/rkisp1.h
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#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.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
@ 2018-03-09 13:57 ` Hans Verkuil
2018-05-03 9:09 ` Baruch Siach
1 sibling, 0 replies; 51+ messages in thread
From: Hans Verkuil @ 2018-03-09 13:57 UTC (permalink / raw)
To: Jacob Chen, linux-rockchip
Cc: linux-kernel, linux-arm-kernel, mchehab, linux-media,
sakari.ailus, hans.verkuil, tfiga, zhengsq, laurent.pinchart,
zyc, eddie.cai.linux, jeffy.chen, devicetree, heiko, Jacob Chen,
Jacob Chen, Allon Huang
On 08/03/18 10:47, Jacob Chen wrote:
> 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 | 1177 +++++++++++++++++++++++++
> drivers/media/platform/rockchip/isp1/rkisp1.h | 105 +++
> 2 files changed, 1282 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..bb16c8118c16
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/rkisp1.c
> @@ -0,0 +1,1177 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +#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;
> +
> + 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;
> +
> + 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;
> + 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;
> + }
> +
> + 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_IE_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;
> +
> + 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;
> + }
> +
> + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) {
This is valid for TGT_CROP, but I'm not sure about TGT_CROP_BOUNDS.
For PAD_SINK the crop bounds are that of v4l2_subdev_get_try_format(sd, cfg, PAD_SINK),
and for PAD_SOURCE_PATH is would be v4l2_subdev_get_try_crop(sd, cfg, PAD_SINK).
I think.
Right now with this code it would (when in TRY mode) return the 'try-crop' value
instead of a valid CROP_BOUNDS.
> + struct v4l2_rect *try_sel;
> +
> + try_sel = v4l2_subdev_get_try_crop(sd, cfg, sel->pad);
> + sel->r = *try_sel;
> + }
> +
> + 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 *isp_dev = sd_to_isp_dev(sd);
> + int ret;
> +
> + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
> +
> + if (on) {
> + ret = pm_runtime_get_sync(isp_dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + rkisp1_config_clk(isp_dev);
> + } else {
> + ret = pm_runtime_put(isp_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..f0bbcbf0c827
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/isp1/rkisp1.h
> @@ -0,0 +1,105 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Rockchip isp1 driver
> + *
> + * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
> + */
> +
> +#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 */
>
Regards,
Hans
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
2018-03-09 13:57 ` Hans Verkuil
@ 2018-05-03 9:09 ` Baruch Siach
2018-05-07 6:13 ` Tomasz Figa
1 sibling, 1 reply; 51+ messages in thread
From: Baruch Siach @ 2018-05-03 9:09 UTC (permalink / raw)
To: Jacob Chen
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, mchehab,
linux-media, sakari.ailus, hans.verkuil, tfiga, zhengsq,
laurent.pinchart, zyc, eddie.cai.linux, jeffy.chen, devicetree,
heiko, Jacob Chen, Jacob Chen, Allon Huang
Hi Jacob,
On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> +{
> + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> + int ret;
> +
> + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n", on);
> +
> + if (on) {
> + ret = pm_runtime_get_sync(isp_dev->dev);
> + if (ret < 0)
> + return ret;
> +
> + rkisp1_config_clk(isp_dev);
> + } else {
> + ret = pm_runtime_put(isp_dev->dev);
I commented this line out to make more than one STREAMON work. Otherwise, the
second STREAMON hangs. I guess the bug is not this driver. Probably something
in drivers/soc/rockchip/pm_domains.c. Just noting that in case you or someone
on Cc would like to investigate it further.
I tested v4.16-rc4 on the Tinkerboard.
baruch
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-03 9:09 ` Baruch Siach
@ 2018-05-07 6:13 ` Tomasz Figa
2018-05-07 6:38 ` Baruch Siach
0 siblings, 1 reply; 51+ messages in thread
From: Tomasz Figa @ 2018-05-07 6:13 UTC (permalink / raw)
To: baruch
Cc: Jacob Chen, open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, Laurent Pinchart,
钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner
Hi Baruch,
On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Jacob,
> On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > +{
> > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > + int ret;
> > +
> > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
on);
> > +
> > + if (on) {
> > + ret = pm_runtime_get_sync(isp_dev->dev);
> > + if (ret < 0)
> > + return ret;
> > +
> > + rkisp1_config_clk(isp_dev);
> > + } else {
> > + ret = pm_runtime_put(isp_dev->dev);
> I commented this line out to make more than one STREAMON work. Otherwise,
the
> second STREAMON hangs. I guess the bug is not this driver. Probably
something
> in drivers/soc/rockchip/pm_domains.c. Just noting that in case you or
someone
> on Cc would like to investigate it further.
> I tested v4.16-rc4 on the Tinkerboard.
Looks like that version doesn't include the IOMMU PM and clock handling
rework [1], which should fix a lot of runtime PM issues. FWIW, linux-next
seems to already include it.
[1] https://lkml.org/lkml/2018/3/23/44
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:13 ` Tomasz Figa
@ 2018-05-07 6:38 ` Baruch Siach
2018-05-07 6:41 ` Tomasz Figa
0 siblings, 1 reply; 51+ messages in thread
From: Baruch Siach @ 2018-05-07 6:38 UTC (permalink / raw)
To: Tomasz Figa
Cc: Jacob Chen, open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, Laurent Pinchart,
钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner
Hi Tomasz,
On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > +{
> > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > + int ret;
> > > +
> > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> on);
> > > +
> > > + if (on) {
> > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + rkisp1_config_clk(isp_dev);
> > > + } else {
> > > + ret = pm_runtime_put(isp_dev->dev);
>
> > I commented this line out to make more than one STREAMON work. Otherwise,
> > the second STREAMON hangs. I guess the bug is not this driver. Probably
> > something in drivers/soc/rockchip/pm_domains.c. Just noting that in case
> > you or someone on Cc would like to investigate it further.
> >
> > I tested v4.16-rc4 on the Tinkerboard.
>
> Looks like that version doesn't include the IOMMU PM and clock handling
> rework [1], which should fix a lot of runtime PM issues. FWIW, linux-next
> seems to already include it.
>
> [1] https://lkml.org/lkml/2018/3/23/44
Thanks for the reference.
It looks like the iommu driver part is in Linus' tree already. The DT part is
in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
Anyway, I'll take a look.
Thanks again,
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:38 ` Baruch Siach
@ 2018-05-07 6:41 ` Tomasz Figa
2018-05-24 11:30 ` Baruch Siach
0 siblings, 1 reply; 51+ messages in thread
From: Tomasz Figa @ 2018-05-07 6:41 UTC (permalink / raw)
To: baruch
Cc: Jacob Chen, open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, Laurent Pinchart,
钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner
On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch@tkos.co.il> wrote:
> Hi Tomasz,
> On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > > +{
> > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > + int ret;
> > > > +
> > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> > on);
> > > > +
> > > > + if (on) {
> > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > + if (ret < 0)
> > > > + return ret;
> > > > +
> > > > + rkisp1_config_clk(isp_dev);
> > > > + } else {
> > > > + ret = pm_runtime_put(isp_dev->dev);
> >
> > > I commented this line out to make more than one STREAMON work.
Otherwise,
> > > the second STREAMON hangs. I guess the bug is not this driver.
Probably
> > > something in drivers/soc/rockchip/pm_domains.c. Just noting that in
case
> > > you or someone on Cc would like to investigate it further.
> > >
> > > I tested v4.16-rc4 on the Tinkerboard.
> >
> > Looks like that version doesn't include the IOMMU PM and clock handling
> > rework [1], which should fix a lot of runtime PM issues. FWIW,
linux-next
> > seems to already include it.
> >
> > [1] https://lkml.org/lkml/2018/3/23/44
> Thanks for the reference.
> It looks like the iommu driver part is in Linus' tree already. The DT
part is
> in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
You're right, most of the series made it in time for 4.17. However, the DT
part (precisely, the clocks properties added to IOMMU nodes) is crucial for
the fixes to be effective.
> Anyway, I'll take a look.
Thanks for testing. :) (Forgot to mention in my previous email...)
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-07 6:41 ` Tomasz Figa
@ 2018-05-24 11:30 ` Baruch Siach
2018-05-25 4:35 ` Tomasz Figa
0 siblings, 1 reply; 51+ messages in thread
From: Baruch Siach @ 2018-05-24 11:30 UTC (permalink / raw)
To: Tomasz Figa
Cc: Jacob Chen, ARM/Rockchip SoC, Linux Kernel Mailing List,
IOMMU DRIVERS, Joerg Roedel, linux-arm-kernel,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, Laurent Pinchart,
钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner
Hi Tomasz,
On Mon, May 07, 2018 at 06:41:50AM +0000, Tomasz Figa wrote:
> On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch@tkos.co.il> wrote:
> > > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int on)
> > > > > +{
> > > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > > + int ret;
> > > > > +
> > > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power: %d\n",
> > > on);
> > > > > +
> > > > > + if (on) {
> > > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > > + if (ret < 0)
> > > > > + return ret;
> > > > > +
> > > > > + rkisp1_config_clk(isp_dev);
> > > > > + } else {
> > > > > + ret = pm_runtime_put(isp_dev->dev);
> > >
> > > > I commented this line out to make more than one STREAMON work.
> Otherwise,
> > > > the second STREAMON hangs. I guess the bug is not this driver.
> Probably
> > > > something in drivers/soc/rockchip/pm_domains.c. Just noting that in
> case
> > > > you or someone on Cc would like to investigate it further.
> > > >
> > > > I tested v4.16-rc4 on the Tinkerboard.
> > >
> > > Looks like that version doesn't include the IOMMU PM and clock handling
> > > rework [1], which should fix a lot of runtime PM issues. FWIW,
> linux-next
> > > seems to already include it.
> > >
> > > [1] https://lkml.org/lkml/2018/3/23/44
>
> > Thanks for the reference.
>
> > It looks like the iommu driver part is in Linus' tree already. The DT
> part is
> > in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing anything?
>
> You're right, most of the series made it in time for 4.17. However, the DT
> part (precisely, the clocks properties added to IOMMU nodes) is crucial for
> the fixes to be effective.
>
> > Anyway, I'll take a look.
>
> Thanks for testing. :) (Forgot to mention in my previous email...)
I finally got around to testing. Unfortunately, kernel v4.17-rc6 with
cherry-pick of commit c78751f91c0b (ARM: dts: rockchip: add clocks in iommu
nodes) from Heiko's tree still exhibit the same problem. STREAMON hangs on
second try. The same workaround "fixes" it.
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver
2018-05-24 11:30 ` Baruch Siach
@ 2018-05-25 4:35 ` Tomasz Figa
0 siblings, 0 replies; 51+ messages in thread
From: Tomasz Figa @ 2018-05-25 4:35 UTC (permalink / raw)
To: baruch-NswTu9S1W3P6gbPvEgmw2w
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Eddie Cai, 陈城,
Heiko Stübner, Jeffy, 钟以崇,
Linux Kernel Mailing List, open list:ARM/Rockchip SoC...,
open list:IOMMU DRIVERS, Jacob Chen, Hans Verkuil,
Laurent Pinchart, Allon Huang, Mauro Carvalho Chehab,
Shunqian Zheng,
list-Y9sIeH5OGRo@public.gmane.org:IOMMU DRIVERS
<iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>,
Joerg Roedel <joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>,
, Linux Media Mailing List
On Thu, May 24, 2018 at 8:30 PM Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org> wrote:
> Hi Tomasz,
> On Mon, May 07, 2018 at 06:41:50AM +0000, Tomasz Figa wrote:
> > On Mon, May 7, 2018 at 3:38 PM Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org> wrote:
> > > On Mon, May 07, 2018 at 06:13:27AM +0000, Tomasz Figa wrote:
> > > > On Thu, May 3, 2018 at 6:09 PM Baruch Siach <baruch-NswTu9S1W3P6gbPvEgmw2w@public.gmane.org>
wrote:
> > > > > On Thu, Mar 08, 2018 at 05:47:55PM +0800, Jacob Chen wrote:
> > > > > > +static int rkisp1_isp_sd_s_power(struct v4l2_subdev *sd, int
on)
> > > > > > +{
> > > > > > + struct rkisp1_device *isp_dev = sd_to_isp_dev(sd);
> > > > > > + int ret;
> > > > > > +
> > > > > > + v4l2_dbg(1, rkisp1_debug, &isp_dev->v4l2_dev, "s_power:
%d\n",
> > > > on);
> > > > > > +
> > > > > > + if (on) {
> > > > > > + ret = pm_runtime_get_sync(isp_dev->dev);
> > > > > > + if (ret < 0)
> > > > > > + return ret;
> > > > > > +
> > > > > > + rkisp1_config_clk(isp_dev);
> > > > > > + } else {
> > > > > > + ret = pm_runtime_put(isp_dev->dev);
> > > >
> > > > > I commented this line out to make more than one STREAMON work.
> > Otherwise,
> > > > > the second STREAMON hangs. I guess the bug is not this driver.
> > Probably
> > > > > something in drivers/soc/rockchip/pm_domains.c. Just noting that
in
> > case
> > > > > you or someone on Cc would like to investigate it further.
> > > > >
> > > > > I tested v4.16-rc4 on the Tinkerboard.
> > > >
> > > > Looks like that version doesn't include the IOMMU PM and clock
handling
> > > > rework [1], which should fix a lot of runtime PM issues. FWIW,
> > linux-next
> > > > seems to already include it.
> > > >
> > > > [1] https://lkml.org/lkml/2018/3/23/44
> >
> > > Thanks for the reference.
> >
> > > It looks like the iommu driver part is in Linus' tree already. The DT
> > part is
> > > in the v4.18-armsoc/dts32 branch of Heiko's tree. Am I missing
anything?
> >
> > You're right, most of the series made it in time for 4.17. However, the
DT
> > part (precisely, the clocks properties added to IOMMU nodes) is crucial
for
> > the fixes to be effective.
> >
> > > Anyway, I'll take a look.
> >
> > Thanks for testing. :) (Forgot to mention in my previous email...)
> I finally got around to testing. Unfortunately, kernel v4.17-rc6 with
> cherry-pick of commit c78751f91c0b (ARM: dts: rockchip: add clocks in
iommu
> nodes) from Heiko's tree still exhibit the same problem. STREAMON hangs on
> second try. The same workaround "fixes" it.
Thanks for testing. I'm out of ideas, since the same code seems to work
fine for us in Chrome OS 4.4 kernel. Maybe we could have someone from RK
take a look.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread
* [PATCH v6 08/17] media: rkisp1: add capture device driver
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
2018-03-08 9:47 ` [PATCH v6 04/17] media: rkisp1: add Rockchip MIPI Synopsys DPHY driver Jacob Chen
2018-03-08 9:47 ` [PATCH v6 05/17] media: rkisp1: add Rockchip ISP1 subdev driver Jacob Chen
@ 2018-03-08 9:47 ` Jacob Chen
2018-03-09 14:07 ` Hans Verkuil
2018-04-17 8:44 ` Tomasz Figa
2018-03-08 9:48 ` [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
` (3 subsequent siblings)
6 siblings, 2 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:47 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w, Jacob Chen,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA, Allon Huang,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
This is the capture device interface driver that provides the v4l2
user interface. Frames can be received from ISP1.
Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Yichong Zhong <zyc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Jacob Chen <cc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Eddie Cai <eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Jeffy Chen <jeffy.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Allon Huang <allon.huang-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Tomasz Figa <tfiga-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
---
drivers/media/platform/rockchip/isp1/capture.c | 1751 ++++++++++++++++++++++++
drivers/media/platform/rockchip/isp1/capture.h | 167 +++
drivers/media/platform/rockchip/isp1/regs.c | 239 ++++
drivers/media/platform/rockchip/isp1/regs.h | 1550 +++++++++++++++++++++
4 files changed, 3707 insertions(+)
create mode 100644 drivers/media/platform/rockchip/isp1/capture.c
create mode 100644 drivers/media/platform/rockchip/isp1/capture.h
create mode 100644 drivers/media/platform/rockchip/isp1/regs.c
create mode 100644 drivers/media/platform/rockchip/isp1/regs.h
diff --git a/drivers/media/platform/rockchip/isp1/capture.c b/drivers/media/platform/rockchip/isp1/capture.c
new file mode 100644
index 000000000000..53484f882704
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/capture.c
@@ -0,0 +1,1751 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+#include "dev.h"
+#include "regs.h"
+
+/*
+ * NOTE:
+ * 1. There are two capture video devices in rkisp1, selfpath and mainpath
+ * 2. Two capture device have separated memory-interface/crop/scale units.
+ * 3. Besides describing stream hardware, this file also contain entries
+ * for pipeline operations.
+ * 4. The register read/write operations in this file are put into regs.c.
+ */
+
+/*
+ * differences between selfpatch and mainpath
+ * available mp sink input: isp
+ * available sp sink input : isp, dma(TODO)
+ * available mp sink pad fmts: yuv422, raw
+ * available sp sink pad fmts: yuv422, yuv420......
+ * available mp source fmts: yuv, raw, jpeg(TODO)
+ * available sp source fmts: yuv, rgb
+ */
+
+#define CIF_ISP_REQ_BUFS_MIN 1
+#define CIF_ISP_REQ_BUFS_MAX 8
+
+#define STREAM_PAD_SINK 0
+#define STREAM_PAD_SOURCE 1
+
+#define STREAM_MAX_MP_RSZ_OUTPUT_WIDTH 4416
+#define STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT 3312
+#define STREAM_MAX_SP_RSZ_OUTPUT_WIDTH 1920
+#define STREAM_MAX_SP_RSZ_OUTPUT_HEIGHT 1920
+#define STREAM_MIN_RSZ_OUTPUT_WIDTH 32
+#define STREAM_MIN_RSZ_OUTPUT_HEIGHT 16
+
+#define STREAM_MAX_MP_SP_INPUT_WIDTH STREAM_MAX_MP_RSZ_OUTPUT_WIDTH
+#define STREAM_MAX_MP_SP_INPUT_HEIGHT STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT
+#define STREAM_MIN_MP_SP_INPUT_WIDTH 32
+#define STREAM_MIN_MP_SP_INPUT_HEIGHT 32
+
+/* Get xsubs and ysubs for fourcc formats
+ *
+ * @xsubs: horizontal color samples in a 4*4 matrix, for yuv
+ * @ysubs: vertical color samples in a 4*4 matrix, for yuv
+ */
+static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs)
+{
+ switch (fcc) {
+ case V4L2_PIX_FMT_GREY:
+ case V4L2_PIX_FMT_YUV444M:
+ *xsubs = 1;
+ *ysubs = 1;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_YVU422M:
+ *xsubs = 2;
+ *ysubs = 1;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_NV12M:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ *xsubs = 2;
+ *ysubs = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mbus_code_xysubs(u32 code, u32 *xsubs, u32 *ysubs)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ *xsubs = 2;
+ *ysubs = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mbus_code_sp_in_fmt(u32 code, u32 *format)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ *format = MI_CTRL_SP_INPUT_YUV422;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct capture_fmt mp_fmts[] = {
+ /* yuv422 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUVINT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUVINT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUVINT,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 3,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ },
+ /* yuv420 */
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 2,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 2,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_SPLA,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ },
+ /* yuv444 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUV444M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 3,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ },
+ /* yuv400 */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_MP_WRITE_YUVINT,
+ },
+ /* raw */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 8 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 8 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 8 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 8 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 10 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 10 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 10 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 10 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 12 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 12 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 12 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .fmt_type = FMT_BAYER,
+ .bpp = { 12 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_MP_WRITE_RAW12,
+ },
+};
+
+static const struct capture_fmt sp_fmts[] = {
+ /* yuv422 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_INT,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_INT,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .fmt_type = FMT_YUV,
+ .bpp = { 16 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_INT,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 3,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV422,
+ },
+ /* yuv420 */
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 2,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 16 },
+ .cplanes = 2,
+ .mplanes = 2,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_SPLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 1,
+ .uv_swap = 1,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV420,
+ },
+ /* yuv444 */
+ {
+ .fourcc = V4L2_PIX_FMT_YUV444M,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8, 8, 8 },
+ .cplanes = 3,
+ .mplanes = 3,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV444,
+ },
+ /* yuv400 */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .fmt_type = FMT_YUV,
+ .bpp = { 8 },
+ .cplanes = 1,
+ .mplanes = 1,
+ .uv_swap = 0,
+ .write_format = MI_CTRL_SP_WRITE_INT,
+ .output_format = MI_CTRL_SP_OUTPUT_YUV400,
+ },
+ /* rgb */
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .fmt_type = FMT_RGB,
+ .bpp = { 24 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_RGB888,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .fmt_type = FMT_RGB,
+ .bpp = { 16 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_RGB565,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR666,
+ .fmt_type = FMT_RGB,
+ .bpp = { 18 },
+ .mplanes = 1,
+ .write_format = MI_CTRL_SP_WRITE_PLA,
+ .output_format = MI_CTRL_SP_OUTPUT_RGB666,
+ },
+};
+
+static struct stream_config rkisp1_mp_stream_config = {
+ .fmts = mp_fmts,
+ .fmt_size = ARRAY_SIZE(mp_fmts),
+ /* constraints */
+ .max_rsz_width = STREAM_MAX_MP_RSZ_OUTPUT_WIDTH,
+ .max_rsz_height = STREAM_MAX_MP_RSZ_OUTPUT_HEIGHT,
+ .min_rsz_width = STREAM_MIN_RSZ_OUTPUT_WIDTH,
+ .min_rsz_height = STREAM_MIN_RSZ_OUTPUT_HEIGHT,
+ /* registers */
+ .rsz = {
+ .ctrl = CIF_MRSZ_CTRL,
+ .scale_hy = CIF_MRSZ_SCALE_HY,
+ .scale_hcr = CIF_MRSZ_SCALE_HCR,
+ .scale_hcb = CIF_MRSZ_SCALE_HCB,
+ .scale_vy = CIF_MRSZ_SCALE_VY,
+ .scale_vc = CIF_MRSZ_SCALE_VC,
+ .scale_lut = CIF_MRSZ_SCALE_LUT,
+ .scale_lut_addr = CIF_MRSZ_SCALE_LUT_ADDR,
+ .scale_hy_shd = CIF_MRSZ_SCALE_HY_SHD,
+ .scale_hcr_shd = CIF_MRSZ_SCALE_HCR_SHD,
+ .scale_hcb_shd = CIF_MRSZ_SCALE_HCB_SHD,
+ .scale_vy_shd = CIF_MRSZ_SCALE_VY_SHD,
+ .scale_vc_shd = CIF_MRSZ_SCALE_VC_SHD,
+ .phase_hy = CIF_MRSZ_PHASE_HY,
+ .phase_hc = CIF_MRSZ_PHASE_HC,
+ .phase_vy = CIF_MRSZ_PHASE_VY,
+ .phase_vc = CIF_MRSZ_PHASE_VC,
+ .ctrl_shd = CIF_MRSZ_CTRL_SHD,
+ .phase_hy_shd = CIF_MRSZ_PHASE_HY_SHD,
+ .phase_hc_shd = CIF_MRSZ_PHASE_HC_SHD,
+ .phase_vy_shd = CIF_MRSZ_PHASE_VY_SHD,
+ .phase_vc_shd = CIF_MRSZ_PHASE_VC_SHD,
+ },
+ .dual_crop = {
+ .ctrl = CIF_DUAL_CROP_CTRL,
+ .yuvmode_mask = CIF_DUAL_CROP_MP_MODE_YUV,
+ .rawmode_mask = CIF_DUAL_CROP_MP_MODE_RAW,
+ .h_offset = CIF_DUAL_CROP_M_H_OFFS,
+ .v_offset = CIF_DUAL_CROP_M_V_OFFS,
+ .h_size = CIF_DUAL_CROP_M_H_SIZE,
+ .v_size = CIF_DUAL_CROP_M_V_SIZE,
+ },
+ .mi = {
+ .y_size_init = CIF_MI_MP_Y_SIZE_INIT,
+ .cb_size_init = CIF_MI_MP_CB_SIZE_INIT,
+ .cr_size_init = CIF_MI_MP_CR_SIZE_INIT,
+ .y_base_ad_init = CIF_MI_MP_Y_BASE_AD_INIT,
+ .cb_base_ad_init = CIF_MI_MP_CB_BASE_AD_INIT,
+ .cr_base_ad_init = CIF_MI_MP_CR_BASE_AD_INIT,
+ .y_offs_cnt_init = CIF_MI_MP_Y_OFFS_CNT_INIT,
+ .cb_offs_cnt_init = CIF_MI_MP_CB_OFFS_CNT_INIT,
+ .cr_offs_cnt_init = CIF_MI_MP_CR_OFFS_CNT_INIT,
+ },
+};
+
+static struct stream_config rkisp1_sp_stream_config = {
+ .fmts = sp_fmts,
+ .fmt_size = ARRAY_SIZE(sp_fmts),
+ /* constraints */
+ .max_rsz_width = STREAM_MAX_SP_RSZ_OUTPUT_WIDTH,
+ .max_rsz_height = STREAM_MAX_SP_RSZ_OUTPUT_HEIGHT,
+ .min_rsz_width = STREAM_MIN_RSZ_OUTPUT_WIDTH,
+ .min_rsz_height = STREAM_MIN_RSZ_OUTPUT_HEIGHT,
+ /* registers */
+ .rsz = {
+ .ctrl = CIF_SRSZ_CTRL,
+ .scale_hy = CIF_SRSZ_SCALE_HY,
+ .scale_hcr = CIF_SRSZ_SCALE_HCR,
+ .scale_hcb = CIF_SRSZ_SCALE_HCB,
+ .scale_vy = CIF_SRSZ_SCALE_VY,
+ .scale_vc = CIF_SRSZ_SCALE_VC,
+ .scale_lut = CIF_SRSZ_SCALE_LUT,
+ .scale_lut_addr = CIF_SRSZ_SCALE_LUT_ADDR,
+ .scale_hy_shd = CIF_SRSZ_SCALE_HY_SHD,
+ .scale_hcr_shd = CIF_SRSZ_SCALE_HCR_SHD,
+ .scale_hcb_shd = CIF_SRSZ_SCALE_HCB_SHD,
+ .scale_vy_shd = CIF_SRSZ_SCALE_VY_SHD,
+ .scale_vc_shd = CIF_SRSZ_SCALE_VC_SHD,
+ .phase_hy = CIF_SRSZ_PHASE_HY,
+ .phase_hc = CIF_SRSZ_PHASE_HC,
+ .phase_vy = CIF_SRSZ_PHASE_VY,
+ .phase_vc = CIF_SRSZ_PHASE_VC,
+ .ctrl_shd = CIF_SRSZ_CTRL_SHD,
+ .phase_hy_shd = CIF_SRSZ_PHASE_HY_SHD,
+ .phase_hc_shd = CIF_SRSZ_PHASE_HC_SHD,
+ .phase_vy_shd = CIF_SRSZ_PHASE_VY_SHD,
+ .phase_vc_shd = CIF_SRSZ_PHASE_VC_SHD,
+ },
+ .dual_crop = {
+ .ctrl = CIF_DUAL_CROP_CTRL,
+ .yuvmode_mask = CIF_DUAL_CROP_SP_MODE_YUV,
+ .rawmode_mask = CIF_DUAL_CROP_SP_MODE_RAW,
+ .h_offset = CIF_DUAL_CROP_S_H_OFFS,
+ .v_offset = CIF_DUAL_CROP_S_V_OFFS,
+ .h_size = CIF_DUAL_CROP_S_H_SIZE,
+ .v_size = CIF_DUAL_CROP_S_V_SIZE,
+ },
+ .mi = {
+ .y_size_init = CIF_MI_SP_Y_SIZE_INIT,
+ .cb_size_init = CIF_MI_SP_CB_SIZE_INIT,
+ .cr_size_init = CIF_MI_SP_CR_SIZE_INIT,
+ .y_base_ad_init = CIF_MI_SP_Y_BASE_AD_INIT,
+ .cb_base_ad_init = CIF_MI_SP_CB_BASE_AD_INIT,
+ .cr_base_ad_init = CIF_MI_SP_CR_BASE_AD_INIT,
+ .y_offs_cnt_init = CIF_MI_SP_Y_OFFS_CNT_INIT,
+ .cb_offs_cnt_init = CIF_MI_SP_CB_OFFS_CNT_INIT,
+ .cr_offs_cnt_init = CIF_MI_SP_CR_OFFS_CNT_INIT,
+ },
+};
+
+static const
+struct capture_fmt *find_fmt(struct rkisp1_stream *stream, const u32 pixelfmt)
+{
+ const struct capture_fmt *fmt;
+ int i;
+
+ for (i = 0; i < stream->config->fmt_size; i++) {
+ fmt = &stream->config->fmts[i];
+ if (fmt->fourcc == pixelfmt)
+ return fmt;
+ }
+ return NULL;
+}
+
+/* configure dual-crop unit */
+static int rkisp1_config_dcrop(struct rkisp1_stream *stream, bool async)
+{
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_rect *dcrop = &stream->dcrop;
+ struct v4l2_rect *input_win;
+
+ /* dual-crop unit get data from isp */
+ input_win = rkisp1_get_isp_sd_win(&dev->isp_sdev);
+
+ if (dcrop->width == input_win->width &&
+ dcrop->height == input_win->height &&
+ dcrop->left == 0 && dcrop->top == 0) {
+ disable_dcrop(stream, async);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "stream %d crop disabled\n", stream->id);
+ return 0;
+ }
+
+ config_dcrop(stream, dcrop, async);
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "stream %d crop: %dx%d -> %dx%d\n", stream->id,
+ input_win->width, input_win->height,
+ dcrop->width, dcrop->height);
+
+ return 0;
+}
+
+/* configure scale unit */
+static int rkisp1_config_rsz(struct rkisp1_stream *stream, bool async)
+{
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_pix_format_mplane output_fmt = stream->out_fmt;
+ struct capture_fmt *output_isp_fmt = &stream->out_isp_fmt;
+ struct ispsd_out_fmt *input_isp_fmt =
+ rkisp1_get_ispsd_out_fmt(&dev->isp_sdev);
+ struct v4l2_rect in_y, in_c, out_y, out_c;
+ u32 xsubs_in, ysubs_in, xsubs_out, ysubs_out;
+
+ if (input_isp_fmt->fmt_type == FMT_BAYER)
+ goto disable;
+
+ /* set input and output sizes for scale calculation */
+ in_y.width = stream->dcrop.width;
+ in_y.height = stream->dcrop.height;
+ out_y.width = output_fmt.width;
+ out_y.height = output_fmt.height;
+
+ /* The size of Cb,Cr are related to the format */
+ if (mbus_code_xysubs(input_isp_fmt->mbus_code, &xsubs_in, &ysubs_in)) {
+ v4l2_err(&dev->v4l2_dev, "Not xsubs/ysubs found\n");
+ return -EINVAL;
+ }
+ in_c.width = in_y.width / xsubs_in;
+ in_c.height = in_y.height / ysubs_in;
+
+ if (output_isp_fmt->fmt_type == FMT_YUV) {
+ fcc_xysubs(output_isp_fmt->fourcc, &xsubs_out, &ysubs_out);
+ out_c.width = out_y.width / xsubs_out;
+ out_c.height = out_y.height / ysubs_out;
+ } else {
+ out_c.width = out_y.width / xsubs_in;
+ out_c.height = out_y.height / ysubs_in;
+ }
+
+ if (in_c.width == out_c.width && in_c.height == out_c.height)
+ goto disable;
+
+ /* set RSZ input and output */
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "stream %d rsz/scale: %dx%d -> %dx%d\n",
+ stream->id, stream->dcrop.width, stream->dcrop.height,
+ output_fmt.width, output_fmt.height);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "chroma scaling %dx%d -> %dx%d\n",
+ in_c.width, in_c.height, out_c.width, out_c.height);
+
+ /* calculate and set scale */
+ config_rsz(stream, &in_y, &in_c, &out_y, &out_c, async);
+
+ if (rkisp1_debug)
+ dump_rsz_regs(stream);
+
+ return 0;
+
+disable:
+ disable_rsz(stream, async);
+
+ return 0;
+}
+
+/***************************** stream operations*******************************/
+
+/*
+ * configure memory interface for mainpath
+ * This should only be called when stream-on
+ */
+static int mp_config_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ /*
+ * NOTE: plane_fmt[0].sizeimage is total size of all planes for single
+ * memory plane formats, so calculate the size explicitly.
+ */
+ mi_set_y_size(stream, stream->out_fmt.plane_fmt[0].bytesperline *
+ stream->out_fmt.height);
+ mi_set_cb_size(stream, stream->out_fmt.plane_fmt[1].sizeimage);
+ mi_set_cr_size(stream, stream->out_fmt.plane_fmt[2].sizeimage);
+
+ mp_frame_end_int_enable(base);
+ if (stream->out_isp_fmt.uv_swap)
+ mp_set_uv_swap(base);
+
+ config_mi_ctrl(stream);
+ mp_mi_ctrl_set_format(base, stream->out_isp_fmt.write_format);
+ mp_mi_ctrl_autoupdate_en(base);
+
+ return 0;
+}
+
+/*
+ * configure memory interface for selfpath
+ * This should only be called when stream-on
+ */
+static int sp_config_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct capture_fmt *output_isp_fmt = &stream->out_isp_fmt;
+ struct ispsd_out_fmt *input_isp_fmt =
+ rkisp1_get_ispsd_out_fmt(&dev->isp_sdev);
+ u32 sp_in_fmt;
+
+ if (mbus_code_sp_in_fmt(input_isp_fmt->mbus_code, &sp_in_fmt)) {
+ v4l2_err(&dev->v4l2_dev, "Can't find the input format\n");
+ return -EINVAL;
+ }
+ /*
+ * NOTE: plane_fmt[0].sizeimage is total size of all planes for single
+ * memory plane formats, so calculate the size explicitly.
+ */
+ mi_set_y_size(stream, stream->out_fmt.plane_fmt[0].bytesperline *
+ stream->out_fmt.height);
+ mi_set_cb_size(stream, stream->out_fmt.plane_fmt[1].sizeimage);
+ mi_set_cr_size(stream, stream->out_fmt.plane_fmt[2].sizeimage);
+
+ sp_set_y_width(base, stream->out_fmt.width);
+ sp_set_y_height(base, stream->out_fmt.height);
+ sp_set_y_line_length(base, stream->u.sp.y_stride);
+
+ sp_frame_end_int_enable(base);
+ if (output_isp_fmt->uv_swap)
+ sp_set_uv_swap(base);
+
+ config_mi_ctrl(stream);
+ sp_mi_ctrl_set_format(base, stream->out_isp_fmt.write_format |
+ sp_in_fmt | output_isp_fmt->output_format);
+
+ sp_mi_ctrl_autoupdate_en(base);
+
+ return 0;
+}
+
+static void mp_enable_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ struct capture_fmt *isp_fmt = &stream->out_isp_fmt;
+
+ mi_ctrl_mp_disable(base);
+ if (isp_fmt->fmt_type == FMT_BAYER)
+ mi_ctrl_mpraw_enable(base);
+ else if (isp_fmt->fmt_type == FMT_YUV)
+ mi_ctrl_mpyuv_enable(base);
+}
+
+static void sp_enable_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ mi_ctrl_spyuv_enable(base);
+}
+
+static void mp_disable_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ mi_ctrl_mp_disable(base);
+}
+
+static void sp_disable_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ mi_ctrl_spyuv_disable(base);
+}
+
+/* Update buffer info to memory interface, it's called in interrupt */
+static void update_mi(struct rkisp1_stream *stream)
+{
+ struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf;
+
+ /* The dummy space allocated by dma_alloc_coherent is used, we can
+ * throw data to it if there is no available buffer.
+ */
+ if (stream->next_buf) {
+ mi_set_y_addr(stream,
+ stream->next_buf->buff_addr[RKISP1_PLANE_Y]);
+ mi_set_cb_addr(stream,
+ stream->next_buf->buff_addr[RKISP1_PLANE_CB]);
+ mi_set_cr_addr(stream,
+ stream->next_buf->buff_addr[RKISP1_PLANE_CR]);
+ } else {
+ v4l2_dbg(1, rkisp1_debug, &stream->ispdev->v4l2_dev,
+ "stream %d: to dummy buf\n", stream->id);
+ mi_set_y_addr(stream, dummy_buf->dma_addr);
+ mi_set_cb_addr(stream, dummy_buf->dma_addr);
+ mi_set_cr_addr(stream, dummy_buf->dma_addr);
+ }
+
+ mi_set_y_offset(stream, 0);
+ mi_set_cb_offset(stream, 0);
+ mi_set_cr_offset(stream, 0);
+}
+
+static void mp_stop_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ if (stream->state != RKISP1_STATE_STREAMING)
+ return;
+ stream->ops->clr_frame_end_int(base);
+ stream->ops->disable_mi(stream);
+}
+
+static void sp_stop_mi(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ if (stream->state != RKISP1_STATE_STREAMING)
+ return;
+ stream->ops->clr_frame_end_int(base);
+ stream->ops->disable_mi(stream);
+}
+
+static struct streams_ops rkisp1_mp_streams_ops = {
+ .config_mi = mp_config_mi,
+ .enable_mi = mp_enable_mi,
+ .disable_mi = mp_disable_mi,
+ .stop_mi = mp_stop_mi,
+ .set_data_path = mp_set_data_path,
+ .clr_frame_end_int = mp_clr_frame_end_int,
+ .is_frame_end_int_masked = mp_is_frame_end_int_masked,
+ .is_stream_stopped = mp_is_stream_stopped,
+};
+
+static struct streams_ops rkisp1_sp_streams_ops = {
+ .config_mi = sp_config_mi,
+ .enable_mi = sp_enable_mi,
+ .disable_mi = sp_disable_mi,
+ .stop_mi = sp_stop_mi,
+ .set_data_path = sp_set_data_path,
+ .clr_frame_end_int = sp_clr_frame_end_int,
+ .is_frame_end_int_masked = sp_is_frame_end_int_masked,
+ .is_stream_stopped = sp_is_stream_stopped,
+};
+
+/*
+ * This function is called when a frame end come. The next frame
+ * is processing and we should set up buffer for next-next frame,
+ * otherwise it will overflow.
+ */
+static int mi_frame_end(struct rkisp1_stream *stream)
+{
+ struct rkisp1_device *isp_dev = stream->ispdev;
+ struct rkisp1_isp_subdev *isp_sd = &isp_dev->isp_sdev;
+ struct capture_fmt *isp_fmt = &stream->out_isp_fmt;
+ unsigned long lock_flags = 0;
+ int i = 0;
+
+ if (stream->curr_buf) {
+ /* Dequeue a filled buffer */
+ for (i = 0; i < isp_fmt->mplanes; i++) {
+ u32 payload_size =
+ stream->out_fmt.plane_fmt[i].sizeimage;
+ vb2_set_plane_payload(
+ &stream->curr_buf->vb.vb2_buf, i,
+ payload_size);
+ }
+ stream->curr_buf->vb.sequence =
+ atomic_read(&isp_sd->frm_sync_seq) - 1;
+ stream->curr_buf->vb.vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&stream->curr_buf->vb.vb2_buf,
+ VB2_BUF_STATE_DONE);
+ }
+
+ /* Next frame is writing to it */
+ stream->curr_buf = stream->next_buf;
+ stream->next_buf = NULL;
+
+ /* Set up an empty buffer for the next-next frame */
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ if (!list_empty(&stream->buf_queue)) {
+ stream->next_buf = list_first_entry(&stream->buf_queue,
+ struct rkisp1_buffer, queue);
+ list_del(&stream->next_buf->queue);
+ }
+
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+
+ update_mi(stream);
+
+ return 0;
+}
+
+/***************************** vb2 operations*******************************/
+
+/*
+ * Set flags and wait, it should stop in interrupt.
+ * If it didn't, stop it by force.
+ */
+static void rkisp1_stream_stop(struct rkisp1_stream *stream)
+{
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int ret = 0;
+
+ stream->stopping = true;
+ ret = wait_event_timeout(stream->done,
+ stream->state != RKISP1_STATE_STREAMING,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ v4l2_warn(v4l2_dev, "waiting on event return error %d\n", ret);
+ stream->ops->stop_mi(stream);
+ stream->stopping = false;
+ stream->state = RKISP1_STATE_READY;
+ }
+ disable_dcrop(stream, true);
+ disable_rsz(stream, true);
+}
+
+/*
+ * Most of registers inside rockchip isp1 have shadow register since
+ * they must be not changed during processing a frame.
+ * Usually, each sub-module updates its shadow register after
+ * processing the last pixel of a frame.
+ */
+static int rkisp1_start(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct rkisp1_stream *other = &dev->stream[stream->id ^ 1];
+ int ret;
+
+ stream->ops->set_data_path(base);
+ ret = stream->ops->config_mi(stream);
+ if (ret)
+ return ret;
+
+ /* Set up an buffer for the next frame */
+ mi_frame_end(stream);
+ stream->ops->enable_mi(stream);
+ /* It's safe to config ACTIVE and SHADOW regs for the
+ * first stream. While when the second is starting, do NOT
+ * force_cfg_update() because it also update the first one.
+ *
+ * The latter case would drop one more buf(that is 2) since
+ * there's not buf in shadow when the second FE received. This's
+ * also required because the sencond FE maybe corrupt especially
+ * when run at 120fps.
+ */
+ if (other->state != RKISP1_STATE_STREAMING) {
+ force_cfg_update(base);
+ mi_frame_end(stream);
+ }
+ stream->state = RKISP1_STATE_STREAMING;
+
+ return 0;
+}
+
+static int rkisp1_queue_setup(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct rkisp1_stream *stream = queue->drv_priv;
+ struct rkisp1_device *dev = stream->ispdev;
+ const struct v4l2_pix_format_mplane *pixm = &stream->out_fmt;
+ const struct capture_fmt *isp_fmt = &stream->out_isp_fmt;
+ u32 i;
+
+ *num_planes = isp_fmt->mplanes;
+
+ for (i = 0; i < isp_fmt->mplanes; i++) {
+ const struct v4l2_plane_pix_format *plane_fmt;
+
+ plane_fmt = &pixm->plane_fmt[i];
+ sizes[i] = plane_fmt->sizeimage;
+ }
+
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev, "%s count %d, size %d\n",
+ v4l2_type_names[queue->type], *num_buffers, sizes[0]);
+
+ return 0;
+}
+
+/*
+ * The vb2_buffer are stored in rkisp1_buffer, in order to unify
+ * mplane buffer and none-mplane buffer.
+ */
+static void rkisp1_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_buffer *ispbuf = to_rkisp1_buffer(vbuf);
+ struct vb2_queue *queue = vb->vb2_queue;
+ struct rkisp1_stream *stream = queue->drv_priv;
+ unsigned long lock_flags = 0;
+ struct capture_fmt *isp_fmt = &stream->out_isp_fmt;
+ int i;
+
+ memset(ispbuf->buff_addr, 0, sizeof(ispbuf->buff_addr));
+ for (i = 0; i < isp_fmt->mplanes; i++)
+ ispbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ /* Convert to non-MPLANE */
+ if (isp_fmt->mplanes == 1) {
+ ispbuf->buff_addr[RKISP1_PLANE_CB] =
+ ispbuf->buff_addr[RKISP1_PLANE_Y] +
+ stream->out_fmt.plane_fmt[RKISP1_PLANE_Y].bytesperline *
+ stream->out_fmt.height;
+ ispbuf->buff_addr[RKISP1_PLANE_CR] =
+ ispbuf->buff_addr[RKISP1_PLANE_CB] +
+ stream->out_fmt.plane_fmt[RKISP1_PLANE_CB].sizeimage;
+ }
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+
+ /* XXX: replace dummy to speed up */
+ if (stream->state == RKISP1_STATE_STREAMING &&
+ stream->next_buf == NULL &&
+ atomic_read(&stream->ispdev->isp_sdev.frm_sync_seq) == 0) {
+ stream->next_buf = ispbuf;
+ update_mi(stream);
+ } else {
+ list_add_tail(&ispbuf->queue, &stream->buf_queue);
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+}
+
+static int rkisp1_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *queue = vb->vb2_queue;
+ struct rkisp1_stream *stream = queue->drv_priv;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct capture_fmt *isp_fmt = &stream->out_isp_fmt;
+ int i;
+
+ for (i = 0; i < isp_fmt->mplanes; i++) {
+ unsigned long size = stream->out_fmt.plane_fmt[i].sizeimage;
+
+ if (isp_fmt->mplanes > 1 && i == 0)
+ size = stream->out_fmt.plane_fmt[RKISP1_PLANE_Y].bytesperline *
+ stream->out_fmt.height;
+
+ if (vb2_plane_size(vb, i) < size) {
+ v4l2_err(&dev->v4l2_dev,
+ "User buffer too small (%ld < %ld)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static int rkisp1_create_dummy_buf(struct rkisp1_stream *stream)
+{
+ struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf;
+ struct rkisp1_device *dev = stream->ispdev;
+
+ /* get a maximum size */
+ dummy_buf->size = max3(stream->out_fmt.plane_fmt[0].bytesperline *
+ stream->out_fmt.height,
+ stream->out_fmt.plane_fmt[1].sizeimage,
+ stream->out_fmt.plane_fmt[2].sizeimage);
+
+ dummy_buf->vaddr = dma_alloc_coherent(dev->dev, dummy_buf->size,
+ &dummy_buf->dma_addr,
+ GFP_KERNEL);
+ if (!dummy_buf->vaddr) {
+ v4l2_err(&dev->v4l2_dev,
+ "Failed to allocate the memory for dummy buffer\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void rkisp1_destroy_dummy_buf(struct rkisp1_stream *stream)
+{
+ struct rkisp1_dummy_buffer *dummy_buf = &stream->dummy_buf;
+ struct rkisp1_device *dev = stream->ispdev;
+
+ dma_free_coherent(dev->dev, dummy_buf->size,
+ dummy_buf->vaddr, dummy_buf->dma_addr);
+}
+
+static void rkisp1_return_all_buffers(struct rkisp1_stream *stream,
+ enum vb2_buffer_state state)
+{
+ unsigned long lock_flags = 0;
+ struct rkisp1_buffer *buf;
+
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ if (stream->curr_buf) {
+ list_add_tail(&stream->curr_buf->queue, &stream->buf_queue);
+ stream->curr_buf = NULL;
+ }
+ if (stream->next_buf) {
+ list_add_tail(&stream->next_buf->queue, &stream->buf_queue);
+ stream->next_buf = NULL;
+ }
+ while (!list_empty(&stream->buf_queue)) {
+ buf = list_first_entry(&stream->buf_queue,
+ struct rkisp1_buffer, queue);
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+}
+
+static void rkisp1_stop_streaming(struct vb2_queue *queue)
+{
+ struct rkisp1_stream *stream = queue->drv_priv;
+ struct rkisp1_vdev_node *node = &stream->vnode;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int ret;
+
+ rkisp1_stream_stop(stream);
+ /* call to the other devices */
+ media_pipeline_stop(&node->vdev.entity);
+ ret = dev->pipe.set_stream(&dev->pipe, false);
+ if (ret < 0)
+ v4l2_err(v4l2_dev, "pipeline stream-off failed error:%d\n",
+ ret);
+
+ /* release buffers */
+ rkisp1_return_all_buffers(stream, VB2_BUF_STATE_ERROR);
+
+ ret = dev->pipe.close(&dev->pipe);
+ if (ret < 0)
+ v4l2_err(v4l2_dev, "pipeline close failed error:%d\n", ret);
+
+ rkisp1_destroy_dummy_buf(stream);
+}
+
+static int rkisp1_stream_start(struct rkisp1_stream *stream)
+{
+ struct v4l2_device *v4l2_dev = &stream->ispdev->v4l2_dev;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct rkisp1_stream *other = &dev->stream[stream->id ^ 1];
+ bool async = false;
+ int ret;
+
+ if (other->state == RKISP1_STATE_STREAMING)
+ async = true;
+
+ ret = rkisp1_config_rsz(stream, async);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "config rsz failed with error %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * can't be async now, otherwise the latter started stream fails to
+ * produce mi interrupt.
+ */
+ ret = rkisp1_config_dcrop(stream, false);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "config dcrop failed with error %d\n", ret);
+ return ret;
+ }
+
+ return rkisp1_start(stream);
+}
+
+static int
+rkisp1_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct rkisp1_stream *stream = queue->drv_priv;
+ struct rkisp1_vdev_node *node = &stream->vnode;
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ int ret;
+
+ if (WARN_ON(stream->state != RKISP1_STATE_READY))
+ goto return_queued_buf;
+
+ ret = rkisp1_create_dummy_buf(stream);
+ if (ret < 0)
+ goto return_queued_buf;
+
+ /* enable clocks/power-domains */
+ ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "open cif pipeline failed %d\n", ret);
+ goto destroy_dummy_buf;
+ }
+
+ /* configure stream hardware to start */
+ ret = rkisp1_stream_start(stream);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "start streaming failed\n");
+ goto close_pipe;
+ }
+
+ /* start sub-devices */
+ ret = dev->pipe.set_stream(&dev->pipe, true);
+ if (ret < 0)
+ goto stop_stream;
+
+ ret = media_pipeline_start(&node->vdev.entity, &dev->pipe.pipe);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "start pipeline failed %d\n", ret);
+ goto pipe_stream_off;
+ }
+
+ return 0;
+
+pipe_stream_off:
+ dev->pipe.set_stream(&dev->pipe, false);
+stop_stream:
+ rkisp1_stream_stop(stream);
+close_pipe:
+ dev->pipe.close(&dev->pipe);
+destroy_dummy_buf:
+ rkisp1_destroy_dummy_buf(stream);
+return_queued_buf:
+ rkisp1_return_all_buffers(stream, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static struct vb2_ops rkisp1_vb2_ops = {
+ .queue_setup = rkisp1_queue_setup,
+ .buf_queue = rkisp1_buf_queue,
+ .buf_prepare = rkisp1_buf_prepare,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .stop_streaming = rkisp1_stop_streaming,
+ .start_streaming = rkisp1_start_streaming,
+};
+
+static int rkisp_init_vb2_queue(struct vb2_queue *q,
+ struct rkisp1_stream *stream,
+ enum v4l2_buf_type buf_type)
+{
+ struct rkisp1_vdev_node *node;
+
+ node = queue_to_node(q);
+
+ q->type = buf_type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->ops = &rkisp1_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->min_buffers_needed = CIF_ISP_REQ_BUFS_MIN;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->vlock;
+ q->dev = stream->ispdev->dev;
+
+ return vb2_queue_init(q);
+}
+
+static void rkisp1_set_fmt(struct rkisp1_stream *stream,
+ struct v4l2_pix_format_mplane *pixm,
+ bool try)
+{
+ const struct capture_fmt *fmt;
+ const struct stream_config *config = stream->config;
+ struct rkisp1_stream *other_stream =
+ &stream->ispdev->stream[!stream->id];
+ unsigned int imagsize = 0;
+ unsigned int planes;
+ u32 xsubs = 1, ysubs = 1;
+ int i;
+
+ fmt = find_fmt(stream, pixm->pixelformat);
+ if (!fmt)
+ fmt = config->fmts;
+
+ /* do checks on resolution */
+ pixm->width = clamp_t(u32, pixm->width, config->min_rsz_width,
+ config->max_rsz_width);
+ pixm->height = clamp_t(u32, pixm->height, config->min_rsz_height,
+ config->max_rsz_height);
+ pixm->num_planes = fmt->mplanes;
+ pixm->field = V4L2_FIELD_NONE;
+ /* get quantization from ispsd */
+ pixm->quantization = stream->ispdev->isp_sdev.quantization;
+
+ /* output full range by default, take effect in isp_params */
+ if (!pixm->quantization)
+ pixm->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ /* can not change quantization when stream-on */
+ if (other_stream->state == RKISP1_STATE_STREAMING)
+ pixm->quantization = other_stream->out_fmt.quantization;
+
+ /* calculate size */
+ fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
+ planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
+ for (i = 0; i < planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt;
+ int width, height, bytesperline;
+
+ plane_fmt = pixm->plane_fmt + i;
+ width = pixm->width / (i ? xsubs : 1);
+ height = pixm->height / (i ? ysubs : 1);
+
+ bytesperline = width * DIV_ROUND_UP(fmt->bpp[i], 8);
+ /* stride is only available for sp stream and y plane */
+ if (stream->id != RKISP1_STREAM_SP || i != 0 ||
+ plane_fmt->bytesperline < bytesperline)
+ plane_fmt->bytesperline = bytesperline;
+
+ plane_fmt->sizeimage = plane_fmt->bytesperline * height;
+
+ imagsize += plane_fmt->sizeimage;
+ }
+
+ /* convert to non-MPLANE format.
+ * it's important since we want to unify none-MPLANE
+ * and MPLANE.
+ */
+ if (fmt->mplanes == 1)
+ pixm->plane_fmt[0].sizeimage = imagsize;
+
+ if (!try) {
+ stream->out_isp_fmt = *fmt;
+ stream->out_fmt = *pixm;
+
+ if (stream->id == RKISP1_STREAM_SP) {
+ stream->u.sp.y_stride =
+ pixm->plane_fmt[0].bytesperline /
+ DIV_ROUND_UP(fmt->bpp[0], 8);
+ } else {
+ stream->u.mp.raw_enable = (fmt->fmt_type == FMT_BAYER);
+ }
+ v4l2_dbg(1, rkisp1_debug, &stream->ispdev->v4l2_dev,
+ "%s: stream: %d req(%d, %d) out(%d, %d)\n", __func__,
+ stream->id, pixm->width, pixm->height,
+ stream->out_fmt.width, stream->out_fmt.height);
+ }
+}
+
+/************************* v4l2_file_operations***************************/
+void rkisp1_stream_init(struct rkisp1_device *dev, u32 id)
+{
+ struct rkisp1_stream *stream = &dev->stream[id];
+ struct v4l2_pix_format_mplane pixm;
+
+ memset(stream, 0, sizeof(*stream));
+ stream->id = id;
+ stream->ispdev = dev;
+
+ INIT_LIST_HEAD(&stream->buf_queue);
+ init_waitqueue_head(&stream->done);
+ spin_lock_init(&stream->vbq_lock);
+ if (stream->id == RKISP1_STREAM_SP) {
+ stream->ops = &rkisp1_sp_streams_ops;
+ stream->config = &rkisp1_sp_stream_config;
+ } else {
+ stream->ops = &rkisp1_mp_streams_ops;
+ stream->config = &rkisp1_mp_stream_config;
+ }
+
+ stream->state = RKISP1_STATE_READY;
+
+ memset(&pixm, 0, sizeof(pixm));
+ pixm.pixelformat = V4L2_PIX_FMT_YUYV;
+ pixm.width = RKISP1_DEFAULT_WIDTH;
+ pixm.height = RKISP1_DEFAULT_HEIGHT;
+ rkisp1_set_fmt(stream, &pixm, false);
+
+ stream->dcrop.left = 0;
+ stream->dcrop.top = 0;
+ stream->dcrop.width = RKISP1_DEFAULT_WIDTH;
+ stream->dcrop.height = RKISP1_DEFAULT_HEIGHT;
+}
+
+static const struct v4l2_file_operations rkisp1_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+/*
+ * mp and sp v4l2_ioctl_ops
+ */
+
+static int rkisp1_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ strlcpy(input->name, "Camera", sizeof(input->name));
+
+ return 0;
+}
+
+static int rkisp1_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+
+ return 0;
+}
+
+static int rkisp1_s_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i > 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int rkisp1_try_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+
+ rkisp1_set_fmt(stream, &f->fmt.pix_mp, true);
+
+ return 0;
+}
+
+static int rkisp1_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+ const struct capture_fmt *fmt = NULL;
+
+ if (f->index >= stream->config->fmt_size)
+ return -EINVAL;
+
+ fmt = &stream->config->fmts[f->index];
+ f->pixelformat = fmt->fourcc;
+
+ return 0;
+}
+
+static int rkisp1_s_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_format *f)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+ struct video_device *vdev = &stream->vnode.vdev;
+ struct rkisp1_vdev_node *node = vdev_to_node(vdev);
+ struct rkisp1_device *dev = stream->ispdev;
+
+ if (vb2_is_busy(&node->buf_queue)) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ rkisp1_set_fmt(stream, &f->fmt.pix_mp, false);
+
+ return 0;
+}
+
+static int rkisp1_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+
+ f->fmt.pix_mp = stream->out_fmt;
+
+ return 0;
+}
+
+static int rkisp1_g_selection(struct file *file, void *prv,
+ struct v4l2_selection *sel)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_rect *dcrop = &stream->dcrop;
+ struct v4l2_rect *input_win;
+
+ input_win = rkisp1_get_isp_sd_win(&dev->isp_sdev);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.width = input_win->width;
+ sel->r.height = input_win->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *dcrop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct v4l2_rect *rkisp1_update_crop(struct rkisp1_stream *stream,
+ struct v4l2_rect *sel,
+ const struct v4l2_rect *in)
+{
+ /* Not crop for MP bayer raw data */
+ if (stream->id == RKISP1_STREAM_MP &&
+ stream->out_isp_fmt.fmt_type == FMT_BAYER) {
+ sel->left = 0;
+ sel->top = 0;
+ sel->width = in->width;
+ sel->height = in->height;
+ return sel;
+ }
+
+ sel->left = ALIGN(sel->left, 2);
+ sel->width = ALIGN(sel->width, 2);
+ sel->left = clamp_t(u32, sel->left, 0,
+ in->width - STREAM_MIN_MP_SP_INPUT_WIDTH);
+ sel->top = clamp_t(u32, sel->top, 0,
+ in->height - STREAM_MIN_MP_SP_INPUT_HEIGHT);
+ sel->width = clamp_t(u32, sel->width, STREAM_MIN_MP_SP_INPUT_WIDTH,
+ in->width - sel->left);
+ sel->height = clamp_t(u32, sel->height, STREAM_MIN_MP_SP_INPUT_HEIGHT,
+ in->height - sel->top);
+ return sel;
+}
+
+static int rkisp1_s_selection(struct file *file, void *prv,
+ struct v4l2_selection *sel)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+ struct video_device *vdev = &stream->vnode.vdev;
+ struct rkisp1_vdev_node *node = vdev_to_node(vdev);
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_rect *dcrop = &stream->dcrop;
+ const struct v4l2_rect *input_win;
+
+ if (vb2_is_busy(&node->buf_queue)) {
+ v4l2_err(&dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ input_win = rkisp1_get_isp_sd_win(&dev->isp_sdev);
+
+ if (sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ if (sel->flags != 0)
+ return -EINVAL;
+
+ if (sel->target == V4L2_SEL_TGT_CROP) {
+ *dcrop = *rkisp1_update_crop(stream, &sel->r, input_win);
+ v4l2_dbg(1, rkisp1_debug, &dev->v4l2_dev,
+ "stream %d crop(%d,%d)/%dx%d\n", stream->id,
+ dcrop->left, dcrop->top, dcrop->width, dcrop->height);
+ }
+
+ return 0;
+}
+
+static int rkisp1_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct rkisp1_stream *stream = video_drvdata(file);
+ struct device *dev = stream->ispdev->dev;
+
+ strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strlcpy(cap->card, dev->driver->name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info),
+ "platform:%s", dev_name(dev));
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops rkisp1_v4l2_ioctl_ops = {
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_input = rkisp1_enum_input,
+ .vidioc_g_input = rkisp1_g_input,
+ .vidioc_s_input = rkisp1_s_input,
+ .vidioc_try_fmt_vid_cap_mplane = rkisp1_try_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap_mplane = rkisp1_enum_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = rkisp1_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = rkisp1_g_fmt_vid_cap_mplane,
+ .vidioc_s_selection = rkisp1_s_selection,
+ .vidioc_g_selection = rkisp1_g_selection,
+ .vidioc_querycap = rkisp1_querycap,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static void rkisp1_unregister_stream_vdev(struct rkisp1_stream *stream)
+{
+ media_entity_cleanup(&stream->vnode.vdev.entity);
+ video_unregister_device(&stream->vnode.vdev);
+}
+
+void rkisp1_unregister_stream_vdevs(struct rkisp1_device *dev)
+{
+ struct rkisp1_stream *mp_stream = &dev->stream[RKISP1_STREAM_MP];
+ struct rkisp1_stream *sp_stream = &dev->stream[RKISP1_STREAM_SP];
+
+ rkisp1_unregister_stream_vdev(mp_stream);
+ rkisp1_unregister_stream_vdev(sp_stream);
+}
+
+static int rkisp1_register_stream_vdev(struct rkisp1_stream *stream)
+{
+ struct rkisp1_device *dev = stream->ispdev;
+ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
+ struct video_device *vdev = &stream->vnode.vdev;
+ struct rkisp1_vdev_node *node;
+ int ret;
+
+ strlcpy(vdev->name,
+ stream->id == RKISP1_STREAM_SP ? SP_VDEV_NAME : MP_VDEV_NAME,
+ sizeof(vdev->name));
+ node = vdev_to_node(vdev);
+ mutex_init(&node->vlock);
+
+ vdev->ioctl_ops = &rkisp1_v4l2_ioctl_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &rkisp1_fops;
+ vdev->minor = -1;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &node->vlock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ video_set_drvdata(vdev, stream);
+ vdev->vfl_dir = VFL_DIR_RX;
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+
+ rkisp_init_vb2_queue(&node->buf_queue, stream,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ vdev->queue = &node->buf_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev,
+ "video_register_device failed with error %d\n", ret);
+ return ret;
+ }
+
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret < 0)
+ goto unreg;
+
+ return 0;
+unreg:
+ video_unregister_device(vdev);
+ return ret;
+}
+
+int rkisp1_register_stream_vdevs(struct rkisp1_device *dev)
+{
+ struct rkisp1_stream *stream;
+ int i, j, ret;
+
+ for (i = 0; i < RKISP1_MAX_STREAM; i++) {
+ stream = &dev->stream[i];
+ stream->ispdev = dev;
+ ret = rkisp1_register_stream_vdev(stream);
+ if (ret < 0)
+ goto err;
+ }
+
+ return 0;
+err:
+ for (j = 0; j < i; j++) {
+ stream = &dev->stream[j];
+ rkisp1_unregister_stream_vdev(stream);
+ }
+
+ return ret;
+}
+
+/**************** Interrupter Handler ****************/
+
+void rkisp1_mi_isr(struct rkisp1_stream *stream)
+{
+ struct rkisp1_device *dev = stream->ispdev;
+ void __iomem *base = stream->ispdev->base_addr;
+ u32 val;
+
+ stream->ops->clr_frame_end_int(base);
+ if (stream->ops->is_frame_end_int_masked(base)) {
+ val = mi_get_masked_int_status(base);
+ v4l2_err(&dev->v4l2_dev, "icr err: 0x%x\n", val);
+ }
+
+ if (stream->stopping) {
+ /* Make sure stream is actually stopped, whose state
+ * can be read from the shadow register, before wake_up()
+ * thread which would immediately free all frame buffers.
+ * stop_mi() takes effect at the next frame end
+ * that sync the configurations to shadow regs.
+ */
+ if (stream->ops->is_stream_stopped(dev->base_addr)) {
+ stream->stopping = false;
+ stream->state = RKISP1_STATE_READY;
+ wake_up(&stream->done);
+ } else {
+ stream->ops->stop_mi(stream);
+ }
+ } else {
+ mi_frame_end(stream);
+ }
+}
diff --git a/drivers/media/platform/rockchip/isp1/capture.h b/drivers/media/platform/rockchip/isp1/capture.h
new file mode 100644
index 000000000000..0e4b25fbc015
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/capture.h
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_PATH_VIDEO_H
+#define _RKISP1_PATH_VIDEO_H
+
+#include "common.h"
+
+struct rkisp1_stream;
+
+/*
+ * @fourcc: pixel format
+ * @mbus_code: pixel format over bus
+ * @fmt_type: helper filed for pixel format
+ * @bpp: bits per pixel
+ * @bayer_pat: bayer patten type
+ * @cplanes: number of colour planes
+ * @mplanes: number of stored memory planes
+ * @uv_swap: if cb cr swaped, for yuv
+ * @write_format: defines how YCbCr self picture data is written to memory
+ * @input_format: defines sp input format
+ * @output_format: defines sp output format
+ */
+struct capture_fmt {
+ u32 fourcc;
+ u32 mbus_code;
+ u8 fmt_type;
+ u8 cplanes;
+ u8 mplanes;
+ u8 uv_swap;
+ u32 write_format;
+ u32 output_format;
+ u8 bpp[VIDEO_MAX_PLANES];
+};
+
+enum rkisp1_sp_inp {
+ RKISP1_SP_INP_ISP,
+ RKISP1_SP_INP_DMA_SP,
+ RKISP1_SP_INP_MAX
+};
+
+struct rkisp1_stream_sp {
+ int y_stride;
+ enum rkisp1_sp_inp input_sel;
+};
+
+struct rkisp1_stream_mp {
+ bool raw_enable;
+};
+
+/* Different config between selfpath and mainpath */
+struct stream_config {
+ const struct capture_fmt *fmts;
+ int fmt_size;
+ /* constrains */
+ const int max_rsz_width;
+ const int max_rsz_height;
+ const int min_rsz_width;
+ const int min_rsz_height;
+ /* registers */
+ struct {
+ u32 ctrl;
+ u32 ctrl_shd;
+ u32 scale_hy;
+ u32 scale_hcr;
+ u32 scale_hcb;
+ u32 scale_vy;
+ u32 scale_vc;
+ u32 scale_lut;
+ u32 scale_lut_addr;
+ u32 scale_hy_shd;
+ u32 scale_hcr_shd;
+ u32 scale_hcb_shd;
+ u32 scale_vy_shd;
+ u32 scale_vc_shd;
+ u32 phase_hy;
+ u32 phase_hc;
+ u32 phase_vy;
+ u32 phase_vc;
+ u32 phase_hy_shd;
+ u32 phase_hc_shd;
+ u32 phase_vy_shd;
+ u32 phase_vc_shd;
+ } rsz;
+ struct {
+ u32 ctrl;
+ u32 yuvmode_mask;
+ u32 rawmode_mask;
+ u32 h_offset;
+ u32 v_offset;
+ u32 h_size;
+ u32 v_size;
+ } dual_crop;
+ struct {
+ u32 y_size_init;
+ u32 cb_size_init;
+ u32 cr_size_init;
+ u32 y_base_ad_init;
+ u32 cb_base_ad_init;
+ u32 cr_base_ad_init;
+ u32 y_offs_cnt_init;
+ u32 cb_offs_cnt_init;
+ u32 cr_offs_cnt_init;
+ } mi;
+};
+
+/* Different reg ops between selfpath and mainpath */
+struct streams_ops {
+ int (*config_mi)(struct rkisp1_stream *stream);
+ void (*stop_mi)(struct rkisp1_stream *stream);
+ void (*enable_mi)(struct rkisp1_stream *stream);
+ void (*disable_mi)(struct rkisp1_stream *stream);
+ void (*set_data_path)(void __iomem *base);
+ void (*clr_frame_end_int)(void __iomem *base);
+ bool (*is_frame_end_int_masked)(void __iomem *base);
+ bool (*is_stream_stopped)(void __iomem *base);
+};
+
+/*
+ * struct rkisp1_stream - ISP capture video device
+ *
+ * @out_isp_fmt: output isp format
+ * @out_fmt: output buffer size
+ * @dcrop: coordinates of dual-crop
+ *
+ * @vbq_lock: lock to protect buf_queue
+ * @buf_queue: queued buffer list
+ * @dummy_buf: dummy space to store dropped data
+ *
+ * rkisp1 use shadowsock registers, so it need two buffer at a time
+ * @curr_buf: the buffer used for current frame
+ * @next_buf: the buffer used for next frame
+ */
+struct rkisp1_stream {
+ u32 id;
+ struct rkisp1_device *ispdev;
+ struct rkisp1_vdev_node vnode;
+ enum rkisp1_state state;
+ enum rkisp1_state saved_state;
+ struct capture_fmt out_isp_fmt;
+ struct v4l2_pix_format_mplane out_fmt;
+ struct v4l2_rect dcrop;
+ struct streams_ops *ops;
+ struct stream_config *config;
+ spinlock_t vbq_lock;
+ struct list_head buf_queue;
+ struct rkisp1_dummy_buffer dummy_buf;
+ struct rkisp1_buffer *curr_buf;
+ struct rkisp1_buffer *next_buf;
+ bool stopping;
+ wait_queue_head_t done;
+ union {
+ struct rkisp1_stream_sp sp;
+ struct rkisp1_stream_mp mp;
+ } u;
+};
+
+void rkisp1_unregister_stream_vdevs(struct rkisp1_device *dev);
+int rkisp1_register_stream_vdevs(struct rkisp1_device *dev);
+void rkisp1_mi_isr(struct rkisp1_stream *stream);
+void rkisp1_stream_init(struct rkisp1_device *dev, u32 id);
+
+#endif /* _RKISP1_PATH_VIDEO_H */
diff --git a/drivers/media/platform/rockchip/isp1/regs.c b/drivers/media/platform/rockchip/isp1/regs.c
new file mode 100644
index 000000000000..4687d7f0c6dc
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/regs.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#include <media/v4l2-common.h>
+#include "regs.h"
+
+void disable_dcrop(struct rkisp1_stream *stream, bool async)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ void __iomem *dc_ctrl_addr = base + stream->config->dual_crop.ctrl;
+ u32 dc_ctrl = readl(dc_ctrl_addr);
+ u32 mask = ~(stream->config->dual_crop.yuvmode_mask |
+ stream->config->dual_crop.rawmode_mask);
+ u32 val = dc_ctrl & mask;
+
+ if (async)
+ val |= CIF_DUAL_CROP_GEN_CFG_UPD;
+ else
+ val |= CIF_DUAL_CROP_CFG_UPD;
+ writel(val, dc_ctrl_addr);
+}
+
+void config_dcrop(struct rkisp1_stream *stream, struct v4l2_rect *rect, bool async)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ void __iomem *dc_ctrl_addr = base + stream->config->dual_crop.ctrl;
+ u32 dc_ctrl = readl(dc_ctrl_addr);
+
+ writel(rect->left, base + stream->config->dual_crop.h_offset);
+ writel(rect->top, base + stream->config->dual_crop.v_offset);
+ writel(rect->width, base + stream->config->dual_crop.h_size);
+ writel(rect->height, base + stream->config->dual_crop.v_size);
+ dc_ctrl |= stream->config->dual_crop.yuvmode_mask;
+ if (async)
+ dc_ctrl |= CIF_DUAL_CROP_GEN_CFG_UPD;
+ else
+ dc_ctrl |= CIF_DUAL_CROP_CFG_UPD;
+ writel(dc_ctrl, dc_ctrl_addr);
+}
+
+void dump_rsz_regs(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ pr_info("RSZ_CTRL 0x%08x/0x%08x\n"
+ "RSZ_SCALE_HY %d/%d\n"
+ "RSZ_SCALE_HCB %d/%d\n"
+ "RSZ_SCALE_HCR %d/%d\n"
+ "RSZ_SCALE_VY %d/%d\n"
+ "RSZ_SCALE_VC %d/%d\n"
+ "RSZ_PHASE_HY %d/%d\n"
+ "RSZ_PHASE_HC %d/%d\n"
+ "RSZ_PHASE_VY %d/%d\n"
+ "RSZ_PHASE_VC %d/%d\n",
+ readl(base + stream->config->rsz.ctrl),
+ readl(base + stream->config->rsz.ctrl_shd),
+ readl(base + stream->config->rsz.scale_hy),
+ readl(base + stream->config->rsz.scale_hy_shd),
+ readl(base + stream->config->rsz.scale_hcb),
+ readl(base + stream->config->rsz.scale_hcb_shd),
+ readl(base + stream->config->rsz.scale_hcr),
+ readl(base + stream->config->rsz.scale_hcr_shd),
+ readl(base + stream->config->rsz.scale_vy),
+ readl(base + stream->config->rsz.scale_vy_shd),
+ readl(base + stream->config->rsz.scale_vc),
+ readl(base + stream->config->rsz.scale_vc_shd),
+ readl(base + stream->config->rsz.phase_hy),
+ readl(base + stream->config->rsz.phase_hy_shd),
+ readl(base + stream->config->rsz.phase_hc),
+ readl(base + stream->config->rsz.phase_hc_shd),
+ readl(base + stream->config->rsz.phase_vy),
+ readl(base + stream->config->rsz.phase_vy_shd),
+ readl(base + stream->config->rsz.phase_vc),
+ readl(base + stream->config->rsz.phase_vc_shd));
+}
+
+static void update_rsz_shadow(struct rkisp1_stream *stream, bool async)
+{
+ void *addr = stream->ispdev->base_addr + stream->config->rsz.ctrl;
+ u32 ctrl_cfg = readl(addr);
+
+ if (async)
+ writel(CIF_RSZ_CTRL_CFG_UPD_AUTO | ctrl_cfg, addr);
+ else
+ writel(CIF_RSZ_CTRL_CFG_UPD | ctrl_cfg, addr);
+}
+
+static void set_scale(struct rkisp1_stream *stream, struct v4l2_rect *in_y,
+ struct v4l2_rect *in_c, struct v4l2_rect *out_y,
+ struct v4l2_rect *out_c)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ void __iomem *scale_hy_addr = base + stream->config->rsz.scale_hy;
+ void __iomem *scale_hcr_addr = base + stream->config->rsz.scale_hcr;
+ void __iomem *scale_hcb_addr = base + stream->config->rsz.scale_hcb;
+ void __iomem *scale_vy_addr = base + stream->config->rsz.scale_vy;
+ void __iomem *scale_vc_addr = base + stream->config->rsz.scale_vc;
+ void __iomem *rsz_ctrl_addr = base + stream->config->rsz.ctrl;
+ u32 scale_hy, scale_hc, scale_vy, scale_vc, rsz_ctrl = 0;
+
+ if (in_y->width < out_y->width) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HY_ENABLE |
+ CIF_RSZ_CTRL_SCALE_HY_UP;
+ scale_hy = ((in_y->width - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (out_y->width - 1);
+ writel(scale_hy, scale_hy_addr);
+ } else if (in_y->width > out_y->width) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HY_ENABLE;
+ scale_hy = ((out_y->width - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (in_y->width - 1) + 1;
+ writel(scale_hy, scale_hy_addr);
+ }
+ if (in_c->width < out_c->width) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HC_ENABLE |
+ CIF_RSZ_CTRL_SCALE_HC_UP;
+ scale_hc = ((in_c->width - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (out_c->width - 1);
+ writel(scale_hc, scale_hcb_addr);
+ writel(scale_hc, scale_hcr_addr);
+ } else if (in_c->width > out_c->width) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_HC_ENABLE;
+ scale_hc = ((out_c->width - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (in_c->width - 1) + 1;
+ writel(scale_hc, scale_hcb_addr);
+ writel(scale_hc, scale_hcr_addr);
+ }
+
+ if (in_y->height < out_y->height) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VY_ENABLE |
+ CIF_RSZ_CTRL_SCALE_VY_UP;
+ scale_vy = ((in_y->height - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (out_y->height - 1);
+ writel(scale_vy, scale_vy_addr);
+ } else if (in_y->height > out_y->height) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VY_ENABLE;
+ scale_vy = ((out_y->height - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (in_y->height - 1) + 1;
+ writel(scale_vy, scale_vy_addr);
+ }
+
+ if (in_c->height < out_c->height) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VC_ENABLE |
+ CIF_RSZ_CTRL_SCALE_VC_UP;
+ scale_vc = ((in_c->height - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (out_c->height - 1);
+ writel(scale_vc, scale_vc_addr);
+ } else if (in_c->height > out_c->height) {
+ rsz_ctrl |= CIF_RSZ_CTRL_SCALE_VC_ENABLE;
+ scale_vc = ((out_c->height - 1) * CIF_RSZ_SCALER_FACTOR) /
+ (in_c->height - 1) + 1;
+ writel(scale_vc, scale_vc_addr);
+ }
+
+ writel(rsz_ctrl, rsz_ctrl_addr);
+}
+
+void config_rsz(struct rkisp1_stream *stream, struct v4l2_rect *in_y,
+ struct v4l2_rect *in_c, struct v4l2_rect *out_y,
+ struct v4l2_rect *out_c, bool async)
+{
+ int i = 0;
+
+ /* No phase offset */
+ writel(0, stream->ispdev->base_addr + stream->config->rsz.phase_hy);
+ writel(0, stream->ispdev->base_addr + stream->config->rsz.phase_hc);
+ writel(0, stream->ispdev->base_addr + stream->config->rsz.phase_vy);
+ writel(0, stream->ispdev->base_addr + stream->config->rsz.phase_vc);
+
+ /* Linear interpolation */
+ for (i = 0; i < 64; i++) {
+ writel(i, stream->ispdev->base_addr + stream->config->rsz.scale_lut_addr);
+ writel(i, stream->ispdev->base_addr + stream->config->rsz.scale_lut);
+ }
+
+ set_scale(stream, in_y, in_c, out_y, out_c);
+
+ update_rsz_shadow(stream, async);
+}
+
+void disable_rsz(struct rkisp1_stream *stream, bool async)
+{
+ writel(0, stream->ispdev->base_addr + stream->config->rsz.ctrl);
+
+ if (!async)
+ update_rsz_shadow(stream, async);
+}
+
+void config_mi_ctrl(struct rkisp1_stream *stream)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+ void __iomem *addr = base + CIF_MI_CTRL;
+ u32 reg;
+
+ reg = readl(addr) & ~GENMASK(17, 16);
+ writel(reg | CIF_MI_CTRL_BURST_LEN_LUM_64, addr);
+ reg = readl(addr) & ~GENMASK(19, 18);
+ writel(reg | CIF_MI_CTRL_BURST_LEN_CHROM_64, addr);
+ reg = readl(addr);
+ writel(reg | CIF_MI_CTRL_INIT_BASE_EN, addr);
+ reg = readl(addr);
+ writel(reg | CIF_MI_CTRL_INIT_OFFSET_EN, addr);
+}
+
+void mp_clr_frame_end_int(void __iomem *base)
+{
+ writel(CIF_MI_MP_FRAME, base + CIF_MI_ICR);
+}
+
+void sp_clr_frame_end_int(void __iomem *base)
+{
+ writel(CIF_MI_SP_FRAME, base + CIF_MI_ICR);
+}
+
+bool mp_is_frame_end_int_masked(void __iomem *base)
+{
+ return (mi_get_masked_int_status(base) & CIF_MI_MP_FRAME);
+}
+
+bool sp_is_frame_end_int_masked(void __iomem *base)
+{
+ return (mi_get_masked_int_status(base) & CIF_MI_SP_FRAME);
+}
+
+bool mp_is_stream_stopped(void __iomem *base)
+{
+ int en;
+
+ en = CIF_MI_CTRL_SHD_MP_IN_ENABLED | CIF_MI_CTRL_SHD_RAW_OUT_ENABLED;
+ return !(readl(base + CIF_MI_CTRL_SHD) & en);
+}
+
+bool sp_is_stream_stopped(void __iomem *base)
+{
+ return !(readl(base + CIF_MI_CTRL_SHD) & CIF_MI_CTRL_SHD_SP_IN_ENABLED);
+}
diff --git a/drivers/media/platform/rockchip/isp1/regs.h b/drivers/media/platform/rockchip/isp1/regs.h
new file mode 100644
index 000000000000..97717b441877
--- /dev/null
+++ b/drivers/media/platform/rockchip/isp1/regs.h
@@ -0,0 +1,1550 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Rockchip isp1 driver
+ *
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef _RKISP1_REGS_H
+#define _RKISP1_REGS_H
+#include "dev.h"
+
+/* ISP_CTRL */
+#define CIF_ISP_CTRL_ISP_ENABLE BIT(0)
+#define CIF_ISP_CTRL_ISP_MODE_RAW_PICT (0 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_ITU656 (1 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_ITU601 (2 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_BAYER_ITU601 (3 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_DATA_MODE (4 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_BAYER_ITU656 (5 << 1)
+#define CIF_ISP_CTRL_ISP_MODE_RAW_PICT_ITU656 (6 << 1)
+#define CIF_ISP_CTRL_ISP_INFORM_ENABLE BIT(4)
+#define CIF_ISP_CTRL_ISP_GAMMA_IN_ENA BIT(6)
+#define CIF_ISP_CTRL_ISP_AWB_ENA BIT(7)
+#define CIF_ISP_CTRL_ISP_CFG_UPD_PERMANENT BIT(8)
+#define CIF_ISP_CTRL_ISP_CFG_UPD BIT(9)
+#define CIF_ISP_CTRL_ISP_GEN_CFG_UPD BIT(10)
+#define CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA BIT(11)
+#define CIF_ISP_CTRL_ISP_FLASH_MODE_ENA BIT(12)
+#define CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA BIT(13)
+#define CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA BIT(14)
+
+/* ISP_ACQ_PROP */
+#define CIF_ISP_ACQ_PROP_POS_EDGE BIT(0)
+#define CIF_ISP_ACQ_PROP_HSYNC_LOW BIT(1)
+#define CIF_ISP_ACQ_PROP_VSYNC_LOW BIT(2)
+#define CIF_ISP_ACQ_PROP_BAYER_PAT_RGGB (0 << 3)
+#define CIF_ISP_ACQ_PROP_BAYER_PAT_GRBG (1 << 3)
+#define CIF_ISP_ACQ_PROP_BAYER_PAT_GBRG (2 << 3)
+#define CIF_ISP_ACQ_PROP_BAYER_PAT_BGGR (3 << 3)
+#define CIF_ISP_ACQ_PROP_BAYER_PAT(pat) ((pat) << 3)
+#define CIF_ISP_ACQ_PROP_YCBYCR (0 << 7)
+#define CIF_ISP_ACQ_PROP_YCRYCB (1 << 7)
+#define CIF_ISP_ACQ_PROP_CBYCRY (2 << 7)
+#define CIF_ISP_ACQ_PROP_CRYCBY (3 << 7)
+#define CIF_ISP_ACQ_PROP_FIELD_SEL_ALL (0 << 9)
+#define CIF_ISP_ACQ_PROP_FIELD_SEL_EVEN (1 << 9)
+#define CIF_ISP_ACQ_PROP_FIELD_SEL_ODD (2 << 9)
+#define CIF_ISP_ACQ_PROP_IN_SEL_12B (0 << 12)
+#define CIF_ISP_ACQ_PROP_IN_SEL_10B_ZERO (1 << 12)
+#define CIF_ISP_ACQ_PROP_IN_SEL_10B_MSB (2 << 12)
+#define CIF_ISP_ACQ_PROP_IN_SEL_8B_ZERO (3 << 12)
+#define CIF_ISP_ACQ_PROP_IN_SEL_8B_MSB (4 << 12)
+
+/* VI_DPCL */
+#define CIF_VI_DPCL_DMA_JPEG (0 << 0)
+#define CIF_VI_DPCL_MP_MUX_MRSZ_MI (1 << 0)
+#define CIF_VI_DPCL_MP_MUX_MRSZ_JPEG (2 << 0)
+#define CIF_VI_DPCL_CHAN_MODE_MP (1 << 2)
+#define CIF_VI_DPCL_CHAN_MODE_SP (2 << 2)
+#define CIF_VI_DPCL_CHAN_MODE_MPSP (3 << 2)
+#define CIF_VI_DPCL_DMA_SW_SPMUX (0 << 4)
+#define CIF_VI_DPCL_DMA_SW_SI (1 << 4)
+#define CIF_VI_DPCL_DMA_SW_IE (2 << 4)
+#define CIF_VI_DPCL_DMA_SW_JPEG (3 << 4)
+#define CIF_VI_DPCL_DMA_SW_ISP (4 << 4)
+#define CIF_VI_DPCL_IF_SEL_PARALLEL (0 << 8)
+#define CIF_VI_DPCL_IF_SEL_SMIA (1 << 8)
+#define CIF_VI_DPCL_IF_SEL_MIPI (2 << 8)
+#define CIF_VI_DPCL_DMA_IE_MUX_DMA BIT(10)
+#define CIF_VI_DPCL_DMA_SP_MUX_DMA BIT(11)
+
+/* ISP_IMSC - ISP_MIS - ISP_RIS - ISP_ICR - ISP_ISR */
+#define CIF_ISP_OFF BIT(0)
+#define CIF_ISP_FRAME BIT(1)
+#define CIF_ISP_DATA_LOSS BIT(2)
+#define CIF_ISP_PIC_SIZE_ERROR BIT(3)
+#define CIF_ISP_AWB_DONE BIT(4)
+#define CIF_ISP_FRAME_IN BIT(5)
+#define CIF_ISP_V_START BIT(6)
+#define CIF_ISP_H_START BIT(7)
+#define CIF_ISP_FLASH_ON BIT(8)
+#define CIF_ISP_FLASH_OFF BIT(9)
+#define CIF_ISP_SHUTTER_ON BIT(10)
+#define CIF_ISP_SHUTTER_OFF BIT(11)
+#define CIF_ISP_AFM_SUM_OF BIT(12)
+#define CIF_ISP_AFM_LUM_OF BIT(13)
+#define CIF_ISP_AFM_FIN BIT(14)
+#define CIF_ISP_HIST_MEASURE_RDY BIT(15)
+#define CIF_ISP_FLASH_CAP BIT(17)
+#define CIF_ISP_EXP_END BIT(18)
+#define CIF_ISP_VSM_END BIT(19)
+
+/* ISP_ERR */
+#define CIF_ISP_ERR_INFORM_SIZE BIT(0)
+#define CIF_ISP_ERR_IS_SIZE BIT(1)
+#define CIF_ISP_ERR_OUTFORM_SIZE BIT(2)
+
+/* MI_CTRL */
+#define CIF_MI_CTRL_MP_ENABLE (1 << 0)
+#define CIF_MI_CTRL_SP_ENABLE (2 << 0)
+#define CIF_MI_CTRL_JPEG_ENABLE (4 << 0)
+#define CIF_MI_CTRL_RAW_ENABLE (8 << 0)
+#define CIF_MI_CTRL_HFLIP BIT(4)
+#define CIF_MI_CTRL_VFLIP BIT(5)
+#define CIF_MI_CTRL_ROT BIT(6)
+#define CIF_MI_BYTE_SWAP BIT(7)
+#define CIF_MI_SP_Y_FULL_YUV2RGB BIT(8)
+#define CIF_MI_SP_CBCR_FULL_YUV2RGB BIT(9)
+#define CIF_MI_SP_422NONCOSITEED BIT(10)
+#define CIF_MI_MP_PINGPONG_ENABEL BIT(11)
+#define CIF_MI_SP_PINGPONG_ENABEL BIT(12)
+#define CIF_MI_MP_AUTOUPDATE_ENABLE BIT(13)
+#define CIF_MI_SP_AUTOUPDATE_ENABLE BIT(14)
+#define CIF_MI_LAST_PIXEL_SIG_ENABLE BIT(15)
+#define CIF_MI_CTRL_BURST_LEN_LUM_16 (0 << 16)
+#define CIF_MI_CTRL_BURST_LEN_LUM_32 (1 << 16)
+#define CIF_MI_CTRL_BURST_LEN_LUM_64 (2 << 16)
+#define CIF_MI_CTRL_BURST_LEN_CHROM_16 (0 << 18)
+#define CIF_MI_CTRL_BURST_LEN_CHROM_32 (1 << 18)
+#define CIF_MI_CTRL_BURST_LEN_CHROM_64 (2 << 18)
+#define CIF_MI_CTRL_INIT_BASE_EN BIT(20)
+#define CIF_MI_CTRL_INIT_OFFSET_EN BIT(21)
+#define MI_CTRL_MP_WRITE_YUV_PLA_OR_RAW8 (0 << 22)
+#define MI_CTRL_MP_WRITE_YUV_SPLA (1 << 22)
+#define MI_CTRL_MP_WRITE_YUVINT (2 << 22)
+#define MI_CTRL_MP_WRITE_RAW12 (2 << 22)
+#define MI_CTRL_SP_WRITE_PLA (0 << 24)
+#define MI_CTRL_SP_WRITE_SPLA (1 << 24)
+#define MI_CTRL_SP_WRITE_INT (2 << 24)
+#define MI_CTRL_SP_INPUT_YUV400 (0 << 26)
+#define MI_CTRL_SP_INPUT_YUV420 (1 << 26)
+#define MI_CTRL_SP_INPUT_YUV422 (2 << 26)
+#define MI_CTRL_SP_INPUT_YUV444 (3 << 26)
+#define MI_CTRL_SP_OUTPUT_YUV400 (0 << 28)
+#define MI_CTRL_SP_OUTPUT_YUV420 (1 << 28)
+#define MI_CTRL_SP_OUTPUT_YUV422 (2 << 28)
+#define MI_CTRL_SP_OUTPUT_YUV444 (3 << 28)
+#define MI_CTRL_SP_OUTPUT_RGB565 (4 << 28)
+#define MI_CTRL_SP_OUTPUT_RGB666 (5 << 28)
+#define MI_CTRL_SP_OUTPUT_RGB888 (6 << 28)
+
+#define MI_CTRL_MP_FMT_MASK GENMASK(23, 22)
+#define MI_CTRL_SP_FMT_MASK GENMASK(30, 24)
+
+/* MI_INIT */
+#define CIF_MI_INIT_SKIP BIT(2)
+#define CIF_MI_INIT_SOFT_UPD BIT(4)
+
+/* MI_CTRL_SHD */
+#define CIF_MI_CTRL_SHD_MP_IN_ENABLED BIT(0)
+#define CIF_MI_CTRL_SHD_SP_IN_ENABLED BIT(1)
+#define CIF_MI_CTRL_SHD_JPEG_IN_ENABLED BIT(2)
+#define CIF_MI_CTRL_SHD_RAW_IN_ENABLED BIT(3)
+#define CIF_MI_CTRL_SHD_MP_OUT_ENABLED BIT(16)
+#define CIF_MI_CTRL_SHD_SP_OUT_ENABLED BIT(17)
+#define CIF_MI_CTRL_SHD_JPEG_OUT_ENABLED BIT(18)
+#define CIF_MI_CTRL_SHD_RAW_OUT_ENABLED BIT(19)
+
+/* RSZ_CTRL */
+#define CIF_RSZ_CTRL_SCALE_HY_ENABLE BIT(0)
+#define CIF_RSZ_CTRL_SCALE_HC_ENABLE BIT(1)
+#define CIF_RSZ_CTRL_SCALE_VY_ENABLE BIT(2)
+#define CIF_RSZ_CTRL_SCALE_VC_ENABLE BIT(3)
+#define CIF_RSZ_CTRL_SCALE_HY_UP BIT(4)
+#define CIF_RSZ_CTRL_SCALE_HC_UP BIT(5)
+#define CIF_RSZ_CTRL_SCALE_VY_UP BIT(6)
+#define CIF_RSZ_CTRL_SCALE_VC_UP BIT(7)
+#define CIF_RSZ_CTRL_CFG_UPD BIT(8)
+#define CIF_RSZ_CTRL_CFG_UPD_AUTO BIT(9)
+#define CIF_RSZ_SCALER_FACTOR BIT(16)
+
+/* MI_IMSC - MI_MIS - MI_RIS - MI_ICR - MI_ISR */
+#define CIF_MI_MP_FRAME BIT(0)
+#define CIF_MI_SP_FRAME BIT(1)
+#define CIF_MI_MBLK_LINE BIT(2)
+#define CIF_MI_FILL_MP_Y BIT(3)
+#define CIF_MI_WRAP_MP_Y BIT(4)
+#define CIF_MI_WRAP_MP_CB BIT(5)
+#define CIF_MI_WRAP_MP_CR BIT(6)
+#define CIF_MI_WRAP_SP_Y BIT(7)
+#define CIF_MI_WRAP_SP_CB BIT(8)
+#define CIF_MI_WRAP_SP_CR BIT(9)
+#define CIF_MI_DMA_READY BIT(11)
+
+/* MI_STATUS */
+#define CIF_MI_STATUS_MP_Y_FIFO_FULL BIT(0)
+#define CIF_MI_STATUS_SP_Y_FIFO_FULL BIT(4)
+
+/* MI_DMA_CTRL */
+#define CIF_MI_DMA_CTRL_BURST_LEN_LUM_16 (0 << 0)
+#define CIF_MI_DMA_CTRL_BURST_LEN_LUM_32 (1 << 0)
+#define CIF_MI_DMA_CTRL_BURST_LEN_LUM_64 (2 << 0)
+#define CIF_MI_DMA_CTRL_BURST_LEN_CHROM_16 (0 << 2)
+#define CIF_MI_DMA_CTRL_BURST_LEN_CHROM_32 (1 << 2)
+#define CIF_MI_DMA_CTRL_BURST_LEN_CHROM_64 (2 << 2)
+#define CIF_MI_DMA_CTRL_READ_FMT_PLANAR (0 << 4)
+#define CIF_MI_DMA_CTRL_READ_FMT_SPLANAR (1 << 4)
+#define CIF_MI_DMA_CTRL_FMT_YUV400 (0 << 6)
+#define CIF_MI_DMA_CTRL_FMT_YUV420 (1 << 6)
+#define CIF_MI_DMA_CTRL_READ_FMT_PACKED (2 << 4)
+#define CIF_MI_DMA_CTRL_FMT_YUV422 (2 << 6)
+#define CIF_MI_DMA_CTRL_FMT_YUV444 (3 << 6)
+#define CIF_MI_DMA_CTRL_BYTE_SWAP BIT(8)
+#define CIF_MI_DMA_CTRL_CONTINUOUS_ENA BIT(9)
+#define CIF_MI_DMA_CTRL_RGB_BAYER_NO (0 << 12)
+#define CIF_MI_DMA_CTRL_RGB_BAYER_8BIT (1 << 12)
+#define CIF_MI_DMA_CTRL_RGB_BAYER_16BIT (2 << 12)
+/* MI_DMA_START */
+#define CIF_MI_DMA_START_ENABLE BIT(0)
+/* MI_XTD_FORMAT_CTRL */
+#define CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP BIT(0)
+#define CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP BIT(1)
+#define CIF_MI_XTD_FMT_CTRL_DMA_CB_CR_SWAP BIT(2)
+
+/* CCL */
+#define CIF_CCL_CIF_CLK_DIS BIT(2)
+/* ICCL */
+#define CIF_ICCL_ISP_CLK BIT(0)
+#define CIF_ICCL_CP_CLK BIT(1)
+#define CIF_ICCL_RES_2 BIT(2)
+#define CIF_ICCL_MRSZ_CLK BIT(3)
+#define CIF_ICCL_SRSZ_CLK BIT(4)
+#define CIF_ICCL_JPEG_CLK BIT(5)
+#define CIF_ICCL_MI_CLK BIT(6)
+#define CIF_ICCL_RES_7 BIT(7)
+#define CIF_ICCL_IE_CLK BIT(8)
+#define CIF_ICCL_SIMP_CLK BIT(9)
+#define CIF_ICCL_SMIA_CLK BIT(10)
+#define CIF_ICCL_MIPI_CLK BIT(11)
+#define CIF_ICCL_DCROP_CLK BIT(12)
+/* IRCL */
+#define CIF_IRCL_ISP_SW_RST BIT(0)
+#define CIF_IRCL_CP_SW_RST BIT(1)
+#define CIF_IRCL_YCS_SW_RST BIT(2)
+#define CIF_IRCL_MRSZ_SW_RST BIT(3)
+#define CIF_IRCL_SRSZ_SW_RST BIT(4)
+#define CIF_IRCL_JPEG_SW_RST BIT(5)
+#define CIF_IRCL_MI_SW_RST BIT(6)
+#define CIF_IRCL_CIF_SW_RST BIT(7)
+#define CIF_IRCL_IE_SW_RST BIT(8)
+#define CIF_IRCL_SI_SW_RST BIT(9)
+#define CIF_IRCL_MIPI_SW_RST BIT(11)
+
+/* C_PROC_CTR */
+#define CIF_C_PROC_CTR_ENABLE BIT(0)
+#define CIF_C_PROC_YOUT_FULL BIT(1)
+#define CIF_C_PROC_YIN_FULL BIT(2)
+#define CIF_C_PROC_COUT_FULL BIT(3)
+#define CIF_C_PROC_CTRL_RESERVED 0xFFFFFFFE
+#define CIF_C_PROC_CONTRAST_RESERVED 0xFFFFFF00
+#define CIF_C_PROC_BRIGHTNESS_RESERVED 0xFFFFFF00
+#define CIF_C_PROC_HUE_RESERVED 0xFFFFFF00
+#define CIF_C_PROC_SATURATION_RESERVED 0xFFFFFF00
+#define CIF_C_PROC_MACC_RESERVED 0xE000E000
+#define CIF_C_PROC_TONE_RESERVED 0xF000
+/* DUAL_CROP_CTRL */
+#define CIF_DUAL_CROP_MP_MODE_BYPASS (0 << 0)
+#define CIF_DUAL_CROP_MP_MODE_YUV (1 << 0)
+#define CIF_DUAL_CROP_MP_MODE_RAW (2 << 0)
+#define CIF_DUAL_CROP_SP_MODE_BYPASS (0 << 2)
+#define CIF_DUAL_CROP_SP_MODE_YUV (1 << 2)
+#define CIF_DUAL_CROP_SP_MODE_RAW (2 << 2)
+#define CIF_DUAL_CROP_CFG_UPD_PERMANENT BIT(4)
+#define CIF_DUAL_CROP_CFG_UPD BIT(5)
+#define CIF_DUAL_CROP_GEN_CFG_UPD BIT(6)
+
+/* IMG_EFF_CTRL */
+#define CIF_IMG_EFF_CTRL_ENABLE BIT(0)
+#define CIF_IMG_EFF_CTRL_MODE_BLACKWHITE (0 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_NEGATIVE (1 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_SEPIA (2 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_COLOR_SEL (3 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_EMBOSS (4 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_SKETCH (5 << 1)
+#define CIF_IMG_EFF_CTRL_MODE_SHARPEN (6 << 1)
+#define CIF_IMG_EFF_CTRL_CFG_UPD BIT(4)
+#define CIF_IMG_EFF_CTRL_YCBCR_FULL BIT(5)
+
+#define CIF_IMG_EFF_CTRL_MODE_BLACKWHITE_SHIFT 0
+#define CIF_IMG_EFF_CTRL_MODE_NEGATIVE_SHIFT 1
+#define CIF_IMG_EFF_CTRL_MODE_SEPIA_SHIFT 2
+#define CIF_IMG_EFF_CTRL_MODE_COLOR_SEL_SHIFT 3
+#define CIF_IMG_EFF_CTRL_MODE_EMBOSS_SHIFT 4
+#define CIF_IMG_EFF_CTRL_MODE_SKETCH_SHIFT 5
+#define CIF_IMG_EFF_CTRL_MODE_SHARPEN_SHIFT 6
+#define CIF_IMG_EFF_CTRL_MODE_MASK 0xE
+
+/* IMG_EFF_COLOR_SEL */
+#define CIF_IMG_EFF_COLOR_RGB 0
+#define CIF_IMG_EFF_COLOR_B (1 << 0)
+#define CIF_IMG_EFF_COLOR_G (2 << 0)
+#define CIF_IMG_EFF_COLOR_GB (3 << 0)
+#define CIF_IMG_EFF_COLOR_R (4 << 0)
+#define CIF_IMG_EFF_COLOR_RB (5 << 0)
+#define CIF_IMG_EFF_COLOR_RG (6 << 0)
+#define CIF_IMG_EFF_COLOR_RGB2 (7 << 0)
+
+/* MIPI_CTRL */
+#define CIF_MIPI_CTRL_OUTPUT_ENA BIT(0)
+#define CIF_MIPI_CTRL_SHUTDOWNLANES(a) (((a) & 0xF) << 8)
+#define CIF_MIPI_CTRL_NUM_LANES(a) (((a) & 0x3) << 12)
+#define CIF_MIPI_CTRL_ERR_SOT_HS_SKIP BIT(16)
+#define CIF_MIPI_CTRL_ERR_SOT_SYNC_HS_SKIP BIT(17)
+#define CIF_MIPI_CTRL_CLOCKLANE_ENA BIT(18)
+
+/* MIPI_DATA_SEL */
+#define CIF_MIPI_DATA_SEL_VC(a) (((a) & 0x3) << 6)
+#define CIF_MIPI_DATA_SEL_DT(a) (((a) & 0x3F) << 0)
+/* MIPI DATA_TYPE */
+#define CIF_CSI2_DT_YUV420_8b 0x18
+#define CIF_CSI2_DT_YUV420_10b 0x19
+#define CIF_CSI2_DT_YUV422_8b 0x1E
+#define CIF_CSI2_DT_YUV422_10b 0x1F
+#define CIF_CSI2_DT_RGB565 0x22
+#define CIF_CSI2_DT_RGB666 0x23
+#define CIF_CSI2_DT_RGB888 0x24
+#define CIF_CSI2_DT_RAW8 0x2A
+#define CIF_CSI2_DT_RAW10 0x2B
+#define CIF_CSI2_DT_RAW12 0x2C
+
+/* MIPI_IMSC, MIPI_RIS, MIPI_MIS, MIPI_ICR, MIPI_ISR */
+#define CIF_MIPI_SYNC_FIFO_OVFLW(a) (((a) & 0xF) << 0)
+#define CIF_MIPI_ERR_SOT(a) (((a) & 0xF) << 4)
+#define CIF_MIPI_ERR_SOT_SYNC(a) (((a) & 0xF) << 8)
+#define CIF_MIPI_ERR_EOT_SYNC(a) (((a) & 0xF) << 12)
+#define CIF_MIPI_ERR_CTRL(a) (((a) & 0xF) << 16)
+#define CIF_MIPI_ERR_PROTOCOL BIT(20)
+#define CIF_MIPI_ERR_ECC1 BIT(21)
+#define CIF_MIPI_ERR_ECC2 BIT(22)
+#define CIF_MIPI_ERR_CS BIT(23)
+#define CIF_MIPI_FRAME_END BIT(24)
+#define CIF_MIPI_ADD_DATA_OVFLW BIT(25)
+#define CIF_MIPI_ADD_DATA_WATER_MARK BIT(26)
+
+#define CIF_MIPI_ERR_CSI (CIF_MIPI_ERR_PROTOCOL | \
+ CIF_MIPI_ERR_ECC1 | \
+ CIF_MIPI_ERR_ECC2 | \
+ CIF_MIPI_ERR_CS)
+
+#define CIF_MIPI_ERR_DPHY (CIF_MIPI_ERR_SOT(3) | \
+ CIF_MIPI_ERR_SOT_SYNC(3) | \
+ CIF_MIPI_ERR_EOT_SYNC(3) | \
+ CIF_MIPI_ERR_CTRL(3))
+
+/* SUPER_IMPOSE */
+#define CIF_SUPER_IMP_CTRL_NORMAL_MODE BIT(0)
+#define CIF_SUPER_IMP_CTRL_REF_IMG_MEM BIT(1)
+#define CIF_SUPER_IMP_CTRL_TRANSP_DIS BIT(2)
+
+/* ISP HISTOGRAM CALCULATION : ISP_HIST_PROP */
+#define CIF_ISP_HIST_PROP_MODE_DIS (0 << 0)
+#define CIF_ISP_HIST_PROP_MODE_RGB (1 << 0)
+#define CIF_ISP_HIST_PROP_MODE_RED (2 << 0)
+#define CIF_ISP_HIST_PROP_MODE_GREEN (3 << 0)
+#define CIF_ISP_HIST_PROP_MODE_BLUE (4 << 0)
+#define CIF_ISP_HIST_PROP_MODE_LUM (5 << 0)
+#define CIF_ISP_HIST_PROP_MODE_MASK 0x7
+#define CIF_ISP_HIST_PREDIV_SET(x) (((x) & 0x7F) << 3)
+#define CIF_ISP_HIST_WEIGHT_SET(v0, v1, v2, v3) \
+ (((v0) & 0x1F) | (((v1) & 0x1F) << 8) |\
+ (((v2) & 0x1F) << 16) | \
+ (((v3) & 0x1F) << 24))
+
+#define CIF_ISP_HIST_WINDOW_OFFSET_RESERVED 0xFFFFF000
+#define CIF_ISP_HIST_WINDOW_SIZE_RESERVED 0xFFFFF800
+#define CIF_ISP_HIST_WEIGHT_RESERVED 0xE0E0E0E0
+#define CIF_ISP_MAX_HIST_PREDIVIDER 0x0000007F
+#define CIF_ISP_HIST_ROW_NUM 5
+#define CIF_ISP_HIST_COLUMN_NUM 5
+
+/* AUTO FOCUS MEASUREMENT: ISP_AFM_CTRL */
+#define ISP_AFM_CTRL_ENABLE BIT(0)
+
+/* SHUTTER CONTROL */
+#define CIF_ISP_SH_CTRL_SH_ENA BIT(0)
+#define CIF_ISP_SH_CTRL_REP_EN BIT(1)
+#define CIF_ISP_SH_CTRL_SRC_SH_TRIG BIT(2)
+#define CIF_ISP_SH_CTRL_EDGE_POS BIT(3)
+#define CIF_ISP_SH_CTRL_POL_LOW BIT(4)
+
+/* FLASH MODULE */
+/* ISP_FLASH_CMD */
+#define CIF_FLASH_CMD_PRELIGHT_ON BIT(0)
+#define CIF_FLASH_CMD_FLASH_ON BIT(1)
+#define CIF_FLASH_CMD_PRE_FLASH_ON BIT(2)
+/* ISP_FLASH_CONFIG */
+#define CIF_FLASH_CONFIG_PRELIGHT_END BIT(0)
+#define CIF_FLASH_CONFIG_VSYNC_POS BIT(1)
+#define CIF_FLASH_CONFIG_PRELIGHT_LOW BIT(2)
+#define CIF_FLASH_CONFIG_SRC_FL_TRIG BIT(3)
+#define CIF_FLASH_CONFIG_DELAY(a) (((a) & 0xF) << 4)
+
+/* Demosaic: ISP_DEMOSAIC */
+#define CIF_ISP_DEMOSAIC_BYPASS BIT(10)
+#define CIF_ISP_DEMOSAIC_TH(x) ((x) & 0xFF)
+
+/* AWB */
+/* ISP_AWB_PROP */
+#define CIF_ISP_AWB_YMAX_CMP_EN BIT(2)
+#define CIFISP_AWB_YMAX_READ(x) (((x) >> 2) & 1)
+#define CIF_ISP_AWB_MODE_RGB_EN ((1 << 31) | (0x2 << 0))
+#define CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0))
+#define CIF_ISP_AWB_MODE_YCBCR_EN ((0 << 31) | (0x2 << 0))
+#define CIF_ISP_AWB_MODE_MASK_NONE 0xFFFFFFFC
+#define CIF_ISP_AWB_MODE_READ(x) ((x) & 3)
+/* ISP_AWB_GAIN_RB, ISP_AWB_GAIN_G */
+#define CIF_ISP_AWB_GAIN_R_SET(x) (((x) & 0x3FF) << 16)
+#define CIF_ISP_AWB_GAIN_R_READ(x) (((x) >> 16) & 0x3FF)
+#define CIF_ISP_AWB_GAIN_B_SET(x) ((x) & 0x3FFF)
+#define CIF_ISP_AWB_GAIN_B_READ(x) ((x) & 0x3FFF)
+/* ISP_AWB_REF */
+#define CIF_ISP_AWB_REF_CR_SET(x) (((x) & 0xFF) << 8)
+#define CIF_ISP_AWB_REF_CR_READ(x) (((x) >> 8) & 0xFF)
+#define CIF_ISP_AWB_REF_CB_READ(x) ((x) & 0xFF)
+/* ISP_AWB_THRESH */
+#define CIF_ISP_AWB_MAX_CS_SET(x) (((x) & 0xFF) << 8)
+#define CIF_ISP_AWB_MAX_CS_READ(x) (((x) >> 8) & 0xFF)
+#define CIF_ISP_AWB_MIN_C_READ(x) ((x) & 0xFF)
+#define CIF_ISP_AWB_MIN_Y_SET(x) (((x) & 0xFF) << 16)
+#define CIF_ISP_AWB_MIN_Y_READ(x) (((x) >> 16) & 0xFF)
+#define CIF_ISP_AWB_MAX_Y_SET(x) (((x) & 0xFF) << 24)
+#define CIF_ISP_AWB_MAX_Y_READ(x) (((x) >> 24) & 0xFF)
+/* ISP_AWB_MEAN */
+#define CIF_ISP_AWB_GET_MEAN_CR_R(x) ((x) & 0xFF)
+#define CIF_ISP_AWB_GET_MEAN_CB_B(x) (((x) >> 8) & 0xFF)
+#define CIF_ISP_AWB_GET_MEAN_Y_G(x) (((x) >> 16) & 0xFF)
+/* ISP_AWB_WHITE_CNT */
+#define CIF_ISP_AWB_GET_PIXEL_CNT(x) ((x) & 0x3FFFFFF)
+
+#define CIF_ISP_AWB_GAINS_MAX_VAL 0x000003FF
+#define CIF_ISP_AWB_WINDOW_OFFSET_MAX 0x00000FFF
+#define CIF_ISP_AWB_WINDOW_MAX_SIZE 0x00001FFF
+#define CIF_ISP_AWB_CBCR_MAX_REF 0x000000FF
+#define CIF_ISP_AWB_THRES_MAX_YC 0x000000FF
+
+/* AE */
+/* ISP_EXP_CTRL */
+#define CIF_ISP_EXP_ENA BIT(0)
+#define CIF_ISP_EXP_CTRL_AUTOSTOP BIT(1)
+/*
+ *'1' luminance calculation according to Y=(R+G+B) x 0.332 (85/256)
+ *'0' luminance calculation according to Y=16+0.25R+0.5G+0.1094B
+ */
+#define CIF_ISP_EXP_CTRL_MEASMODE_1 BIT(31)
+
+/* ISP_EXP_H_SIZE */
+#define CIF_ISP_EXP_H_SIZE_SET(x) ((x) & 0x7FF)
+#define CIF_ISP_EXP_HEIGHT_MASK 0x000007FF
+/* ISP_EXP_V_SIZE : vertical size must be a multiple of 2). */
+#define CIF_ISP_EXP_V_SIZE_SET(x) ((x) & 0x7FE)
+
+/* ISP_EXP_H_OFFSET */
+#define CIF_ISP_EXP_H_OFFSET_SET(x) ((x) & 0x1FFF)
+#define CIF_ISP_EXP_MAX_HOFFS 2424
+/* ISP_EXP_V_OFFSET */
+#define CIF_ISP_EXP_V_OFFSET_SET(x) ((x) & 0x1FFF)
+#define CIF_ISP_EXP_MAX_VOFFS 1806
+
+#define CIF_ISP_EXP_ROW_NUM 5
+#define CIF_ISP_EXP_COLUMN_NUM 5
+#define CIF_ISP_EXP_NUM_LUMA_REGS \
+ (CIF_ISP_EXP_ROW_NUM * CIF_ISP_EXP_COLUMN_NUM)
+#define CIF_ISP_EXP_BLOCK_MAX_HSIZE 516
+#define CIF_ISP_EXP_BLOCK_MIN_HSIZE 35
+#define CIF_ISP_EXP_BLOCK_MAX_VSIZE 390
+#define CIF_ISP_EXP_BLOCK_MIN_VSIZE 28
+#define CIF_ISP_EXP_MAX_HSIZE \
+ (CIF_ISP_EXP_BLOCK_MAX_HSIZE * CIF_ISP_EXP_COLUMN_NUM + 1)
+#define CIF_ISP_EXP_MIN_HSIZE \
+ (CIF_ISP_EXP_BLOCK_MIN_HSIZE * CIF_ISP_EXP_COLUMN_NUM + 1)
+#define CIF_ISP_EXP_MAX_VSIZE \
+ (CIF_ISP_EXP_BLOCK_MAX_VSIZE * CIF_ISP_EXP_ROW_NUM + 1)
+#define CIF_ISP_EXP_MIN_VSIZE \
+ (CIF_ISP_EXP_BLOCK_MIN_VSIZE * CIF_ISP_EXP_ROW_NUM + 1)
+
+/* LSC: ISP_LSC_CTRL */
+#define CIF_ISP_LSC_CTRL_ENA BIT(0)
+#define CIF_ISP_LSC_SECT_SIZE_RESERVED 0xFC00FC00
+#define CIF_ISP_LSC_GRAD_RESERVED 0xF000F000
+#define CIF_ISP_LSC_SAMPLE_RESERVED 0xF000F000
+#define CIF_ISP_LSC_SECTORS_MAX 16
+#define CIF_ISP_LSC_TABLE_DATA(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 12))
+#define CIF_ISP_LSC_SECT_SIZE(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+#define CIF_ISP_LSC_GRAD_SIZE(v0, v1) \
+ (((v0) & 0xFFF) | (((v1) & 0xFFF) << 16))
+
+/* LSC: ISP_LSC_TABLE_SEL */
+#define CIF_ISP_LSC_TABLE_0 0
+#define CIF_ISP_LSC_TABLE_1 1
+
+/* LSC: ISP_LSC_STATUS */
+#define CIF_ISP_LSC_ACTIVE_TABLE BIT(1)
+#define CIF_ISP_LSC_TABLE_ADDRESS_0 0
+#define CIF_ISP_LSC_TABLE_ADDRESS_153 153
+
+/* FLT */
+/* ISP_FILT_MODE */
+#define CIF_ISP_FLT_ENA BIT(0)
+
+/*
+ * 0: green filter static mode (active filter factor = FILT_FAC_MID)
+ * 1: dynamic noise reduction/sharpen Default
+ */
+#define CIF_ISP_FLT_MODE_DNR BIT(1)
+#define CIF_ISP_FLT_MODE_MAX 1
+#define CIF_ISP_FLT_CHROMA_V_MODE(x) (((x) & 0x3) << 4)
+#define CIF_ISP_FLT_CHROMA_H_MODE(x) (((x) & 0x3) << 6)
+#define CIF_ISP_FLT_CHROMA_MODE_MAX 3
+#define CIF_ISP_FLT_GREEN_STAGE1(x) (((x) & 0xF) << 8)
+#define CIF_ISP_FLT_GREEN_STAGE1_MAX 8
+#define CIF_ISP_FLT_THREAD_RESERVED 0xFFFFFC00
+#define CIF_ISP_FLT_FAC_RESERVED 0xFFFFFFC0
+#define CIF_ISP_FLT_LUM_WEIGHT_RESERVED 0xFFF80000
+
+#define CIF_ISP_CTK_COEFF_RESERVED 0xFFFFF800
+#define CIF_ISP_XTALK_OFFSET_RESERVED 0xFFFFF000
+
+/* GOC */
+#define CIF_ISP_GAMMA_OUT_MODE_EQU BIT(0)
+#define CIF_ISP_GOC_MODE_MAX 1
+#define CIF_ISP_GOC_RESERVED 0xFFFFF800
+/* ISP_CTRL BIT 11*/
+#define CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA_READ(x) (((x) >> 11) & 1)
+
+/* DPCC */
+/* ISP_DPCC_MODE */
+#define CIF_ISP_DPCC_ENA BIT(0)
+#define CIF_ISP_DPCC_MODE_MAX 0x07
+#define CIF_ISP_DPCC_OUTPUTMODE_MAX 0x0F
+#define CIF_ISP_DPCC_SETUSE_MAX 0x0F
+#define CIF_ISP_DPCC_METHODS_SET_RESERVED 0xFFFFE000
+#define CIF_ISP_DPCC_LINE_THRESH_RESERVED 0xFFFF0000
+#define CIF_ISP_DPCC_LINE_MAD_FAC_RESERVED 0xFFFFC0C0
+#define CIF_ISP_DPCC_PG_FAC_RESERVED 0xFFFFC0C0
+#define CIF_ISP_DPCC_RND_THRESH_RESERVED 0xFFFF0000
+#define CIF_ISP_DPCC_RG_FAC_RESERVED 0xFFFFC0C0
+#define CIF_ISP_DPCC_RO_LIMIT_RESERVED 0xFFFFF000
+#define CIF_ISP_DPCC_RND_OFFS_RESERVED 0xFFFFF000
+
+/* BLS */
+/* ISP_BLS_CTRL */
+#define CIF_ISP_BLS_ENA BIT(0)
+#define CIF_ISP_BLS_MODE_MEASURED BIT(1)
+#define CIF_ISP_BLS_MODE_FIXED 0
+#define CIF_ISP_BLS_WINDOW_1 (1 << 2)
+#define CIF_ISP_BLS_WINDOW_2 (2 << 2)
+
+/* GAMMA-IN */
+#define CIFISP_DEGAMMA_X_RESERVED \
+ ((1 << 31) | (1 << 27) | (1 << 23) | (1 << 19) |\
+ (1 << 15) | (1 << 11) | (1 << 7) | (1 << 3))
+#define CIFISP_DEGAMMA_Y_RESERVED 0xFFFFF000
+
+/* AFM */
+#define CIF_ISP_AFM_ENA BIT(0)
+#define CIF_ISP_AFM_THRES_RESERVED 0xFFFF0000
+#define CIF_ISP_AFM_VAR_SHIFT_RESERVED 0xFFF8FFF8
+#define CIF_ISP_AFM_WINDOW_X_RESERVED 0xE000
+#define CIF_ISP_AFM_WINDOW_Y_RESERVED 0xF000
+#define CIF_ISP_AFM_WINDOW_X_MIN 0x5
+#define CIF_ISP_AFM_WINDOW_Y_MIN 0x2
+#define CIF_ISP_AFM_WINDOW_X(x) (((x) & 0x1FFF) << 16)
+#define CIF_ISP_AFM_WINDOW_Y(x) ((x) & 0x1FFF)
+
+/* DPF */
+#define CIF_ISP_DPF_MODE_EN BIT(0)
+#define CIF_ISP_DPF_MODE_B_FLT_DIS BIT(1)
+#define CIF_ISP_DPF_MODE_GB_FLT_DIS BIT(2)
+#define CIF_ISP_DPF_MODE_GR_FLT_DIS BIT(3)
+#define CIF_ISP_DPF_MODE_R_FLT_DIS BIT(4)
+#define CIF_ISP_DPF_MODE_RB_FLTSIZE_9x9 BIT(5)
+#define CIF_ISP_DPF_MODE_NLL_SEGMENTATION BIT(6)
+#define CIF_ISP_DPF_MODE_AWB_GAIN_COMP BIT(7)
+#define CIF_ISP_DPF_MODE_LSC_GAIN_COMP BIT(8)
+#define CIF_ISP_DPF_MODE_USE_NF_GAIN BIT(9)
+#define CIF_ISP_DPF_NF_GAIN_RESERVED 0xFFFFF000
+#define CIF_ISP_DPF_SPATIAL_COEFF_MAX 0x1F
+#define CIF_ISP_DPF_NLL_COEFF_N_MAX 0x3FF
+
+/* =================================================================== */
+/* CIF Registers */
+/* =================================================================== */
+#define CIF_CTRL_BASE 0x00000000
+#define CIF_CCL (CIF_CTRL_BASE + 0x00000000)
+#define CIF_VI_ID (CIF_CTRL_BASE + 0x00000008)
+#define CIF_ICCL (CIF_CTRL_BASE + 0x00000010)
+#define CIF_IRCL (CIF_CTRL_BASE + 0x00000014)
+#define CIF_VI_DPCL (CIF_CTRL_BASE + 0x00000018)
+
+#define CIF_IMG_EFF_BASE 0x00000200
+#define CIF_IMG_EFF_CTRL (CIF_IMG_EFF_BASE + 0x00000000)
+#define CIF_IMG_EFF_COLOR_SEL (CIF_IMG_EFF_BASE + 0x00000004)
+#define CIF_IMG_EFF_MAT_1 (CIF_IMG_EFF_BASE + 0x00000008)
+#define CIF_IMG_EFF_MAT_2 (CIF_IMG_EFF_BASE + 0x0000000C)
+#define CIF_IMG_EFF_MAT_3 (CIF_IMG_EFF_BASE + 0x00000010)
+#define CIF_IMG_EFF_MAT_4 (CIF_IMG_EFF_BASE + 0x00000014)
+#define CIF_IMG_EFF_MAT_5 (CIF_IMG_EFF_BASE + 0x00000018)
+#define CIF_IMG_EFF_TINT (CIF_IMG_EFF_BASE + 0x0000001C)
+#define CIF_IMG_EFF_CTRL_SHD (CIF_IMG_EFF_BASE + 0x00000020)
+#define CIF_IMG_EFF_SHARPEN (CIF_IMG_EFF_BASE + 0x00000024)
+
+#define CIF_SUPER_IMP_BASE 0x00000300
+#define CIF_SUPER_IMP_CTRL (CIF_SUPER_IMP_BASE + 0x00000000)
+#define CIF_SUPER_IMP_OFFSET_X (CIF_SUPER_IMP_BASE + 0x00000004)
+#define CIF_SUPER_IMP_OFFSET_Y (CIF_SUPER_IMP_BASE + 0x00000008)
+#define CIF_SUPER_IMP_COLOR_Y (CIF_SUPER_IMP_BASE + 0x0000000C)
+#define CIF_SUPER_IMP_COLOR_CB (CIF_SUPER_IMP_BASE + 0x00000010)
+#define CIF_SUPER_IMP_COLOR_CR (CIF_SUPER_IMP_BASE + 0x00000014)
+
+#define CIF_ISP_BASE 0x00000400
+#define CIF_ISP_CTRL (CIF_ISP_BASE + 0x00000000)
+#define CIF_ISP_ACQ_PROP (CIF_ISP_BASE + 0x00000004)
+#define CIF_ISP_ACQ_H_OFFS (CIF_ISP_BASE + 0x00000008)
+#define CIF_ISP_ACQ_V_OFFS (CIF_ISP_BASE + 0x0000000C)
+#define CIF_ISP_ACQ_H_SIZE (CIF_ISP_BASE + 0x00000010)
+#define CIF_ISP_ACQ_V_SIZE (CIF_ISP_BASE + 0x00000014)
+#define CIF_ISP_ACQ_NR_FRAMES (CIF_ISP_BASE + 0x00000018)
+#define CIF_ISP_GAMMA_DX_LO (CIF_ISP_BASE + 0x0000001C)
+#define CIF_ISP_GAMMA_DX_HI (CIF_ISP_BASE + 0x00000020)
+#define CIF_ISP_GAMMA_R_Y0 (CIF_ISP_BASE + 0x00000024)
+#define CIF_ISP_GAMMA_R_Y1 (CIF_ISP_BASE + 0x00000028)
+#define CIF_ISP_GAMMA_R_Y2 (CIF_ISP_BASE + 0x0000002C)
+#define CIF_ISP_GAMMA_R_Y3 (CIF_ISP_BASE + 0x00000030)
+#define CIF_ISP_GAMMA_R_Y4 (CIF_ISP_BASE + 0x00000034)
+#define CIF_ISP_GAMMA_R_Y5 (CIF_ISP_BASE + 0x00000038)
+#define CIF_ISP_GAMMA_R_Y6 (CIF_ISP_BASE + 0x0000003C)
+#define CIF_ISP_GAMMA_R_Y7 (CIF_ISP_BASE + 0x00000040)
+#define CIF_ISP_GAMMA_R_Y8 (CIF_ISP_BASE + 0x00000044)
+#define CIF_ISP_GAMMA_R_Y9 (CIF_ISP_BASE + 0x00000048)
+#define CIF_ISP_GAMMA_R_Y10 (CIF_ISP_BASE + 0x0000004C)
+#define CIF_ISP_GAMMA_R_Y11 (CIF_ISP_BASE + 0x00000050)
+#define CIF_ISP_GAMMA_R_Y12 (CIF_ISP_BASE + 0x00000054)
+#define CIF_ISP_GAMMA_R_Y13 (CIF_ISP_BASE + 0x00000058)
+#define CIF_ISP_GAMMA_R_Y14 (CIF_ISP_BASE + 0x0000005C)
+#define CIF_ISP_GAMMA_R_Y15 (CIF_ISP_BASE + 0x00000060)
+#define CIF_ISP_GAMMA_R_Y16 (CIF_ISP_BASE + 0x00000064)
+#define CIF_ISP_GAMMA_G_Y0 (CIF_ISP_BASE + 0x00000068)
+#define CIF_ISP_GAMMA_G_Y1 (CIF_ISP_BASE + 0x0000006C)
+#define CIF_ISP_GAMMA_G_Y2 (CIF_ISP_BASE + 0x00000070)
+#define CIF_ISP_GAMMA_G_Y3 (CIF_ISP_BASE + 0x00000074)
+#define CIF_ISP_GAMMA_G_Y4 (CIF_ISP_BASE + 0x00000078)
+#define CIF_ISP_GAMMA_G_Y5 (CIF_ISP_BASE + 0x0000007C)
+#define CIF_ISP_GAMMA_G_Y6 (CIF_ISP_BASE + 0x00000080)
+#define CIF_ISP_GAMMA_G_Y7 (CIF_ISP_BASE + 0x00000084)
+#define CIF_ISP_GAMMA_G_Y8 (CIF_ISP_BASE + 0x00000088)
+#define CIF_ISP_GAMMA_G_Y9 (CIF_ISP_BASE + 0x0000008C)
+#define CIF_ISP_GAMMA_G_Y10 (CIF_ISP_BASE + 0x00000090)
+#define CIF_ISP_GAMMA_G_Y11 (CIF_ISP_BASE + 0x00000094)
+#define CIF_ISP_GAMMA_G_Y12 (CIF_ISP_BASE + 0x00000098)
+#define CIF_ISP_GAMMA_G_Y13 (CIF_ISP_BASE + 0x0000009C)
+#define CIF_ISP_GAMMA_G_Y14 (CIF_ISP_BASE + 0x000000A0)
+#define CIF_ISP_GAMMA_G_Y15 (CIF_ISP_BASE + 0x000000A4)
+#define CIF_ISP_GAMMA_G_Y16 (CIF_ISP_BASE + 0x000000A8)
+#define CIF_ISP_GAMMA_B_Y0 (CIF_ISP_BASE + 0x000000AC)
+#define CIF_ISP_GAMMA_B_Y1 (CIF_ISP_BASE + 0x000000B0)
+#define CIF_ISP_GAMMA_B_Y2 (CIF_ISP_BASE + 0x000000B4)
+#define CIF_ISP_GAMMA_B_Y3 (CIF_ISP_BASE + 0x000000B8)
+#define CIF_ISP_GAMMA_B_Y4 (CIF_ISP_BASE + 0x000000BC)
+#define CIF_ISP_GAMMA_B_Y5 (CIF_ISP_BASE + 0x000000C0)
+#define CIF_ISP_GAMMA_B_Y6 (CIF_ISP_BASE + 0x000000C4)
+#define CIF_ISP_GAMMA_B_Y7 (CIF_ISP_BASE + 0x000000C8)
+#define CIF_ISP_GAMMA_B_Y8 (CIF_ISP_BASE + 0x000000CC)
+#define CIF_ISP_GAMMA_B_Y9 (CIF_ISP_BASE + 0x000000D0)
+#define CIF_ISP_GAMMA_B_Y10 (CIF_ISP_BASE + 0x000000D4)
+#define CIF_ISP_GAMMA_B_Y11 (CIF_ISP_BASE + 0x000000D8)
+#define CIF_ISP_GAMMA_B_Y12 (CIF_ISP_BASE + 0x000000DC)
+#define CIF_ISP_GAMMA_B_Y13 (CIF_ISP_BASE + 0x000000E0)
+#define CIF_ISP_GAMMA_B_Y14 (CIF_ISP_BASE + 0x000000E4)
+#define CIF_ISP_GAMMA_B_Y15 (CIF_ISP_BASE + 0x000000E8)
+#define CIF_ISP_GAMMA_B_Y16 (CIF_ISP_BASE + 0x000000EC)
+#define CIF_ISP_AWB_PROP (CIF_ISP_BASE + 0x00000110)
+#define CIF_ISP_AWB_WND_H_OFFS (CIF_ISP_BASE + 0x00000114)
+#define CIF_ISP_AWB_WND_V_OFFS (CIF_ISP_BASE + 0x00000118)
+#define CIF_ISP_AWB_WND_H_SIZE (CIF_ISP_BASE + 0x0000011C)
+#define CIF_ISP_AWB_WND_V_SIZE (CIF_ISP_BASE + 0x00000120)
+#define CIF_ISP_AWB_FRAMES (CIF_ISP_BASE + 0x00000124)
+#define CIF_ISP_AWB_REF (CIF_ISP_BASE + 0x00000128)
+#define CIF_ISP_AWB_THRESH (CIF_ISP_BASE + 0x0000012C)
+#define CIF_ISP_AWB_GAIN_G (CIF_ISP_BASE + 0x00000138)
+#define CIF_ISP_AWB_GAIN_RB (CIF_ISP_BASE + 0x0000013C)
+#define CIF_ISP_AWB_WHITE_CNT (CIF_ISP_BASE + 0x00000140)
+#define CIF_ISP_AWB_MEAN (CIF_ISP_BASE + 0x00000144)
+#define CIF_ISP_CC_COEFF_0 (CIF_ISP_BASE + 0x00000170)
+#define CIF_ISP_CC_COEFF_1 (CIF_ISP_BASE + 0x00000174)
+#define CIF_ISP_CC_COEFF_2 (CIF_ISP_BASE + 0x00000178)
+#define CIF_ISP_CC_COEFF_3 (CIF_ISP_BASE + 0x0000017C)
+#define CIF_ISP_CC_COEFF_4 (CIF_ISP_BASE + 0x00000180)
+#define CIF_ISP_CC_COEFF_5 (CIF_ISP_BASE + 0x00000184)
+#define CIF_ISP_CC_COEFF_6 (CIF_ISP_BASE + 0x00000188)
+#define CIF_ISP_CC_COEFF_7 (CIF_ISP_BASE + 0x0000018C)
+#define CIF_ISP_CC_COEFF_8 (CIF_ISP_BASE + 0x00000190)
+#define CIF_ISP_OUT_H_OFFS (CIF_ISP_BASE + 0x00000194)
+#define CIF_ISP_OUT_V_OFFS (CIF_ISP_BASE + 0x00000198)
+#define CIF_ISP_OUT_H_SIZE (CIF_ISP_BASE + 0x0000019C)
+#define CIF_ISP_OUT_V_SIZE (CIF_ISP_BASE + 0x000001A0)
+#define CIF_ISP_DEMOSAIC (CIF_ISP_BASE + 0x000001A4)
+#define CIF_ISP_FLAGS_SHD (CIF_ISP_BASE + 0x000001A8)
+#define CIF_ISP_OUT_H_OFFS_SHD (CIF_ISP_BASE + 0x000001AC)
+#define CIF_ISP_OUT_V_OFFS_SHD (CIF_ISP_BASE + 0x000001B0)
+#define CIF_ISP_OUT_H_SIZE_SHD (CIF_ISP_BASE + 0x000001B4)
+#define CIF_ISP_OUT_V_SIZE_SHD (CIF_ISP_BASE + 0x000001B8)
+#define CIF_ISP_IMSC (CIF_ISP_BASE + 0x000001BC)
+#define CIF_ISP_RIS (CIF_ISP_BASE + 0x000001C0)
+#define CIF_ISP_MIS (CIF_ISP_BASE + 0x000001C4)
+#define CIF_ISP_ICR (CIF_ISP_BASE + 0x000001C8)
+#define CIF_ISP_ISR (CIF_ISP_BASE + 0x000001CC)
+#define CIF_ISP_CT_COEFF_0 (CIF_ISP_BASE + 0x000001D0)
+#define CIF_ISP_CT_COEFF_1 (CIF_ISP_BASE + 0x000001D4)
+#define CIF_ISP_CT_COEFF_2 (CIF_ISP_BASE + 0x000001D8)
+#define CIF_ISP_CT_COEFF_3 (CIF_ISP_BASE + 0x000001DC)
+#define CIF_ISP_CT_COEFF_4 (CIF_ISP_BASE + 0x000001E0)
+#define CIF_ISP_CT_COEFF_5 (CIF_ISP_BASE + 0x000001E4)
+#define CIF_ISP_CT_COEFF_6 (CIF_ISP_BASE + 0x000001E8)
+#define CIF_ISP_CT_COEFF_7 (CIF_ISP_BASE + 0x000001EC)
+#define CIF_ISP_CT_COEFF_8 (CIF_ISP_BASE + 0x000001F0)
+#define CIF_ISP_GAMMA_OUT_MODE (CIF_ISP_BASE + 0x000001F4)
+#define CIF_ISP_GAMMA_OUT_Y_0 (CIF_ISP_BASE + 0x000001F8)
+#define CIF_ISP_GAMMA_OUT_Y_1 (CIF_ISP_BASE + 0x000001FC)
+#define CIF_ISP_GAMMA_OUT_Y_2 (CIF_ISP_BASE + 0x00000200)
+#define CIF_ISP_GAMMA_OUT_Y_3 (CIF_ISP_BASE + 0x00000204)
+#define CIF_ISP_GAMMA_OUT_Y_4 (CIF_ISP_BASE + 0x00000208)
+#define CIF_ISP_GAMMA_OUT_Y_5 (CIF_ISP_BASE + 0x0000020C)
+#define CIF_ISP_GAMMA_OUT_Y_6 (CIF_ISP_BASE + 0x00000210)
+#define CIF_ISP_GAMMA_OUT_Y_7 (CIF_ISP_BASE + 0x00000214)
+#define CIF_ISP_GAMMA_OUT_Y_8 (CIF_ISP_BASE + 0x00000218)
+#define CIF_ISP_GAMMA_OUT_Y_9 (CIF_ISP_BASE + 0x0000021C)
+#define CIF_ISP_GAMMA_OUT_Y_10 (CIF_ISP_BASE + 0x00000220)
+#define CIF_ISP_GAMMA_OUT_Y_11 (CIF_ISP_BASE + 0x00000224)
+#define CIF_ISP_GAMMA_OUT_Y_12 (CIF_ISP_BASE + 0x00000228)
+#define CIF_ISP_GAMMA_OUT_Y_13 (CIF_ISP_BASE + 0x0000022C)
+#define CIF_ISP_GAMMA_OUT_Y_14 (CIF_ISP_BASE + 0x00000230)
+#define CIF_ISP_GAMMA_OUT_Y_15 (CIF_ISP_BASE + 0x00000234)
+#define CIF_ISP_GAMMA_OUT_Y_16 (CIF_ISP_BASE + 0x00000238)
+#define CIF_ISP_ERR (CIF_ISP_BASE + 0x0000023C)
+#define CIF_ISP_ERR_CLR (CIF_ISP_BASE + 0x00000240)
+#define CIF_ISP_FRAME_COUNT (CIF_ISP_BASE + 0x00000244)
+#define CIF_ISP_CT_OFFSET_R (CIF_ISP_BASE + 0x00000248)
+#define CIF_ISP_CT_OFFSET_G (CIF_ISP_BASE + 0x0000024C)
+#define CIF_ISP_CT_OFFSET_B (CIF_ISP_BASE + 0x00000250)
+
+#define CIF_ISP_FLASH_BASE 0x00000660
+#define CIF_ISP_FLASH_CMD (CIF_ISP_FLASH_BASE + 0x00000000)
+#define CIF_ISP_FLASH_CONFIG (CIF_ISP_FLASH_BASE + 0x00000004)
+#define CIF_ISP_FLASH_PREDIV (CIF_ISP_FLASH_BASE + 0x00000008)
+#define CIF_ISP_FLASH_DELAY (CIF_ISP_FLASH_BASE + 0x0000000C)
+#define CIF_ISP_FLASH_TIME (CIF_ISP_FLASH_BASE + 0x00000010)
+#define CIF_ISP_FLASH_MAXP (CIF_ISP_FLASH_BASE + 0x00000014)
+
+#define CIF_ISP_SH_BASE 0x00000680
+#define CIF_ISP_SH_CTRL (CIF_ISP_SH_BASE + 0x00000000)
+#define CIF_ISP_SH_PREDIV (CIF_ISP_SH_BASE + 0x00000004)
+#define CIF_ISP_SH_DELAY (CIF_ISP_SH_BASE + 0x00000008)
+#define CIF_ISP_SH_TIME (CIF_ISP_SH_BASE + 0x0000000C)
+
+#define CIF_C_PROC_BASE 0x00000800
+#define CIF_C_PROC_CTRL (CIF_C_PROC_BASE + 0x00000000)
+#define CIF_C_PROC_CONTRAST (CIF_C_PROC_BASE + 0x00000004)
+#define CIF_C_PROC_BRIGHTNESS (CIF_C_PROC_BASE + 0x00000008)
+#define CIF_C_PROC_SATURATION (CIF_C_PROC_BASE + 0x0000000C)
+#define CIF_C_PROC_HUE (CIF_C_PROC_BASE + 0x00000010)
+
+#define CIF_DUAL_CROP_BASE 0x00000880
+#define CIF_DUAL_CROP_CTRL (CIF_DUAL_CROP_BASE + 0x00000000)
+#define CIF_DUAL_CROP_M_H_OFFS (CIF_DUAL_CROP_BASE + 0x00000004)
+#define CIF_DUAL_CROP_M_V_OFFS (CIF_DUAL_CROP_BASE + 0x00000008)
+#define CIF_DUAL_CROP_M_H_SIZE (CIF_DUAL_CROP_BASE + 0x0000000C)
+#define CIF_DUAL_CROP_M_V_SIZE (CIF_DUAL_CROP_BASE + 0x00000010)
+#define CIF_DUAL_CROP_S_H_OFFS (CIF_DUAL_CROP_BASE + 0x00000014)
+#define CIF_DUAL_CROP_S_V_OFFS (CIF_DUAL_CROP_BASE + 0x00000018)
+#define CIF_DUAL_CROP_S_H_SIZE (CIF_DUAL_CROP_BASE + 0x0000001C)
+#define CIF_DUAL_CROP_S_V_SIZE (CIF_DUAL_CROP_BASE + 0x00000020)
+#define CIF_DUAL_CROP_M_H_OFFS_SHD (CIF_DUAL_CROP_BASE + 0x00000024)
+#define CIF_DUAL_CROP_M_V_OFFS_SHD (CIF_DUAL_CROP_BASE + 0x00000028)
+#define CIF_DUAL_CROP_M_H_SIZE_SHD (CIF_DUAL_CROP_BASE + 0x0000002C)
+#define CIF_DUAL_CROP_M_V_SIZE_SHD (CIF_DUAL_CROP_BASE + 0x00000030)
+#define CIF_DUAL_CROP_S_H_OFFS_SHD (CIF_DUAL_CROP_BASE + 0x00000034)
+#define CIF_DUAL_CROP_S_V_OFFS_SHD (CIF_DUAL_CROP_BASE + 0x00000038)
+#define CIF_DUAL_CROP_S_H_SIZE_SHD (CIF_DUAL_CROP_BASE + 0x0000003C)
+#define CIF_DUAL_CROP_S_V_SIZE_SHD (CIF_DUAL_CROP_BASE + 0x00000040)
+
+#define CIF_MRSZ_BASE 0x00000C00
+#define CIF_MRSZ_CTRL (CIF_MRSZ_BASE + 0x00000000)
+#define CIF_MRSZ_SCALE_HY (CIF_MRSZ_BASE + 0x00000004)
+#define CIF_MRSZ_SCALE_HCB (CIF_MRSZ_BASE + 0x00000008)
+#define CIF_MRSZ_SCALE_HCR (CIF_MRSZ_BASE + 0x0000000C)
+#define CIF_MRSZ_SCALE_VY (CIF_MRSZ_BASE + 0x00000010)
+#define CIF_MRSZ_SCALE_VC (CIF_MRSZ_BASE + 0x00000014)
+#define CIF_MRSZ_PHASE_HY (CIF_MRSZ_BASE + 0x00000018)
+#define CIF_MRSZ_PHASE_HC (CIF_MRSZ_BASE + 0x0000001C)
+#define CIF_MRSZ_PHASE_VY (CIF_MRSZ_BASE + 0x00000020)
+#define CIF_MRSZ_PHASE_VC (CIF_MRSZ_BASE + 0x00000024)
+#define CIF_MRSZ_SCALE_LUT_ADDR (CIF_MRSZ_BASE + 0x00000028)
+#define CIF_MRSZ_SCALE_LUT (CIF_MRSZ_BASE + 0x0000002C)
+#define CIF_MRSZ_CTRL_SHD (CIF_MRSZ_BASE + 0x00000030)
+#define CIF_MRSZ_SCALE_HY_SHD (CIF_MRSZ_BASE + 0x00000034)
+#define CIF_MRSZ_SCALE_HCB_SHD (CIF_MRSZ_BASE + 0x00000038)
+#define CIF_MRSZ_SCALE_HCR_SHD (CIF_MRSZ_BASE + 0x0000003C)
+#define CIF_MRSZ_SCALE_VY_SHD (CIF_MRSZ_BASE + 0x00000040)
+#define CIF_MRSZ_SCALE_VC_SHD (CIF_MRSZ_BASE + 0x00000044)
+#define CIF_MRSZ_PHASE_HY_SHD (CIF_MRSZ_BASE + 0x00000048)
+#define CIF_MRSZ_PHASE_HC_SHD (CIF_MRSZ_BASE + 0x0000004C)
+#define CIF_MRSZ_PHASE_VY_SHD (CIF_MRSZ_BASE + 0x00000050)
+#define CIF_MRSZ_PHASE_VC_SHD (CIF_MRSZ_BASE + 0x00000054)
+
+#define CIF_SRSZ_BASE 0x00001000
+#define CIF_SRSZ_CTRL (CIF_SRSZ_BASE + 0x00000000)
+#define CIF_SRSZ_SCALE_HY (CIF_SRSZ_BASE + 0x00000004)
+#define CIF_SRSZ_SCALE_HCB (CIF_SRSZ_BASE + 0x00000008)
+#define CIF_SRSZ_SCALE_HCR (CIF_SRSZ_BASE + 0x0000000C)
+#define CIF_SRSZ_SCALE_VY (CIF_SRSZ_BASE + 0x00000010)
+#define CIF_SRSZ_SCALE_VC (CIF_SRSZ_BASE + 0x00000014)
+#define CIF_SRSZ_PHASE_HY (CIF_SRSZ_BASE + 0x00000018)
+#define CIF_SRSZ_PHASE_HC (CIF_SRSZ_BASE + 0x0000001C)
+#define CIF_SRSZ_PHASE_VY (CIF_SRSZ_BASE + 0x00000020)
+#define CIF_SRSZ_PHASE_VC (CIF_SRSZ_BASE + 0x00000024)
+#define CIF_SRSZ_SCALE_LUT_ADDR (CIF_SRSZ_BASE + 0x00000028)
+#define CIF_SRSZ_SCALE_LUT (CIF_SRSZ_BASE + 0x0000002C)
+#define CIF_SRSZ_CTRL_SHD (CIF_SRSZ_BASE + 0x00000030)
+#define CIF_SRSZ_SCALE_HY_SHD (CIF_SRSZ_BASE + 0x00000034)
+#define CIF_SRSZ_SCALE_HCB_SHD (CIF_SRSZ_BASE + 0x00000038)
+#define CIF_SRSZ_SCALE_HCR_SHD (CIF_SRSZ_BASE + 0x0000003C)
+#define CIF_SRSZ_SCALE_VY_SHD (CIF_SRSZ_BASE + 0x00000040)
+#define CIF_SRSZ_SCALE_VC_SHD (CIF_SRSZ_BASE + 0x00000044)
+#define CIF_SRSZ_PHASE_HY_SHD (CIF_SRSZ_BASE + 0x00000048)
+#define CIF_SRSZ_PHASE_HC_SHD (CIF_SRSZ_BASE + 0x0000004C)
+#define CIF_SRSZ_PHASE_VY_SHD (CIF_SRSZ_BASE + 0x00000050)
+#define CIF_SRSZ_PHASE_VC_SHD (CIF_SRSZ_BASE + 0x00000054)
+
+#define CIF_MI_BASE 0x00001400
+#define CIF_MI_CTRL (CIF_MI_BASE + 0x00000000)
+#define CIF_MI_INIT (CIF_MI_BASE + 0x00000004)
+#define CIF_MI_MP_Y_BASE_AD_INIT (CIF_MI_BASE + 0x00000008)
+#define CIF_MI_MP_Y_SIZE_INIT (CIF_MI_BASE + 0x0000000C)
+#define CIF_MI_MP_Y_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000010)
+#define CIF_MI_MP_Y_OFFS_CNT_START (CIF_MI_BASE + 0x00000014)
+#define CIF_MI_MP_Y_IRQ_OFFS_INIT (CIF_MI_BASE + 0x00000018)
+#define CIF_MI_MP_CB_BASE_AD_INIT (CIF_MI_BASE + 0x0000001C)
+#define CIF_MI_MP_CB_SIZE_INIT (CIF_MI_BASE + 0x00000020)
+#define CIF_MI_MP_CB_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000024)
+#define CIF_MI_MP_CB_OFFS_CNT_START (CIF_MI_BASE + 0x00000028)
+#define CIF_MI_MP_CR_BASE_AD_INIT (CIF_MI_BASE + 0x0000002C)
+#define CIF_MI_MP_CR_SIZE_INIT (CIF_MI_BASE + 0x00000030)
+#define CIF_MI_MP_CR_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000034)
+#define CIF_MI_MP_CR_OFFS_CNT_START (CIF_MI_BASE + 0x00000038)
+#define CIF_MI_SP_Y_BASE_AD_INIT (CIF_MI_BASE + 0x0000003C)
+#define CIF_MI_SP_Y_SIZE_INIT (CIF_MI_BASE + 0x00000040)
+#define CIF_MI_SP_Y_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000044)
+#define CIF_MI_SP_Y_OFFS_CNT_START (CIF_MI_BASE + 0x00000048)
+#define CIF_MI_SP_Y_LLENGTH (CIF_MI_BASE + 0x0000004C)
+#define CIF_MI_SP_CB_BASE_AD_INIT (CIF_MI_BASE + 0x00000050)
+#define CIF_MI_SP_CB_SIZE_INIT (CIF_MI_BASE + 0x00000054)
+#define CIF_MI_SP_CB_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000058)
+#define CIF_MI_SP_CB_OFFS_CNT_START (CIF_MI_BASE + 0x0000005C)
+#define CIF_MI_SP_CR_BASE_AD_INIT (CIF_MI_BASE + 0x00000060)
+#define CIF_MI_SP_CR_SIZE_INIT (CIF_MI_BASE + 0x00000064)
+#define CIF_MI_SP_CR_OFFS_CNT_INIT (CIF_MI_BASE + 0x00000068)
+#define CIF_MI_SP_CR_OFFS_CNT_START (CIF_MI_BASE + 0x0000006C)
+#define CIF_MI_BYTE_CNT (CIF_MI_BASE + 0x00000070)
+#define CIF_MI_CTRL_SHD (CIF_MI_BASE + 0x00000074)
+#define CIF_MI_MP_Y_BASE_AD_SHD (CIF_MI_BASE + 0x00000078)
+#define CIF_MI_MP_Y_SIZE_SHD (CIF_MI_BASE + 0x0000007C)
+#define CIF_MI_MP_Y_OFFS_CNT_SHD (CIF_MI_BASE + 0x00000080)
+#define CIF_MI_MP_Y_IRQ_OFFS_SHD (CIF_MI_BASE + 0x00000084)
+#define CIF_MI_MP_CB_BASE_AD_SHD (CIF_MI_BASE + 0x00000088)
+#define CIF_MI_MP_CB_SIZE_SHD (CIF_MI_BASE + 0x0000008C)
+#define CIF_MI_MP_CB_OFFS_CNT_SHD (CIF_MI_BASE + 0x00000090)
+#define CIF_MI_MP_CR_BASE_AD_SHD (CIF_MI_BASE + 0x00000094)
+#define CIF_MI_MP_CR_SIZE_SHD (CIF_MI_BASE + 0x00000098)
+#define CIF_MI_MP_CR_OFFS_CNT_SHD (CIF_MI_BASE + 0x0000009C)
+#define CIF_MI_SP_Y_BASE_AD_SHD (CIF_MI_BASE + 0x000000A0)
+#define CIF_MI_SP_Y_SIZE_SHD (CIF_MI_BASE + 0x000000A4)
+#define CIF_MI_SP_Y_OFFS_CNT_SHD (CIF_MI_BASE + 0x000000A8)
+#define CIF_MI_SP_CB_BASE_AD_SHD (CIF_MI_BASE + 0x000000B0)
+#define CIF_MI_SP_CB_SIZE_SHD (CIF_MI_BASE + 0x000000B4)
+#define CIF_MI_SP_CB_OFFS_CNT_SHD (CIF_MI_BASE + 0x000000B8)
+#define CIF_MI_SP_CR_BASE_AD_SHD (CIF_MI_BASE + 0x000000BC)
+#define CIF_MI_SP_CR_SIZE_SHD (CIF_MI_BASE + 0x000000C0)
+#define CIF_MI_SP_CR_OFFS_CNT_SHD (CIF_MI_BASE + 0x000000C4)
+#define CIF_MI_DMA_Y_PIC_START_AD (CIF_MI_BASE + 0x000000C8)
+#define CIF_MI_DMA_Y_PIC_WIDTH (CIF_MI_BASE + 0x000000CC)
+#define CIF_MI_DMA_Y_LLENGTH (CIF_MI_BASE + 0x000000D0)
+#define CIF_MI_DMA_Y_PIC_SIZE (CIF_MI_BASE + 0x000000D4)
+#define CIF_MI_DMA_CB_PIC_START_AD (CIF_MI_BASE + 0x000000D8)
+#define CIF_MI_DMA_CR_PIC_START_AD (CIF_MI_BASE + 0x000000E8)
+#define CIF_MI_IMSC (CIF_MI_BASE + 0x000000F8)
+#define CIF_MI_RIS (CIF_MI_BASE + 0x000000FC)
+#define CIF_MI_MIS (CIF_MI_BASE + 0x00000100)
+#define CIF_MI_ICR (CIF_MI_BASE + 0x00000104)
+#define CIF_MI_ISR (CIF_MI_BASE + 0x00000108)
+#define CIF_MI_STATUS (CIF_MI_BASE + 0x0000010C)
+#define CIF_MI_STATUS_CLR (CIF_MI_BASE + 0x00000110)
+#define CIF_MI_SP_Y_PIC_WIDTH (CIF_MI_BASE + 0x00000114)
+#define CIF_MI_SP_Y_PIC_HEIGHT (CIF_MI_BASE + 0x00000118)
+#define CIF_MI_SP_Y_PIC_SIZE (CIF_MI_BASE + 0x0000011C)
+#define CIF_MI_DMA_CTRL (CIF_MI_BASE + 0x00000120)
+#define CIF_MI_DMA_START (CIF_MI_BASE + 0x00000124)
+#define CIF_MI_DMA_STATUS (CIF_MI_BASE + 0x00000128)
+#define CIF_MI_PIXEL_COUNT (CIF_MI_BASE + 0x0000012C)
+#define CIF_MI_MP_Y_BASE_AD_INIT2 (CIF_MI_BASE + 0x00000130)
+#define CIF_MI_MP_CB_BASE_AD_INIT2 (CIF_MI_BASE + 0x00000134)
+#define CIF_MI_MP_CR_BASE_AD_INIT2 (CIF_MI_BASE + 0x00000138)
+#define CIF_MI_SP_Y_BASE_AD_INIT2 (CIF_MI_BASE + 0x0000013C)
+#define CIF_MI_SP_CB_BASE_AD_INIT2 (CIF_MI_BASE + 0x00000140)
+#define CIF_MI_SP_CR_BASE_AD_INIT2 (CIF_MI_BASE + 0x00000144)
+#define CIF_MI_XTD_FORMAT_CTRL (CIF_MI_BASE + 0x00000148)
+
+#define CIF_SMIA_BASE 0x00001A00
+#define CIF_SMIA_CTRL (CIF_SMIA_BASE + 0x00000000)
+#define CIF_SMIA_STATUS (CIF_SMIA_BASE + 0x00000004)
+#define CIF_SMIA_IMSC (CIF_SMIA_BASE + 0x00000008)
+#define CIF_SMIA_RIS (CIF_SMIA_BASE + 0x0000000C)
+#define CIF_SMIA_MIS (CIF_SMIA_BASE + 0x00000010)
+#define CIF_SMIA_ICR (CIF_SMIA_BASE + 0x00000014)
+#define CIF_SMIA_ISR (CIF_SMIA_BASE + 0x00000018)
+#define CIF_SMIA_DATA_FORMAT_SEL (CIF_SMIA_BASE + 0x0000001C)
+#define CIF_SMIA_SOF_EMB_DATA_LINES (CIF_SMIA_BASE + 0x00000020)
+#define CIF_SMIA_EMB_HSTART (CIF_SMIA_BASE + 0x00000024)
+#define CIF_SMIA_EMB_HSIZE (CIF_SMIA_BASE + 0x00000028)
+#define CIF_SMIA_EMB_VSTART (CIF_SMIA_BASE + 0x0000002c)
+#define CIF_SMIA_NUM_LINES (CIF_SMIA_BASE + 0x00000030)
+#define CIF_SMIA_EMB_DATA_FIFO (CIF_SMIA_BASE + 0x00000034)
+#define CIF_SMIA_EMB_DATA_WATERMARK (CIF_SMIA_BASE + 0x00000038)
+
+#define CIF_MIPI_BASE 0x00001C00
+#define CIF_MIPI_CTRL (CIF_MIPI_BASE + 0x00000000)
+#define CIF_MIPI_STATUS (CIF_MIPI_BASE + 0x00000004)
+#define CIF_MIPI_IMSC (CIF_MIPI_BASE + 0x00000008)
+#define CIF_MIPI_RIS (CIF_MIPI_BASE + 0x0000000C)
+#define CIF_MIPI_MIS (CIF_MIPI_BASE + 0x00000010)
+#define CIF_MIPI_ICR (CIF_MIPI_BASE + 0x00000014)
+#define CIF_MIPI_ISR (CIF_MIPI_BASE + 0x00000018)
+#define CIF_MIPI_CUR_DATA_ID (CIF_MIPI_BASE + 0x0000001C)
+#define CIF_MIPI_IMG_DATA_SEL (CIF_MIPI_BASE + 0x00000020)
+#define CIF_MIPI_ADD_DATA_SEL_1 (CIF_MIPI_BASE + 0x00000024)
+#define CIF_MIPI_ADD_DATA_SEL_2 (CIF_MIPI_BASE + 0x00000028)
+#define CIF_MIPI_ADD_DATA_SEL_3 (CIF_MIPI_BASE + 0x0000002C)
+#define CIF_MIPI_ADD_DATA_SEL_4 (CIF_MIPI_BASE + 0x00000030)
+#define CIF_MIPI_ADD_DATA_FIFO (CIF_MIPI_BASE + 0x00000034)
+#define CIF_MIPI_FIFO_FILL_LEVEL (CIF_MIPI_BASE + 0x00000038)
+#define CIF_MIPI_COMPRESSED_MODE (CIF_MIPI_BASE + 0x0000003C)
+#define CIF_MIPI_FRAME (CIF_MIPI_BASE + 0x00000040)
+#define CIF_MIPI_GEN_SHORT_DT (CIF_MIPI_BASE + 0x00000044)
+#define CIF_MIPI_GEN_SHORT_8_9 (CIF_MIPI_BASE + 0x00000048)
+#define CIF_MIPI_GEN_SHORT_A_B (CIF_MIPI_BASE + 0x0000004C)
+#define CIF_MIPI_GEN_SHORT_C_D (CIF_MIPI_BASE + 0x00000050)
+#define CIF_MIPI_GEN_SHORT_E_F (CIF_MIPI_BASE + 0x00000054)
+
+#define CIF_ISP_AFM_BASE 0x00002000
+#define CIF_ISP_AFM_CTRL (CIF_ISP_AFM_BASE + 0x00000000)
+#define CIF_ISP_AFM_LT_A (CIF_ISP_AFM_BASE + 0x00000004)
+#define CIF_ISP_AFM_RB_A (CIF_ISP_AFM_BASE + 0x00000008)
+#define CIF_ISP_AFM_LT_B (CIF_ISP_AFM_BASE + 0x0000000C)
+#define CIF_ISP_AFM_RB_B (CIF_ISP_AFM_BASE + 0x00000010)
+#define CIF_ISP_AFM_LT_C (CIF_ISP_AFM_BASE + 0x00000014)
+#define CIF_ISP_AFM_RB_C (CIF_ISP_AFM_BASE + 0x00000018)
+#define CIF_ISP_AFM_THRES (CIF_ISP_AFM_BASE + 0x0000001C)
+#define CIF_ISP_AFM_VAR_SHIFT (CIF_ISP_AFM_BASE + 0x00000020)
+#define CIF_ISP_AFM_SUM_A (CIF_ISP_AFM_BASE + 0x00000024)
+#define CIF_ISP_AFM_SUM_B (CIF_ISP_AFM_BASE + 0x00000028)
+#define CIF_ISP_AFM_SUM_C (CIF_ISP_AFM_BASE + 0x0000002C)
+#define CIF_ISP_AFM_LUM_A (CIF_ISP_AFM_BASE + 0x00000030)
+#define CIF_ISP_AFM_LUM_B (CIF_ISP_AFM_BASE + 0x00000034)
+#define CIF_ISP_AFM_LUM_C (CIF_ISP_AFM_BASE + 0x00000038)
+
+#define CIF_ISP_LSC_BASE 0x00002200
+#define CIF_ISP_LSC_CTRL (CIF_ISP_LSC_BASE + 0x00000000)
+#define CIF_ISP_LSC_R_TABLE_ADDR (CIF_ISP_LSC_BASE + 0x00000004)
+#define CIF_ISP_LSC_GR_TABLE_ADDR (CIF_ISP_LSC_BASE + 0x00000008)
+#define CIF_ISP_LSC_B_TABLE_ADDR (CIF_ISP_LSC_BASE + 0x0000000C)
+#define CIF_ISP_LSC_GB_TABLE_ADDR (CIF_ISP_LSC_BASE + 0x00000010)
+#define CIF_ISP_LSC_R_TABLE_DATA (CIF_ISP_LSC_BASE + 0x00000014)
+#define CIF_ISP_LSC_GR_TABLE_DATA (CIF_ISP_LSC_BASE + 0x00000018)
+#define CIF_ISP_LSC_B_TABLE_DATA (CIF_ISP_LSC_BASE + 0x0000001C)
+#define CIF_ISP_LSC_GB_TABLE_DATA (CIF_ISP_LSC_BASE + 0x00000020)
+#define CIF_ISP_LSC_XGRAD_01 (CIF_ISP_LSC_BASE + 0x00000024)
+#define CIF_ISP_LSC_XGRAD_23 (CIF_ISP_LSC_BASE + 0x00000028)
+#define CIF_ISP_LSC_XGRAD_45 (CIF_ISP_LSC_BASE + 0x0000002C)
+#define CIF_ISP_LSC_XGRAD_67 (CIF_ISP_LSC_BASE + 0x00000030)
+#define CIF_ISP_LSC_YGRAD_01 (CIF_ISP_LSC_BASE + 0x00000034)
+#define CIF_ISP_LSC_YGRAD_23 (CIF_ISP_LSC_BASE + 0x00000038)
+#define CIF_ISP_LSC_YGRAD_45 (CIF_ISP_LSC_BASE + 0x0000003C)
+#define CIF_ISP_LSC_YGRAD_67 (CIF_ISP_LSC_BASE + 0x00000040)
+#define CIF_ISP_LSC_XSIZE_01 (CIF_ISP_LSC_BASE + 0x00000044)
+#define CIF_ISP_LSC_XSIZE_23 (CIF_ISP_LSC_BASE + 0x00000048)
+#define CIF_ISP_LSC_XSIZE_45 (CIF_ISP_LSC_BASE + 0x0000004C)
+#define CIF_ISP_LSC_XSIZE_67 (CIF_ISP_LSC_BASE + 0x00000050)
+#define CIF_ISP_LSC_YSIZE_01 (CIF_ISP_LSC_BASE + 0x00000054)
+#define CIF_ISP_LSC_YSIZE_23 (CIF_ISP_LSC_BASE + 0x00000058)
+#define CIF_ISP_LSC_YSIZE_45 (CIF_ISP_LSC_BASE + 0x0000005C)
+#define CIF_ISP_LSC_YSIZE_67 (CIF_ISP_LSC_BASE + 0x00000060)
+#define CIF_ISP_LSC_TABLE_SEL (CIF_ISP_LSC_BASE + 0x00000064)
+#define CIF_ISP_LSC_STATUS (CIF_ISP_LSC_BASE + 0x00000068)
+
+#define CIF_ISP_IS_BASE 0x00002300
+#define CIF_ISP_IS_CTRL (CIF_ISP_IS_BASE + 0x00000000)
+#define CIF_ISP_IS_RECENTER (CIF_ISP_IS_BASE + 0x00000004)
+#define CIF_ISP_IS_H_OFFS (CIF_ISP_IS_BASE + 0x00000008)
+#define CIF_ISP_IS_V_OFFS (CIF_ISP_IS_BASE + 0x0000000C)
+#define CIF_ISP_IS_H_SIZE (CIF_ISP_IS_BASE + 0x00000010)
+#define CIF_ISP_IS_V_SIZE (CIF_ISP_IS_BASE + 0x00000014)
+#define CIF_ISP_IS_MAX_DX (CIF_ISP_IS_BASE + 0x00000018)
+#define CIF_ISP_IS_MAX_DY (CIF_ISP_IS_BASE + 0x0000001C)
+#define CIF_ISP_IS_DISPLACE (CIF_ISP_IS_BASE + 0x00000020)
+#define CIF_ISP_IS_H_OFFS_SHD (CIF_ISP_IS_BASE + 0x00000024)
+#define CIF_ISP_IS_V_OFFS_SHD (CIF_ISP_IS_BASE + 0x00000028)
+#define CIF_ISP_IS_H_SIZE_SHD (CIF_ISP_IS_BASE + 0x0000002C)
+#define CIF_ISP_IS_V_SIZE_SHD (CIF_ISP_IS_BASE + 0x00000030)
+
+#define CIF_ISP_HIST_BASE 0x00002400
+
+#define CIF_ISP_HIST_PROP (CIF_ISP_HIST_BASE + 0x00000000)
+#define CIF_ISP_HIST_H_OFFS (CIF_ISP_HIST_BASE + 0x00000004)
+#define CIF_ISP_HIST_V_OFFS (CIF_ISP_HIST_BASE + 0x00000008)
+#define CIF_ISP_HIST_H_SIZE (CIF_ISP_HIST_BASE + 0x0000000C)
+#define CIF_ISP_HIST_V_SIZE (CIF_ISP_HIST_BASE + 0x00000010)
+#define CIF_ISP_HIST_BIN_0 (CIF_ISP_HIST_BASE + 0x00000014)
+#define CIF_ISP_HIST_BIN_1 (CIF_ISP_HIST_BASE + 0x00000018)
+#define CIF_ISP_HIST_BIN_2 (CIF_ISP_HIST_BASE + 0x0000001C)
+#define CIF_ISP_HIST_BIN_3 (CIF_ISP_HIST_BASE + 0x00000020)
+#define CIF_ISP_HIST_BIN_4 (CIF_ISP_HIST_BASE + 0x00000024)
+#define CIF_ISP_HIST_BIN_5 (CIF_ISP_HIST_BASE + 0x00000028)
+#define CIF_ISP_HIST_BIN_6 (CIF_ISP_HIST_BASE + 0x0000002C)
+#define CIF_ISP_HIST_BIN_7 (CIF_ISP_HIST_BASE + 0x00000030)
+#define CIF_ISP_HIST_BIN_8 (CIF_ISP_HIST_BASE + 0x00000034)
+#define CIF_ISP_HIST_BIN_9 (CIF_ISP_HIST_BASE + 0x00000038)
+#define CIF_ISP_HIST_BIN_10 (CIF_ISP_HIST_BASE + 0x0000003C)
+#define CIF_ISP_HIST_BIN_11 (CIF_ISP_HIST_BASE + 0x00000040)
+#define CIF_ISP_HIST_BIN_12 (CIF_ISP_HIST_BASE + 0x00000044)
+#define CIF_ISP_HIST_BIN_13 (CIF_ISP_HIST_BASE + 0x00000048)
+#define CIF_ISP_HIST_BIN_14 (CIF_ISP_HIST_BASE + 0x0000004C)
+#define CIF_ISP_HIST_BIN_15 (CIF_ISP_HIST_BASE + 0x00000050)
+#define CIF_ISP_HIST_WEIGHT_00TO30 (CIF_ISP_HIST_BASE + 0x00000054)
+#define CIF_ISP_HIST_WEIGHT_40TO21 (CIF_ISP_HIST_BASE + 0x00000058)
+#define CIF_ISP_HIST_WEIGHT_31TO12 (CIF_ISP_HIST_BASE + 0x0000005C)
+#define CIF_ISP_HIST_WEIGHT_22TO03 (CIF_ISP_HIST_BASE + 0x00000060)
+#define CIF_ISP_HIST_WEIGHT_13TO43 (CIF_ISP_HIST_BASE + 0x00000064)
+#define CIF_ISP_HIST_WEIGHT_04TO34 (CIF_ISP_HIST_BASE + 0x00000068)
+#define CIF_ISP_HIST_WEIGHT_44 (CIF_ISP_HIST_BASE + 0x0000006C)
+
+#define CIF_ISP_FILT_BASE 0x00002500
+#define CIF_ISP_FILT_MODE (CIF_ISP_FILT_BASE + 0x00000000)
+#define CIF_ISP_FILT_THRESH_BL0 (CIF_ISP_FILT_BASE + 0x00000028)
+#define CIF_ISP_FILT_THRESH_BL1 (CIF_ISP_FILT_BASE + 0x0000002c)
+#define CIF_ISP_FILT_THRESH_SH0 (CIF_ISP_FILT_BASE + 0x00000030)
+#define CIF_ISP_FILT_THRESH_SH1 (CIF_ISP_FILT_BASE + 0x00000034)
+#define CIF_ISP_FILT_LUM_WEIGHT (CIF_ISP_FILT_BASE + 0x00000038)
+#define CIF_ISP_FILT_FAC_SH1 (CIF_ISP_FILT_BASE + 0x0000003c)
+#define CIF_ISP_FILT_FAC_SH0 (CIF_ISP_FILT_BASE + 0x00000040)
+#define CIF_ISP_FILT_FAC_MID (CIF_ISP_FILT_BASE + 0x00000044)
+#define CIF_ISP_FILT_FAC_BL0 (CIF_ISP_FILT_BASE + 0x00000048)
+#define CIF_ISP_FILT_FAC_BL1 (CIF_ISP_FILT_BASE + 0x0000004C)
+
+#define CIF_ISP_CAC_BASE 0x00002580
+#define CIF_ISP_CAC_CTRL (CIF_ISP_CAC_BASE + 0x00000000)
+#define CIF_ISP_CAC_COUNT_START (CIF_ISP_CAC_BASE + 0x00000004)
+#define CIF_ISP_CAC_A (CIF_ISP_CAC_BASE + 0x00000008)
+#define CIF_ISP_CAC_B (CIF_ISP_CAC_BASE + 0x0000000C)
+#define CIF_ISP_CAC_C (CIF_ISP_CAC_BASE + 0x00000010)
+#define CIF_ISP_X_NORM (CIF_ISP_CAC_BASE + 0x00000014)
+#define CIF_ISP_Y_NORM (CIF_ISP_CAC_BASE + 0x00000018)
+
+#define CIF_ISP_EXP_BASE 0x00002600
+#define CIF_ISP_EXP_CTRL (CIF_ISP_EXP_BASE + 0x00000000)
+#define CIF_ISP_EXP_H_OFFSET (CIF_ISP_EXP_BASE + 0x00000004)
+#define CIF_ISP_EXP_V_OFFSET (CIF_ISP_EXP_BASE + 0x00000008)
+#define CIF_ISP_EXP_H_SIZE (CIF_ISP_EXP_BASE + 0x0000000C)
+#define CIF_ISP_EXP_V_SIZE (CIF_ISP_EXP_BASE + 0x00000010)
+#define CIF_ISP_EXP_MEAN_00 (CIF_ISP_EXP_BASE + 0x00000014)
+#define CIF_ISP_EXP_MEAN_10 (CIF_ISP_EXP_BASE + 0x00000018)
+#define CIF_ISP_EXP_MEAN_20 (CIF_ISP_EXP_BASE + 0x0000001c)
+#define CIF_ISP_EXP_MEAN_30 (CIF_ISP_EXP_BASE + 0x00000020)
+#define CIF_ISP_EXP_MEAN_40 (CIF_ISP_EXP_BASE + 0x00000024)
+#define CIF_ISP_EXP_MEAN_01 (CIF_ISP_EXP_BASE + 0x00000028)
+#define CIF_ISP_EXP_MEAN_11 (CIF_ISP_EXP_BASE + 0x0000002c)
+#define CIF_ISP_EXP_MEAN_21 (CIF_ISP_EXP_BASE + 0x00000030)
+#define CIF_ISP_EXP_MEAN_31 (CIF_ISP_EXP_BASE + 0x00000034)
+#define CIF_ISP_EXP_MEAN_41 (CIF_ISP_EXP_BASE + 0x00000038)
+#define CIF_ISP_EXP_MEAN_02 (CIF_ISP_EXP_BASE + 0x0000003c)
+#define CIF_ISP_EXP_MEAN_12 (CIF_ISP_EXP_BASE + 0x00000040)
+#define CIF_ISP_EXP_MEAN_22 (CIF_ISP_EXP_BASE + 0x00000044)
+#define CIF_ISP_EXP_MEAN_32 (CIF_ISP_EXP_BASE + 0x00000048)
+#define CIF_ISP_EXP_MEAN_42 (CIF_ISP_EXP_BASE + 0x0000004c)
+#define CIF_ISP_EXP_MEAN_03 (CIF_ISP_EXP_BASE + 0x00000050)
+#define CIF_ISP_EXP_MEAN_13 (CIF_ISP_EXP_BASE + 0x00000054)
+#define CIF_ISP_EXP_MEAN_23 (CIF_ISP_EXP_BASE + 0x00000058)
+#define CIF_ISP_EXP_MEAN_33 (CIF_ISP_EXP_BASE + 0x0000005c)
+#define CIF_ISP_EXP_MEAN_43 (CIF_ISP_EXP_BASE + 0x00000060)
+#define CIF_ISP_EXP_MEAN_04 (CIF_ISP_EXP_BASE + 0x00000064)
+#define CIF_ISP_EXP_MEAN_14 (CIF_ISP_EXP_BASE + 0x00000068)
+#define CIF_ISP_EXP_MEAN_24 (CIF_ISP_EXP_BASE + 0x0000006c)
+#define CIF_ISP_EXP_MEAN_34 (CIF_ISP_EXP_BASE + 0x00000070)
+#define CIF_ISP_EXP_MEAN_44 (CIF_ISP_EXP_BASE + 0x00000074)
+
+#define CIF_ISP_BLS_BASE 0x00002700
+#define CIF_ISP_BLS_CTRL (CIF_ISP_BLS_BASE + 0x00000000)
+#define CIF_ISP_BLS_SAMPLES (CIF_ISP_BLS_BASE + 0x00000004)
+#define CIF_ISP_BLS_H1_START (CIF_ISP_BLS_BASE + 0x00000008)
+#define CIF_ISP_BLS_H1_STOP (CIF_ISP_BLS_BASE + 0x0000000c)
+#define CIF_ISP_BLS_V1_START (CIF_ISP_BLS_BASE + 0x00000010)
+#define CIF_ISP_BLS_V1_STOP (CIF_ISP_BLS_BASE + 0x00000014)
+#define CIF_ISP_BLS_H2_START (CIF_ISP_BLS_BASE + 0x00000018)
+#define CIF_ISP_BLS_H2_STOP (CIF_ISP_BLS_BASE + 0x0000001c)
+#define CIF_ISP_BLS_V2_START (CIF_ISP_BLS_BASE + 0x00000020)
+#define CIF_ISP_BLS_V2_STOP (CIF_ISP_BLS_BASE + 0x00000024)
+#define CIF_ISP_BLS_A_FIXED (CIF_ISP_BLS_BASE + 0x00000028)
+#define CIF_ISP_BLS_B_FIXED (CIF_ISP_BLS_BASE + 0x0000002c)
+#define CIF_ISP_BLS_C_FIXED (CIF_ISP_BLS_BASE + 0x00000030)
+#define CIF_ISP_BLS_D_FIXED (CIF_ISP_BLS_BASE + 0x00000034)
+#define CIF_ISP_BLS_A_MEASURED (CIF_ISP_BLS_BASE + 0x00000038)
+#define CIF_ISP_BLS_B_MEASURED (CIF_ISP_BLS_BASE + 0x0000003c)
+#define CIF_ISP_BLS_C_MEASURED (CIF_ISP_BLS_BASE + 0x00000040)
+#define CIF_ISP_BLS_D_MEASURED (CIF_ISP_BLS_BASE + 0x00000044)
+
+#define CIF_ISP_DPF_BASE 0x00002800
+#define CIF_ISP_DPF_MODE (CIF_ISP_DPF_BASE + 0x00000000)
+#define CIF_ISP_DPF_STRENGTH_R (CIF_ISP_DPF_BASE + 0x00000004)
+#define CIF_ISP_DPF_STRENGTH_G (CIF_ISP_DPF_BASE + 0x00000008)
+#define CIF_ISP_DPF_STRENGTH_B (CIF_ISP_DPF_BASE + 0x0000000C)
+#define CIF_ISP_DPF_S_WEIGHT_G_1_4 (CIF_ISP_DPF_BASE + 0x00000010)
+#define CIF_ISP_DPF_S_WEIGHT_G_5_6 (CIF_ISP_DPF_BASE + 0x00000014)
+#define CIF_ISP_DPF_S_WEIGHT_RB_1_4 (CIF_ISP_DPF_BASE + 0x00000018)
+#define CIF_ISP_DPF_S_WEIGHT_RB_5_6 (CIF_ISP_DPF_BASE + 0x0000001C)
+#define CIF_ISP_DPF_NULL_COEFF_0 (CIF_ISP_DPF_BASE + 0x00000020)
+#define CIF_ISP_DPF_NULL_COEFF_1 (CIF_ISP_DPF_BASE + 0x00000024)
+#define CIF_ISP_DPF_NULL_COEFF_2 (CIF_ISP_DPF_BASE + 0x00000028)
+#define CIF_ISP_DPF_NULL_COEFF_3 (CIF_ISP_DPF_BASE + 0x0000002C)
+#define CIF_ISP_DPF_NULL_COEFF_4 (CIF_ISP_DPF_BASE + 0x00000030)
+#define CIF_ISP_DPF_NULL_COEFF_5 (CIF_ISP_DPF_BASE + 0x00000034)
+#define CIF_ISP_DPF_NULL_COEFF_6 (CIF_ISP_DPF_BASE + 0x00000038)
+#define CIF_ISP_DPF_NULL_COEFF_7 (CIF_ISP_DPF_BASE + 0x0000003C)
+#define CIF_ISP_DPF_NULL_COEFF_8 (CIF_ISP_DPF_BASE + 0x00000040)
+#define CIF_ISP_DPF_NULL_COEFF_9 (CIF_ISP_DPF_BASE + 0x00000044)
+#define CIF_ISP_DPF_NULL_COEFF_10 (CIF_ISP_DPF_BASE + 0x00000048)
+#define CIF_ISP_DPF_NULL_COEFF_11 (CIF_ISP_DPF_BASE + 0x0000004C)
+#define CIF_ISP_DPF_NULL_COEFF_12 (CIF_ISP_DPF_BASE + 0x00000050)
+#define CIF_ISP_DPF_NULL_COEFF_13 (CIF_ISP_DPF_BASE + 0x00000054)
+#define CIF_ISP_DPF_NULL_COEFF_14 (CIF_ISP_DPF_BASE + 0x00000058)
+#define CIF_ISP_DPF_NULL_COEFF_15 (CIF_ISP_DPF_BASE + 0x0000005C)
+#define CIF_ISP_DPF_NULL_COEFF_16 (CIF_ISP_DPF_BASE + 0x00000060)
+#define CIF_ISP_DPF_NF_GAIN_R (CIF_ISP_DPF_BASE + 0x00000064)
+#define CIF_ISP_DPF_NF_GAIN_GR (CIF_ISP_DPF_BASE + 0x00000068)
+#define CIF_ISP_DPF_NF_GAIN_GB (CIF_ISP_DPF_BASE + 0x0000006C)
+#define CIF_ISP_DPF_NF_GAIN_B (CIF_ISP_DPF_BASE + 0x00000070)
+
+#define CIF_ISP_DPCC_BASE 0x00002900
+#define CIF_ISP_DPCC_MODE (CIF_ISP_DPCC_BASE + 0x00000000)
+#define CIF_ISP_DPCC_OUTPUT_MODE (CIF_ISP_DPCC_BASE + 0x00000004)
+#define CIF_ISP_DPCC_SET_USE (CIF_ISP_DPCC_BASE + 0x00000008)
+#define CIF_ISP_DPCC_METHODS_SET_1 (CIF_ISP_DPCC_BASE + 0x0000000C)
+#define CIF_ISP_DPCC_METHODS_SET_2 (CIF_ISP_DPCC_BASE + 0x00000010)
+#define CIF_ISP_DPCC_METHODS_SET_3 (CIF_ISP_DPCC_BASE + 0x00000014)
+#define CIF_ISP_DPCC_LINE_THRESH_1 (CIF_ISP_DPCC_BASE + 0x00000018)
+#define CIF_ISP_DPCC_LINE_MAD_FAC_1 (CIF_ISP_DPCC_BASE + 0x0000001C)
+#define CIF_ISP_DPCC_PG_FAC_1 (CIF_ISP_DPCC_BASE + 0x00000020)
+#define CIF_ISP_DPCC_RND_THRESH_1 (CIF_ISP_DPCC_BASE + 0x00000024)
+#define CIF_ISP_DPCC_RG_FAC_1 (CIF_ISP_DPCC_BASE + 0x00000028)
+#define CIF_ISP_DPCC_LINE_THRESH_2 (CIF_ISP_DPCC_BASE + 0x0000002C)
+#define CIF_ISP_DPCC_LINE_MAD_FAC_2 (CIF_ISP_DPCC_BASE + 0x00000030)
+#define CIF_ISP_DPCC_PG_FAC_2 (CIF_ISP_DPCC_BASE + 0x00000034)
+#define CIF_ISP_DPCC_RND_THRESH_2 (CIF_ISP_DPCC_BASE + 0x00000038)
+#define CIF_ISP_DPCC_RG_FAC_2 (CIF_ISP_DPCC_BASE + 0x0000003C)
+#define CIF_ISP_DPCC_LINE_THRESH_3 (CIF_ISP_DPCC_BASE + 0x00000040)
+#define CIF_ISP_DPCC_LINE_MAD_FAC_3 (CIF_ISP_DPCC_BASE + 0x00000044)
+#define CIF_ISP_DPCC_PG_FAC_3 (CIF_ISP_DPCC_BASE + 0x00000048)
+#define CIF_ISP_DPCC_RND_THRESH_3 (CIF_ISP_DPCC_BASE + 0x0000004C)
+#define CIF_ISP_DPCC_RG_FAC_3 (CIF_ISP_DPCC_BASE + 0x00000050)
+#define CIF_ISP_DPCC_RO_LIMITS (CIF_ISP_DPCC_BASE + 0x00000054)
+#define CIF_ISP_DPCC_RND_OFFS (CIF_ISP_DPCC_BASE + 0x00000058)
+#define CIF_ISP_DPCC_BPT_CTRL (CIF_ISP_DPCC_BASE + 0x0000005C)
+#define CIF_ISP_DPCC_BPT_NUMBER (CIF_ISP_DPCC_BASE + 0x00000060)
+#define CIF_ISP_DPCC_BPT_ADDR (CIF_ISP_DPCC_BASE + 0x00000064)
+#define CIF_ISP_DPCC_BPT_DATA (CIF_ISP_DPCC_BASE + 0x00000068)
+
+#define CIF_ISP_WDR_BASE 0x00002A00
+#define CIF_ISP_WDR_CTRL (CIF_ISP_WDR_BASE + 0x00000000)
+#define CIF_ISP_WDR_TONECURVE_1 (CIF_ISP_WDR_BASE + 0x00000004)
+#define CIF_ISP_WDR_TONECURVE_2 (CIF_ISP_WDR_BASE + 0x00000008)
+#define CIF_ISP_WDR_TONECURVE_3 (CIF_ISP_WDR_BASE + 0x0000000C)
+#define CIF_ISP_WDR_TONECURVE_4 (CIF_ISP_WDR_BASE + 0x00000010)
+#define CIF_ISP_WDR_TONECURVE_YM_0 (CIF_ISP_WDR_BASE + 0x00000014)
+#define CIF_ISP_WDR_TONECURVE_YM_1 (CIF_ISP_WDR_BASE + 0x00000018)
+#define CIF_ISP_WDR_TONECURVE_YM_2 (CIF_ISP_WDR_BASE + 0x0000001C)
+#define CIF_ISP_WDR_TONECURVE_YM_3 (CIF_ISP_WDR_BASE + 0x00000020)
+#define CIF_ISP_WDR_TONECURVE_YM_4 (CIF_ISP_WDR_BASE + 0x00000024)
+#define CIF_ISP_WDR_TONECURVE_YM_5 (CIF_ISP_WDR_BASE + 0x00000028)
+#define CIF_ISP_WDR_TONECURVE_YM_6 (CIF_ISP_WDR_BASE + 0x0000002C)
+#define CIF_ISP_WDR_TONECURVE_YM_7 (CIF_ISP_WDR_BASE + 0x00000030)
+#define CIF_ISP_WDR_TONECURVE_YM_8 (CIF_ISP_WDR_BASE + 0x00000034)
+#define CIF_ISP_WDR_TONECURVE_YM_9 (CIF_ISP_WDR_BASE + 0x00000038)
+#define CIF_ISP_WDR_TONECURVE_YM_10 (CIF_ISP_WDR_BASE + 0x0000003C)
+#define CIF_ISP_WDR_TONECURVE_YM_11 (CIF_ISP_WDR_BASE + 0x00000040)
+#define CIF_ISP_WDR_TONECURVE_YM_12 (CIF_ISP_WDR_BASE + 0x00000044)
+#define CIF_ISP_WDR_TONECURVE_YM_13 (CIF_ISP_WDR_BASE + 0x00000048)
+#define CIF_ISP_WDR_TONECURVE_YM_14 (CIF_ISP_WDR_BASE + 0x0000004C)
+#define CIF_ISP_WDR_TONECURVE_YM_15 (CIF_ISP_WDR_BASE + 0x00000050)
+#define CIF_ISP_WDR_TONECURVE_YM_16 (CIF_ISP_WDR_BASE + 0x00000054)
+#define CIF_ISP_WDR_TONECURVE_YM_17 (CIF_ISP_WDR_BASE + 0x00000058)
+#define CIF_ISP_WDR_TONECURVE_YM_18 (CIF_ISP_WDR_BASE + 0x0000005C)
+#define CIF_ISP_WDR_TONECURVE_YM_19 (CIF_ISP_WDR_BASE + 0x00000060)
+#define CIF_ISP_WDR_TONECURVE_YM_20 (CIF_ISP_WDR_BASE + 0x00000064)
+#define CIF_ISP_WDR_TONECURVE_YM_21 (CIF_ISP_WDR_BASE + 0x00000068)
+#define CIF_ISP_WDR_TONECURVE_YM_22 (CIF_ISP_WDR_BASE + 0x0000006C)
+#define CIF_ISP_WDR_TONECURVE_YM_23 (CIF_ISP_WDR_BASE + 0x00000070)
+#define CIF_ISP_WDR_TONECURVE_YM_24 (CIF_ISP_WDR_BASE + 0x00000074)
+#define CIF_ISP_WDR_TONECURVE_YM_25 (CIF_ISP_WDR_BASE + 0x00000078)
+#define CIF_ISP_WDR_TONECURVE_YM_26 (CIF_ISP_WDR_BASE + 0x0000007C)
+#define CIF_ISP_WDR_TONECURVE_YM_27 (CIF_ISP_WDR_BASE + 0x00000080)
+#define CIF_ISP_WDR_TONECURVE_YM_28 (CIF_ISP_WDR_BASE + 0x00000084)
+#define CIF_ISP_WDR_TONECURVE_YM_29 (CIF_ISP_WDR_BASE + 0x00000088)
+#define CIF_ISP_WDR_TONECURVE_YM_30 (CIF_ISP_WDR_BASE + 0x0000008C)
+#define CIF_ISP_WDR_TONECURVE_YM_31 (CIF_ISP_WDR_BASE + 0x00000090)
+#define CIF_ISP_WDR_TONECURVE_YM_32 (CIF_ISP_WDR_BASE + 0x00000094)
+#define CIF_ISP_WDR_OFFSET (CIF_ISP_WDR_BASE + 0x00000098)
+#define CIF_ISP_WDR_DELTAMIN (CIF_ISP_WDR_BASE + 0x0000009C)
+#define CIF_ISP_WDR_TONECURVE_1_SHD (CIF_ISP_WDR_BASE + 0x000000A0)
+#define CIF_ISP_WDR_TONECURVE_2_SHD (CIF_ISP_WDR_BASE + 0x000000A4)
+#define CIF_ISP_WDR_TONECURVE_3_SHD (CIF_ISP_WDR_BASE + 0x000000A8)
+#define CIF_ISP_WDR_TONECURVE_4_SHD (CIF_ISP_WDR_BASE + 0x000000AC)
+#define CIF_ISP_WDR_TONECURVE_YM_0_SHD (CIF_ISP_WDR_BASE + 0x000000B0)
+#define CIF_ISP_WDR_TONECURVE_YM_1_SHD (CIF_ISP_WDR_BASE + 0x000000B4)
+#define CIF_ISP_WDR_TONECURVE_YM_2_SHD (CIF_ISP_WDR_BASE + 0x000000B8)
+#define CIF_ISP_WDR_TONECURVE_YM_3_SHD (CIF_ISP_WDR_BASE + 0x000000BC)
+#define CIF_ISP_WDR_TONECURVE_YM_4_SHD (CIF_ISP_WDR_BASE + 0x000000C0)
+#define CIF_ISP_WDR_TONECURVE_YM_5_SHD (CIF_ISP_WDR_BASE + 0x000000C4)
+#define CIF_ISP_WDR_TONECURVE_YM_6_SHD (CIF_ISP_WDR_BASE + 0x000000C8)
+#define CIF_ISP_WDR_TONECURVE_YM_7_SHD (CIF_ISP_WDR_BASE + 0x000000CC)
+#define CIF_ISP_WDR_TONECURVE_YM_8_SHD (CIF_ISP_WDR_BASE + 0x000000D0)
+#define CIF_ISP_WDR_TONECURVE_YM_9_SHD (CIF_ISP_WDR_BASE + 0x000000D4)
+#define CIF_ISP_WDR_TONECURVE_YM_10_SHD (CIF_ISP_WDR_BASE + 0x000000D8)
+#define CIF_ISP_WDR_TONECURVE_YM_11_SHD (CIF_ISP_WDR_BASE + 0x000000DC)
+#define CIF_ISP_WDR_TONECURVE_YM_12_SHD (CIF_ISP_WDR_BASE + 0x000000E0)
+#define CIF_ISP_WDR_TONECURVE_YM_13_SHD (CIF_ISP_WDR_BASE + 0x000000E4)
+#define CIF_ISP_WDR_TONECURVE_YM_14_SHD (CIF_ISP_WDR_BASE + 0x000000E8)
+#define CIF_ISP_WDR_TONECURVE_YM_15_SHD (CIF_ISP_WDR_BASE + 0x000000EC)
+#define CIF_ISP_WDR_TONECURVE_YM_16_SHD (CIF_ISP_WDR_BASE + 0x000000F0)
+#define CIF_ISP_WDR_TONECURVE_YM_17_SHD (CIF_ISP_WDR_BASE + 0x000000F4)
+#define CIF_ISP_WDR_TONECURVE_YM_18_SHD (CIF_ISP_WDR_BASE + 0x000000F8)
+#define CIF_ISP_WDR_TONECURVE_YM_19_SHD (CIF_ISP_WDR_BASE + 0x000000FC)
+#define CIF_ISP_WDR_TONECURVE_YM_20_SHD (CIF_ISP_WDR_BASE + 0x00000100)
+#define CIF_ISP_WDR_TONECURVE_YM_21_SHD (CIF_ISP_WDR_BASE + 0x00000104)
+#define CIF_ISP_WDR_TONECURVE_YM_22_SHD (CIF_ISP_WDR_BASE + 0x00000108)
+#define CIF_ISP_WDR_TONECURVE_YM_23_SHD (CIF_ISP_WDR_BASE + 0x0000010C)
+#define CIF_ISP_WDR_TONECURVE_YM_24_SHD (CIF_ISP_WDR_BASE + 0x00000110)
+#define CIF_ISP_WDR_TONECURVE_YM_25_SHD (CIF_ISP_WDR_BASE + 0x00000114)
+#define CIF_ISP_WDR_TONECURVE_YM_26_SHD (CIF_ISP_WDR_BASE + 0x00000118)
+#define CIF_ISP_WDR_TONECURVE_YM_27_SHD (CIF_ISP_WDR_BASE + 0x0000011C)
+#define CIF_ISP_WDR_TONECURVE_YM_28_SHD (CIF_ISP_WDR_BASE + 0x00000120)
+#define CIF_ISP_WDR_TONECURVE_YM_29_SHD (CIF_ISP_WDR_BASE + 0x00000124)
+#define CIF_ISP_WDR_TONECURVE_YM_30_SHD (CIF_ISP_WDR_BASE + 0x00000128)
+#define CIF_ISP_WDR_TONECURVE_YM_31_SHD (CIF_ISP_WDR_BASE + 0x0000012C)
+#define CIF_ISP_WDR_TONECURVE_YM_32_SHD (CIF_ISP_WDR_BASE + 0x00000130)
+
+#define CIF_ISP_VSM_BASE 0x00002F00
+#define CIF_ISP_VSM_MODE (CIF_ISP_VSM_BASE + 0x00000000)
+#define CIF_ISP_VSM_H_OFFS (CIF_ISP_VSM_BASE + 0x00000004)
+#define CIF_ISP_VSM_V_OFFS (CIF_ISP_VSM_BASE + 0x00000008)
+#define CIF_ISP_VSM_H_SIZE (CIF_ISP_VSM_BASE + 0x0000000C)
+#define CIF_ISP_VSM_V_SIZE (CIF_ISP_VSM_BASE + 0x00000010)
+#define CIF_ISP_VSM_H_SEGMENTS (CIF_ISP_VSM_BASE + 0x00000014)
+#define CIF_ISP_VSM_V_SEGMENTS (CIF_ISP_VSM_BASE + 0x00000018)
+#define CIF_ISP_VSM_DELTA_H (CIF_ISP_VSM_BASE + 0x0000001C)
+#define CIF_ISP_VSM_DELTA_V (CIF_ISP_VSM_BASE + 0x00000020)
+
+void disable_dcrop(struct rkisp1_stream *stream, bool async);
+void config_dcrop(struct rkisp1_stream *stream, struct v4l2_rect *rect,
+ bool async);
+
+void dump_rsz_regs(struct rkisp1_stream *stream);
+void disable_rsz(struct rkisp1_stream *stream, bool async);
+void config_rsz(struct rkisp1_stream *stream, struct v4l2_rect *in_y,
+ struct v4l2_rect *in_c, struct v4l2_rect *out_y,
+ struct v4l2_rect *out_c, bool async);
+
+void config_mi_ctrl(struct rkisp1_stream *stream);
+
+void mp_clr_frame_end_int(void __iomem *base);
+void sp_clr_frame_end_int(void __iomem *base);
+
+bool mp_is_frame_end_int_masked(void __iomem *base);
+bool sp_is_frame_end_int_masked(void __iomem *base);
+bool mp_is_stream_stopped(void __iomem *base);
+bool sp_is_stream_stopped(void __iomem *base);
+
+static inline void mi_set_y_size(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.y_size_init);
+}
+
+static inline void mi_set_cb_size(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cb_size_init);
+}
+
+static inline void mi_set_cr_size(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cr_size_init);
+}
+
+static inline void mi_set_y_addr(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.y_base_ad_init);
+}
+
+static inline void mi_set_cb_addr(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cb_base_ad_init);
+}
+
+static inline void mi_set_cr_addr(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cr_base_ad_init);
+}
+
+static inline void mi_set_y_offset(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.y_offs_cnt_init);
+}
+
+static inline void mi_set_cb_offset(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cb_offs_cnt_init);
+}
+
+static inline void mi_set_cr_offset(struct rkisp1_stream *stream, int val)
+{
+ void __iomem *base = stream->ispdev->base_addr;
+
+ writel(val, base + stream->config->mi.cr_offs_cnt_init);
+}
+
+static inline void mp_set_chain_mode(void __iomem *base)
+{
+ u32 dpcl = readl(base + CIF_VI_DPCL);
+
+ dpcl |= CIF_VI_DPCL_CHAN_MODE_MP;
+ writel(dpcl, base + CIF_VI_DPCL);
+}
+
+static inline void sp_set_chain_mode(void __iomem *base)
+{
+ u32 dpcl = readl(base + CIF_VI_DPCL);
+
+ dpcl |= CIF_VI_DPCL_CHAN_MODE_SP;
+ writel(dpcl, base + CIF_VI_DPCL);
+}
+
+static inline void mp_set_data_path(void __iomem *base)
+{
+ u32 dpcl = readl(base + CIF_VI_DPCL);
+
+ dpcl = dpcl | CIF_VI_DPCL_CHAN_MODE_MP | CIF_VI_DPCL_MP_MUX_MRSZ_MI;
+ writel(dpcl, base + CIF_VI_DPCL);
+}
+
+static inline void sp_set_data_path(void __iomem *base)
+{
+ u32 dpcl = readl(base + CIF_VI_DPCL);
+
+ dpcl |= CIF_VI_DPCL_CHAN_MODE_SP;
+ writel(dpcl, base + CIF_VI_DPCL);
+}
+
+static inline void mp_frame_end_int_enable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_IMSC;
+
+ writel(CIF_MI_MP_FRAME | readl(addr), addr);
+}
+
+static inline void sp_frame_end_int_enable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_IMSC;
+
+ writel(CIF_MI_SP_FRAME | readl(addr), addr);
+}
+
+static inline void mp_frame_end_int_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_IMSC;
+
+ writel(~CIF_MI_MP_FRAME & readl(addr), addr);
+}
+
+static inline void sp_frame_end_int_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_IMSC;
+
+ writel(~CIF_MI_SP_FRAME & readl(addr), addr);
+}
+
+static inline void clr_mpsp_frame_end_int(void __iomem *base)
+{
+ writel(CIF_MI_SP_FRAME | CIF_MI_MP_FRAME, base + CIF_MI_ICR);
+}
+
+static inline void clr_mp_crop_rsz_int(void __iomem *base)
+{
+ writel(~CIF_MI_MP_FRAME, base + CIF_MI_ICR);
+}
+
+static inline void clr_sp_crop_rsz_int(void __iomem *base)
+{
+ writel(~CIF_MI_SP_FRAME, base + CIF_MI_ICR);
+}
+
+static inline void mp_set_uv_swap(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_XTD_FORMAT_CTRL;
+ u32 reg = readl(addr) & ~BIT(0);
+
+ writel(reg | CIF_MI_XTD_FMT_CTRL_MP_CB_CR_SWAP, addr);
+}
+
+static inline void sp_set_uv_swap(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_XTD_FORMAT_CTRL;
+ u32 reg = readl(addr) & ~BIT(1);
+
+ writel(reg | CIF_MI_XTD_FMT_CTRL_SP_CB_CR_SWAP, addr);
+}
+
+static inline void sp_set_y_width(void __iomem *base, u32 val)
+{
+ writel(val, base + CIF_MI_SP_Y_PIC_WIDTH);
+}
+
+static inline void sp_set_y_height(void __iomem *base, u32 val)
+{
+ writel(val, base + CIF_MI_SP_Y_PIC_HEIGHT);
+}
+
+static inline void sp_set_y_line_length(void __iomem *base, u32 val)
+{
+ writel(val, base + CIF_MI_SP_Y_LLENGTH);
+}
+
+static inline void mp_mi_ctrl_set_format(void __iomem *base, u32 val)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+ u32 reg = readl(addr) & ~MI_CTRL_MP_FMT_MASK;
+
+ writel(reg | val, addr);
+}
+
+static inline void sp_mi_ctrl_set_format(void __iomem *base, u32 val)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+ u32 reg = readl(addr) & ~MI_CTRL_SP_FMT_MASK;
+
+ writel(reg | val, addr);
+}
+
+static inline void mi_ctrl_mpyuv_enable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(CIF_MI_CTRL_MP_ENABLE | readl(addr), addr);
+}
+
+static inline void mi_ctrl_mpyuv_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(~CIF_MI_CTRL_MP_ENABLE & readl(addr), addr);
+}
+
+static inline void mi_ctrl_mp_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(~(CIF_MI_CTRL_MP_ENABLE | CIF_MI_CTRL_RAW_ENABLE) & readl(addr),
+ addr);
+}
+
+static inline void mi_ctrl_spyuv_enable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(CIF_MI_CTRL_SP_ENABLE | readl(addr), addr);
+}
+
+static inline void mi_ctrl_spyuv_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(~CIF_MI_CTRL_SP_ENABLE & readl(addr), addr);
+}
+
+static inline void mi_ctrl_sp_disable(void __iomem *base)
+{
+ mi_ctrl_spyuv_disable(base);
+}
+
+static inline void mi_ctrl_mpraw_enable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(CIF_MI_CTRL_RAW_ENABLE | readl(addr), addr);
+}
+
+static inline void mi_ctrl_mpraw_disable(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(~CIF_MI_CTRL_RAW_ENABLE & readl(addr), addr);
+}
+
+static inline void mp_mi_ctrl_autoupdate_en(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(readl(addr) | CIF_MI_MP_AUTOUPDATE_ENABLE, addr);
+}
+
+static inline void sp_mi_ctrl_autoupdate_en(void __iomem *base)
+{
+ void __iomem *addr = base + CIF_MI_CTRL;
+
+ writel(readl(addr) | CIF_MI_SP_AUTOUPDATE_ENABLE, addr);
+}
+
+static inline void force_cfg_update(void __iomem *base)
+{
+ writel(CIF_MI_INIT_SOFT_UPD, base + CIF_MI_INIT);
+}
+
+static inline u32 mi_get_masked_int_status(void __iomem *base)
+{
+ return readl(base + CIF_MI_MIS);
+}
+
+#endif /* _RKISP1_REGS_H */
--
2.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v6 08/17] media: rkisp1: add capture device driver
2018-03-08 9:47 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Jacob Chen
@ 2018-03-09 14:07 ` Hans Verkuil
2018-04-17 8:44 ` Tomasz Figa
1 sibling, 0 replies; 51+ messages in thread
From: Hans Verkuil @ 2018-03-09 14:07 UTC (permalink / raw)
To: Jacob Chen, linux-rockchip
Cc: linux-kernel, linux-arm-kernel, mchehab, linux-media,
sakari.ailus, hans.verkuil, tfiga, zhengsq, laurent.pinchart,
zyc, eddie.cai.linux, jeffy.chen, devicetree, heiko, Jacob Chen,
Jacob Chen, Allon Huang
On 08/03/18 10:47, Jacob Chen wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
>
> This is the capture device interface driver that provides the v4l2
> user interface. Frames can be received from 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>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Regards,
Hans
^ permalink raw reply [flat|nested] 51+ messages in thread
* Re: [PATCH v6 08/17] media: rkisp1: add capture device driver
2018-03-08 9:47 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Jacob Chen
2018-03-09 14:07 ` Hans Verkuil
@ 2018-04-17 8:44 ` Tomasz Figa
1 sibling, 0 replies; 51+ messages in thread
From: Tomasz Figa @ 2018-04-17 8:44 UTC (permalink / raw)
To: Jacob Chen
Cc: open list:ARM/Rockchip SoC...,
Linux Kernel Mailing List,
list@263.net:IOMMU DRIVERS
<iommu@lists.linux-foundation.org>,
Joerg Roedel <joro@8bytes.org>,,
Mauro Carvalho Chehab, Linux Media Mailing List, Sakari Ailus,
Hans Verkuil, Shunqian Zheng, Laurent Pinchart,
钟以崇,
Eddie Cai, Jeffy, devicetree, Heiko Stübner, Chen Jacob
Hi Jacob,
On Thu, Mar 8, 2018 at 6:49 PM Jacob Chen <jacob-chen@iotwrt.com> wrote:
> From: Jacob Chen <jacob2.chen@rock-chips.com>
> This is the capture device interface driver that provides the v4l2
> user interface. Frames can be received from ISP1.
Thanks for the patch. Please find my comment inline.
[snip]
> +static int
> +rkisp1_start_streaming(struct vb2_queue *queue, unsigned int count)
> +{
> + struct rkisp1_stream *stream = queue->drv_priv;
> + struct rkisp1_vdev_node *node = &stream->vnode;
> + struct rkisp1_device *dev = stream->ispdev;
> + struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
> + int ret;
> +
> + if (WARN_ON(stream->state != RKISP1_STATE_READY))
> + goto return_queued_buf;
We jump out with ret unitialized here. For reference, it triggers a
compiler warning for me.
Note that rather than initializing ret at its definition, I'd recommend
adding an assignment before the goto statement. This will still let the
compiler issue warnings, without assuming that the default value is correct.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread
* [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
` (2 preceding siblings ...)
2018-03-08 9:47 ` [PATCH v6 08/17] media: rkisp1: add capture device driver Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy for rk3399 Jacob Chen
` (2 subsequent siblings)
6 siblings, 0 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Add DT bindings documentation for Rockchip MIPI D-PHY RX
Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Reviewed-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
.../bindings/media/rockchip-mipi-dphy.txt | 90 ++++++++++++++++++++++
1 file changed, 90 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..d83700faf4c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/rockchip-mipi-dphy.txt
@@ -0,0 +1,90 @@
+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 RX 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.
+- rockchip,grf: MIPI TX1RX1 D-PHY not only has its own register but also
+ the GRF, so it is only necessary for MIPI TX1RX1 D-PHY.
+
+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
+activated 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@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@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ mipi_in_wcam: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&wcam_out>;
+ data-lanes = <1 2>;
+ };
+ mipi_in_ucam: endpoint@1 {
+ reg = <1>;
+ remote-endpoint = <&ucam_out>;
+ data-lanes = <1>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ dphy_rx0_out: endpoint {
+ remote-endpoint = <&isp0_mipi_in>;
+ };
+ };
+ };
+ };
+};
--
2.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy for rk3399
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
` (3 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 11/17] dt-bindings: Document the Rockchip MIPI RX D-PHY bindings Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 9:48 ` [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
2018-03-08 10:29 ` [PATCH v6 00/17] Rockchip ISP1 Driver Tomasz Figa
6 siblings, 0 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
It's a Designware MIPI D-PHY, used for ISP0 in rk3399.
Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
Signed-off-by: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
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 14ed016f2fdd..d18ab8f56ef2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -1308,6 +1308,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@e450 {
compatible = "rockchip,rk3399-usb2phy";
reg = <0xe450 0x10>;
--
2.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
` (4 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 16/17] arm64: dts: rockchip: add rx0 mipi-phy for rk3399 Jacob Chen
@ 2018-03-08 9:48 ` Jacob Chen
2018-03-08 10:29 ` [PATCH v6 00/17] Rockchip ISP1 Driver Tomasz Figa
6 siblings, 0 replies; 51+ messages in thread
From: Jacob Chen @ 2018-03-08 9:48 UTC (permalink / raw)
To: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
eddie.cai.linux-Re5JQEeQqe8AvxtiuMwx3w,
heiko-4mtYJXux2i+zQB+pC5nmwQ, Jacob Chen,
jeffy.chen-TNX95d0MmH7DzftRWevZcw, zyc-TNX95d0MmH7DzftRWevZcw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
tfiga-F7+t8E8rja9g9hUCZPvPmw,
hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w,
laurent.pinchart-ryLnwIuWjnjg/C1BVhZhaw,
sakari.ailus-VuQAYsv1563Yd54FQh9/CA,
mchehab-DgEjT+Ai2ygdnm+yROfE0A, zhengsq-TNX95d0MmH7DzftRWevZcw,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-media-u79uwXL29TY76Z2rM5mHXA
From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
MAINTAINERS | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 4623caf8d72d..7a9ff4fa4592 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11852,6 +11852,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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+M: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+M: Yichong Zhong <zyc-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
+L: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.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-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
L: linux-media-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
--
2.16.1
^ permalink raw reply related [flat|nested] 51+ messages in thread
* Re: [PATCH v6 00/17] Rockchip ISP1 Driver
[not found] ` <20180308094807.9443-1-jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org>
` (5 preceding siblings ...)
2018-03-08 9:48 ` [PATCH v6 17/17] MAINTAINERS: add entry for Rockchip ISP1 driver Jacob Chen
@ 2018-03-08 10:29 ` Tomasz Figa
6 siblings, 0 replies; 51+ messages in thread
From: Tomasz Figa @ 2018-03-08 10:29 UTC (permalink / raw)
To: Jacob Chen
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Eddie Cai, Heiko Stübner,
Jacob Chen, Jeffy, 钟以崇,
Linux Kernel Mailing List, open list:ARM/Rockchip SoC...,
Hans Verkuil, Laurent Pinchart, Sakari Ailus,
Mauro Carvalho Chehab, Shunqian Zheng,
list-Y9sIeH5OGRo@public.gmane.org:IOMMU DRIVERS
<iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>,
Joerg Roedel <joro-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>,
, Linux Media Mailing List
On Thu, Mar 8, 2018 at 6:47 PM, Jacob Chen <jacob-chen-fyOeoxGR3m/QT0dZR+AlfA@public.gmane.org> wrote:
> From: Jacob Chen <jacob2.chen-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>
> changes in V6:
> - add mipi txrx phy support
> - remove bool and enum from uapi header
> - add buf_prepare op
> - correct some spelling problems
> - return all queued buffers when starting stream failed
Thanks Jacob.
For anyone planning to review, especially Hans, who pointed it out in
previous version, g_mbus_config is still there and we're working on
replacing it with something less controversial.
Best regards,
Tomasz
^ permalink raw reply [flat|nested] 51+ messages in thread