From 9370bb92b2d16684ee45cf24e879c93c509162da Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Thu, 19 Dec 2024 01:47:39 +0000
Subject: [PATCH] add wifi6 8852be driver

---
 kernel/drivers/media/i2c/sc200ai.c |  474 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 407 insertions(+), 67 deletions(-)

diff --git a/kernel/drivers/media/i2c/sc200ai.c b/kernel/drivers/media/i2c/sc200ai.c
index 8b9dd82..44c7156 100644
--- a/kernel/drivers/media/i2c/sc200ai.c
+++ b/kernel/drivers/media/i2c/sc200ai.c
@@ -11,8 +11,11 @@
  * V0.0X01.0X05 add quick stream on/off.
  * V0.0X01.0X06 fix set vflip/hflip failed bug.
  * V0.0X01.0X07
- * 1. fix set double times exposue value failed issue.
- * 2. add some debug info.
+ *	1. fix set double times exposue value failed issue.
+ *	2. add some debug info.
+ * V0.0X01.0X08
+ *	1. add support wakeup & sleep for aov function
+ *	2. using 60fps output default
  */
 
 #include <linux/clk.h>
@@ -33,8 +36,11 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
 #include <linux/pinctrl/consumer.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+#include "cam-tb-setup.h"
+#include "cam-sleep-wakeup.h"
 
-#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x07)
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x08)
 
 #ifndef V4L2_CID_DIGITAL_GAIN
 #define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
@@ -46,6 +52,7 @@
 
 #define PIXEL_RATE_WITH_371M_10BIT		(SC200AI_LINK_FREQ_371 * 2 * \
 					SC200AI_LANES / SC200AI_BITS_PER_SAMPLE)
+
 #define SC200AI_XVCLK_FREQ		27000000
 
 #define CHIP_ID				0xcb1c
@@ -125,14 +132,6 @@
 
 #define SC200AI_NUM_SUPPLIES ARRAY_SIZE(sc200ai_supply_names)
 
-enum sc200ai_max_pad {
-	PAD0, /* link to isp */
-	PAD1, /* link to csi wr0 | hdr x2:L x3:M */
-	PAD2, /* link to csi wr1 | hdr      x3:L */
-	PAD3, /* link to csi wr2 | hdr x2:M x3:S */
-	PAD_MAX,
-};
-
 struct regval {
 	u16 addr;
 	u8 val;
@@ -172,6 +171,7 @@
 	struct v4l2_ctrl	*vblank;
 	struct v4l2_ctrl	*test_pattern;
 	struct mutex		mutex;
+	struct v4l2_fract	cur_fps;
 	bool			streaming;
 	bool			power_on;
 	const struct sc200ai_mode *cur_mode;
@@ -181,7 +181,10 @@
 	const char		*len_name;
 	u32			cur_vts;
 	bool			has_init_exp;
+	bool			is_thunderboot;
+	bool			is_first_streamoff;
 	struct preisp_hdrae_exp_s init_hdrae_exp;
+	struct cam_sw_info	*cam_sw_inf;
 };
 
 #define to_sc200ai(sd) container_of(sd, struct sc200ai, subdev)
@@ -195,10 +198,10 @@
 
 /*
  * Xclk 24Mhz
- * max_framerate 90fps
+ * max_framerate 60fps
  * mipi_datarate per lane 1008Mbps, 4lane
  */
-static const struct regval sc200ai_linear_10_1920x1080_regs[] = {
+static const struct regval sc200ai_linear_10_1920x1080_60fps_regs[] = {
 	{0x0103, 0x01},
 	{0x0100, 0x00},
 	{0x36e9, 0x80},
@@ -338,6 +341,113 @@
 /*
  * Xclk 27Mhz
  * max_framerate 30fps
+ * mipi_datarate per lane 371.25Mbps, 2lane
+ */
+static const struct regval sc200ai_linear_10_1920x1080_30fps_regs[] = {
+	{0x0103, 0x01},
+	{0x0100, 0x00},
+	{0x36e9, 0x80},
+	{0x36f9, 0x80},
+	{0x301f, 0x03},
+	//HTS=1100*2=2200
+	{0x320c, 0x04},
+	{0x320d, 0x4c},
+	//VTS=1125
+	{0x320e, 0x04},
+	{0x320f, 0x65},
+	{0x3243, 0x01},
+	{0x3248, 0x02},
+	{0x3249, 0x09},
+	{0x3253, 0x08},
+	{0x3271, 0x0a},
+	{0x3301, 0x20},
+	{0x3304, 0x40},
+	{0x3306, 0x32},
+	{0x330b, 0x88},
+	{0x330f, 0x02},
+	{0x331e, 0x39},
+	{0x3333, 0x10},
+	{0x3621, 0xe8},
+	{0x3622, 0x16},
+	{0x3637, 0x1b},
+	{0x363a, 0x1f},
+	{0x363b, 0xc6},
+	{0x363c, 0x0e},
+	{0x3670, 0x0a},
+	{0x3674, 0x82},
+	{0x3675, 0x76},
+	{0x3676, 0x78},
+	{0x367c, 0x48},
+	{0x367d, 0x58},
+	{0x3690, 0x34},
+	{0x3691, 0x33},
+	{0x3692, 0x44},
+	{0x369c, 0x40},
+	{0x369d, 0x48},
+	{0x3901, 0x02},
+	{0x3904, 0x04},
+	{0x3908, 0x41},
+	{0x391d, 0x14},
+	{0x391f, 0x18},
+	{0x3e01, 0x8c},
+	{0x3e02, 0x20},
+	{0x3e16, 0x00},
+	{0x3e17, 0x80},
+	{0x3f09, 0x48},
+	{0x5787, 0x10},
+	{0x5788, 0x06},
+	{0x578a, 0x10},
+	{0x578b, 0x06},
+	{0x5790, 0x10},
+	{0x5791, 0x10},
+	{0x5792, 0x00},
+	{0x5793, 0x10},
+	{0x5794, 0x10},
+	{0x5795, 0x00},
+	{0x5799, 0x00},
+	{0x57c7, 0x10},
+	{0x57c8, 0x06},
+	{0x57ca, 0x10},
+	{0x57cb, 0x06},
+	{0x57d1, 0x10},
+	{0x57d4, 0x10},
+	{0x57d9, 0x00},
+	{0x59e0, 0x60},
+	{0x59e1, 0x08},
+	{0x59e2, 0x3f},
+	{0x59e3, 0x18},
+	{0x59e4, 0x18},
+	{0x59e5, 0x3f},
+	{0x59e6, 0x06},
+	{0x59e7, 0x02},
+	{0x59e8, 0x38},
+	{0x59e9, 0x10},
+	{0x59ea, 0x0c},
+	{0x59eb, 0x10},
+	{0x59ec, 0x04},
+	{0x59ed, 0x02},
+	{0x59ee, 0xa0},
+	{0x59ef, 0x08},
+	{0x59f4, 0x18},
+	{0x59f5, 0x10},
+	{0x59f6, 0x0c},
+	{0x59f7, 0x10},
+	{0x59f8, 0x06},
+	{0x59f9, 0x02},
+	{0x59fa, 0x18},
+	{0x59fb, 0x10},
+	{0x59fc, 0x0c},
+	{0x59fd, 0x10},
+	{0x59fe, 0x04},
+	{0x59ff, 0x02},
+	{0x36e9, 0x20},
+	{0x36f9, 0x27},
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 27Mhz
+ * max_framerate 30fps
  * mipi_datarate per lane 742.5Mbps, HDR 2lane
  */
 static const struct regval sc200ai_hdr_10_1920x1080_regs[] = {
@@ -346,8 +456,12 @@
 	{0x36e9, 0x80},
 	{0x36f9, 0x80},
 	{0x301f, 0x02},
+	//HTS=1100*2=2200
+	{0x320c, 0x04},
+	{0x320d, 0x4c},
+	//VTS =2252
 	{0x320e, 0x08},
-	{0x320f, 0xca},
+	{0x320f, 0xcc},
 	{0x3220, 0x53},
 	{0x3243, 0x01},
 	{0x3248, 0x02},
@@ -372,9 +486,11 @@
 	{0x331f, 0x61},
 	{0x3320, 0x07},
 	{0x3333, 0x10},
+	{0x3347, 0x77},
 	{0x334c, 0x08},
 	{0x3356, 0x09},
 	{0x3364, 0x17},
+	{0x336c, 0xcc},
 	{0x3390, 0x08},
 	{0x3391, 0x18},
 	{0x3392, 0x38},
@@ -431,8 +547,8 @@
 	{0x3e13, 0x40},
 	{0x3e16, 0x00},
 	{0x3e17, 0x80},
-	{0x3e23, 0x00},
-	{0x3e24, 0x40},
+	{0x3e23, 0x01},
+	{0x3e24, 0x9e},
 	{0x3f09, 0x48},
 	{0x4816, 0xb1},
 	{0x4819, 0x09},
@@ -507,10 +623,11 @@
 		.hts_def = 0x44C * 2,
 		.vts_def = 0x0465,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
-		.reg_list = sc200ai_linear_10_1920x1080_regs,
+		.reg_list = sc200ai_linear_10_1920x1080_60fps_regs,
 		.hdr_mode = NO_HDR,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
-	}, {
+	},
+	{
 		.width = 1920,
 		.height = 1080,
 		.max_fps = {
@@ -519,7 +636,22 @@
 		},
 		.exp_def = 0x0080,
 		.hts_def = 0x44C * 2,
-		.vts_def = 0x08CA,
+		.vts_def = 0x0465,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = sc200ai_linear_10_1920x1080_30fps_regs,
+		.hdr_mode = NO_HDR,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	},
+	{
+		.width = 1920,
+		.height = 1080,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0080,
+		.hts_def = 0x44C * 2,
+		.vts_def = 0x08CC,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
 		.reg_list = sc200ai_hdr_10_1920x1080_regs,
 		.hdr_mode = HDR_X2,
@@ -814,8 +946,8 @@
 	s_exp_time = s_exp_time * 2;
 	if (l_exp_time > 4362)                  //(2250 - 64 - 5) * 2
 		l_exp_time = 4362;
-	if (s_exp_time > 118)                //(64 - 5) * 2
-		s_exp_time = 118;
+	if (s_exp_time > 404)                //(64 - 5) * 2
+		s_exp_time = 404;
 
 	ret = sc200ai_write_reg(sc200ai->client,
 				SC200AI_REG_EXPOSURE_H,
@@ -837,7 +969,6 @@
 				 SC200AI_REG_SEXPOSURE_L,
 				 SC200AI_REG_VALUE_08BIT,
 				 SC200AI_FETCH_EXP_L(s_exp_time));
-
 
 	ret |= sc200ai_set_gain_reg(sc200ai, l_a_gain, SC200AI_LGAIN);
 	ret |= sc200ai_set_gain_reg(sc200ai, s_a_gain, SC200AI_SGAIN);
@@ -902,6 +1033,8 @@
 		__v4l2_ctrl_modify_range(sc200ai->vblank, vblank_def,
 					 SC200AI_VTS_MAX - mode->height,
 					 1, vblank_def);
+		sc200ai->cur_fps = mode->max_fps;
+		sc200ai->cur_vts = mode->vts_def;
 	}
 
 	mutex_unlock(&sc200ai->mutex);
@@ -994,14 +1127,15 @@
 	struct sc200ai *sc200ai = to_sc200ai(sd);
 	const struct sc200ai_mode *mode = sc200ai->cur_mode;
 
-	mutex_lock(&sc200ai->mutex);
-	fi->interval = mode->max_fps;
-	mutex_unlock(&sc200ai->mutex);
+	if (sc200ai->streaming)
+		fi->interval = sc200ai->cur_fps;
+	else
+		fi->interval = mode->max_fps;
 
 	return 0;
 }
 
-static int sc200ai_g_mbus_config(struct v4l2_subdev *sd,
+static int sc200ai_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
 				 struct v4l2_mbus_config *config)
 {
 	struct sc200ai *sc200ai = to_sc200ai(sd);
@@ -1015,7 +1149,7 @@
 	if (mode->hdr_mode == HDR_X3)
 		val |= V4L2_MBUS_CSI2_CHANNEL_2;
 
-	config->type = V4L2_MBUS_CSI2;
+	config->type = V4L2_MBUS_CSI2_DPHY;
 	config->flags = val;
 
 	return 0;
@@ -1031,10 +1165,22 @@
 	strlcpy(inf->base.lens, sc200ai->len_name, sizeof(inf->base.lens));
 }
 
+static int sc200ai_get_channel_info(struct sc200ai *sc200ai, struct rkmodule_channel_info *ch_info)
+{
+	if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX)
+		return -EINVAL;
+	ch_info->vc = sc200ai->cur_mode->vc[ch_info->index];
+	ch_info->width = sc200ai->cur_mode->width;
+	ch_info->height = sc200ai->cur_mode->height;
+	ch_info->bus_fmt = sc200ai->cur_mode->bus_fmt;
+	return 0;
+}
+
 static long sc200ai_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
 	struct sc200ai *sc200ai = to_sc200ai(sd);
 	struct rkmodule_hdr_cfg *hdr;
+	struct rkmodule_channel_info *ch_info;
 	u32 i, h, w;
 	long ret = 0;
 	u32 stream = 0;
@@ -1071,21 +1217,33 @@
 			__v4l2_ctrl_modify_range(sc200ai->hblank, w, w, 1, w);
 			__v4l2_ctrl_modify_range(sc200ai->vblank, h,
 						 SC200AI_VTS_MAX - sc200ai->cur_mode->height, 1, h);
+			sc200ai->cur_fps = sc200ai->cur_mode->max_fps;
+			sc200ai->cur_vts = sc200ai->cur_mode->vts_def;
 		}
 		break;
 	case PREISP_CMD_SET_HDRAE_EXP:
 		sc200ai_set_hdrae(sc200ai, arg);
+		if (sc200ai->cam_sw_inf)
+			memcpy(&sc200ai->cam_sw_inf->hdr_ae, (struct preisp_hdrae_exp_s *)(arg),
+				sizeof(struct preisp_hdrae_exp_s));
 		break;
 	case RKMODULE_SET_QUICK_STREAM:
 
 		stream = *((u32 *)arg);
 
-		if (stream)
+		if (stream) {
+			gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 1);
 			ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
 				 SC200AI_REG_VALUE_08BIT, SC200AI_MODE_STREAMING);
-		else
+		} else {
 			ret = sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
 				 SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
+			gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 0);
+		}
+		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = (struct rkmodule_channel_info *)arg;
+		ret = sc200ai_get_channel_info(sc200ai, ch_info);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -1104,6 +1262,7 @@
 	struct rkmodule_awb_cfg *cfg;
 	struct rkmodule_hdr_cfg *hdr;
 	struct preisp_hdrae_exp_s *hdrae;
+	struct rkmodule_channel_info *ch_info;
 	long ret;
 	u32 stream = 0;
 
@@ -1116,8 +1275,11 @@
 		}
 
 		ret = sc200ai_ioctl(sd, cmd, inf);
-		if (!ret)
+		if (!ret) {
 			ret = copy_to_user(up, inf, sizeof(*inf));
+			if (ret)
+				ret = -EFAULT;
+		}
 		kfree(inf);
 		break;
 	case RKMODULE_AWB_CFG:
@@ -1130,6 +1292,8 @@
 		ret = copy_from_user(cfg, up, sizeof(*cfg));
 		if (!ret)
 			ret = sc200ai_ioctl(sd, cmd, cfg);
+		else
+			ret = -EFAULT;
 		kfree(cfg);
 		break;
 	case RKMODULE_GET_HDR_CFG:
@@ -1140,8 +1304,11 @@
 		}
 
 		ret = sc200ai_ioctl(sd, cmd, hdr);
-		if (!ret)
+		if (!ret) {
 			ret = copy_to_user(up, hdr, sizeof(*hdr));
+			if (ret)
+				ret = -EFAULT;
+		}
 		kfree(hdr);
 		break;
 	case RKMODULE_SET_HDR_CFG:
@@ -1154,6 +1321,8 @@
 		ret = copy_from_user(hdr, up, sizeof(*hdr));
 		if (!ret)
 			ret = sc200ai_ioctl(sd, cmd, hdr);
+		else
+			ret = -EFAULT;
 		kfree(hdr);
 		break;
 	case PREISP_CMD_SET_HDRAE_EXP:
@@ -1166,12 +1335,31 @@
 		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
 		if (!ret)
 			ret = sc200ai_ioctl(sd, cmd, hdrae);
+		else
+			ret = -EFAULT;
 		kfree(hdrae);
 		break;
 	case RKMODULE_SET_QUICK_STREAM:
 		ret = copy_from_user(&stream, up, sizeof(u32));
 		if (!ret)
 			ret = sc200ai_ioctl(sd, cmd, &stream);
+		else
+			ret = -EFAULT;
+		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
+		if (!ch_info) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc200ai_ioctl(sd, cmd, ch_info);
+		if (!ret) {
+			ret = copy_to_user(up, ch_info, sizeof(*ch_info));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(ch_info);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -1186,21 +1374,30 @@
 {
 	int ret;
 
-	ret = sc200ai_write_array(sc200ai->client, sc200ai->cur_mode->reg_list);
-	if (ret)
-		return ret;
-
-	/* In case these controls are set before streaming */
-	ret = __v4l2_ctrl_handler_setup(&sc200ai->ctrl_handler);
-	if (ret)
-		return ret;
-	if (sc200ai->has_init_exp && sc200ai->cur_mode->hdr_mode != NO_HDR) {
-		ret = sc200ai_ioctl(&sc200ai->subdev, PREISP_CMD_SET_HDRAE_EXP,
-			&sc200ai->init_hdrae_exp);
-		if (ret) {
-			dev_err(&sc200ai->client->dev,
-				"init exp fail in hdr mode\n");
+	dev_info(&sc200ai->client->dev,
+		 "%dx%d@%d, mode %d, vts 0x%x\n",
+		 sc200ai->cur_mode->width,
+		 sc200ai->cur_mode->height,
+		 sc200ai->cur_fps.denominator / sc200ai->cur_fps.numerator,
+		 sc200ai->cur_mode->hdr_mode,
+		 sc200ai->cur_vts);
+	if (!sc200ai->is_thunderboot) {
+		ret = sc200ai_write_array(sc200ai->client, sc200ai->cur_mode->reg_list);
+		if (ret)
 			return ret;
+
+		/* In case these controls are set before streaming */
+		ret = __v4l2_ctrl_handler_setup(&sc200ai->ctrl_handler);
+		if (ret)
+			return ret;
+		if (sc200ai->has_init_exp && sc200ai->cur_mode->hdr_mode != NO_HDR) {
+			ret = sc200ai_ioctl(&sc200ai->subdev, PREISP_CMD_SET_HDRAE_EXP,
+				&sc200ai->init_hdrae_exp);
+			if (ret) {
+				dev_err(&sc200ai->client->dev,
+					"init exp fail in hdr mode\n");
+				return ret;
+			}
 		}
 	}
 
@@ -1211,10 +1408,15 @@
 static int __sc200ai_stop_stream(struct sc200ai *sc200ai)
 {
 	sc200ai->has_init_exp = false;
+	if (sc200ai->is_thunderboot) {
+		sc200ai->is_first_streamoff = true;
+		pm_runtime_put(&sc200ai->client->dev);
+	}
 	return sc200ai_write_reg(sc200ai->client, SC200AI_REG_CTRL_MODE,
 				 SC200AI_REG_VALUE_08BIT, SC200AI_MODE_SW_STANDBY);
 }
 
+static int __sc200ai_power_on(struct sc200ai *sc200ai);
 static int sc200ai_s_stream(struct v4l2_subdev *sd, int on)
 {
 	struct sc200ai *sc200ai = to_sc200ai(sd);
@@ -1227,6 +1429,10 @@
 		goto unlock_and_return;
 
 	if (on) {
+		if (sc200ai->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			sc200ai->is_thunderboot = false;
+			__sc200ai_power_on(sc200ai);
+		}
 		ret = pm_runtime_get_sync(&client->dev);
 		if (ret < 0) {
 			pm_runtime_put_noidle(&client->dev);
@@ -1271,11 +1477,13 @@
 			goto unlock_and_return;
 		}
 
-		ret = sc200ai_write_array(sc200ai->client, sc200ai_global_regs);
-		if (ret) {
-			v4l2_err(sd, "could not set init registers\n");
-			pm_runtime_put_noidle(&client->dev);
-			goto unlock_and_return;
+		if (!sc200ai->is_thunderboot) {
+			ret = sc200ai_write_array(sc200ai->client, sc200ai_global_regs);
+			if (ret) {
+				v4l2_err(sd, "could not set init registers\n");
+				pm_runtime_put_noidle(&client->dev);
+				goto unlock_and_return;
+			}
 		}
 
 		sc200ai->power_on = true;
@@ -1318,6 +1526,12 @@
 		dev_err(dev, "Failed to enable xvclk\n");
 		return ret;
 	}
+
+	cam_sw_regulator_bulk_init(sc200ai->cam_sw_inf, SC200AI_NUM_SUPPLIES, sc200ai->supplies);
+
+	if (sc200ai->is_thunderboot)
+		return 0;
+
 	if (!IS_ERR(sc200ai->reset_gpio))
 		gpiod_set_value_cansleep(sc200ai->reset_gpio, 0);
 
@@ -1356,6 +1570,15 @@
 	int ret;
 	struct device *dev = &sc200ai->client->dev;
 
+	clk_disable_unprepare(sc200ai->xvclk);
+	if (sc200ai->is_thunderboot) {
+		if (sc200ai->is_first_streamoff) {
+			sc200ai->is_thunderboot = false;
+			sc200ai->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
 	if (!IS_ERR(sc200ai->pwdn_gpio))
 		gpiod_set_value_cansleep(sc200ai->pwdn_gpio, 0);
 	clk_disable_unprepare(sc200ai->xvclk);
@@ -1369,6 +1592,51 @@
 	}
 	regulator_bulk_disable(SC200AI_NUM_SUPPLIES, sc200ai->supplies);
 }
+
+#if IS_REACHABLE(CONFIG_VIDEO_CAM_SLEEP_WAKEUP)
+static int __maybe_unused sc200ai_resume(struct device *dev)
+{
+	int ret;
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc200ai *sc200ai = to_sc200ai(sd);
+
+	cam_sw_prepare_wakeup(sc200ai->cam_sw_inf, dev);
+
+	usleep_range(4000, 5000);
+	cam_sw_write_array(sc200ai->cam_sw_inf);
+
+	if (__v4l2_ctrl_handler_setup(&sc200ai->ctrl_handler))
+		dev_err(dev, "__v4l2_ctrl_handler_setup fail!");
+
+	if (sc200ai->has_init_exp && sc200ai->cur_mode != NO_HDR) {	// hdr mode
+		ret = sc200ai_ioctl(&sc200ai->subdev, PREISP_CMD_SET_HDRAE_EXP,
+				    &sc200ai->cam_sw_inf->hdr_ae);
+		if (ret) {
+			dev_err(&sc200ai->client->dev, "set exp fail in hdr mode\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int __maybe_unused sc200ai_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc200ai *sc200ai = to_sc200ai(sd);
+
+	cam_sw_write_array_cb_init(sc200ai->cam_sw_inf, client,
+				   (void *)sc200ai->cur_mode->reg_list,
+				   (sensor_write_array)sc200ai_write_array);
+	cam_sw_prepare_sleep(sc200ai->cam_sw_inf);
+
+	return 0;
+}
+#else
+#define sc200ai_resume NULL
+#define sc200ai_suspend NULL
+#endif
 
 static int sc200ai_runtime_resume(struct device *dev)
 {
@@ -1430,6 +1698,7 @@
 static const struct dev_pm_ops sc200ai_pm_ops = {
 	SET_RUNTIME_PM_OPS(sc200ai_runtime_suspend,
 			   sc200ai_runtime_resume, NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(sc200ai_suspend, sc200ai_resume)
 };
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
@@ -1449,7 +1718,6 @@
 static const struct v4l2_subdev_video_ops sc200ai_video_ops = {
 	.s_stream = sc200ai_s_stream,
 	.g_frame_interval = sc200ai_g_frame_interval,
-	.g_mbus_config = sc200ai_g_mbus_config,
 };
 
 static const struct v4l2_subdev_pad_ops sc200ai_pad_ops = {
@@ -1458,6 +1726,7 @@
 	.enum_frame_interval = sc200ai_enum_frame_interval,
 	.get_fmt = sc200ai_get_fmt,
 	.set_fmt = sc200ai_set_fmt,
+	.get_mbus_config = sc200ai_g_mbus_config,
 };
 
 static const struct v4l2_subdev_ops sc200ai_subdev_ops = {
@@ -1465,6 +1734,14 @@
 	.video	= &sc200ai_video_ops,
 	.pad	= &sc200ai_pad_ops,
 };
+
+static void sc200ai_modify_fps_info(struct sc200ai *sc200ai)
+{
+	const struct sc200ai_mode *mode = sc200ai->cur_mode;
+
+	sc200ai->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def/
+				       sc200ai->cur_vts;
+}
 
 static int sc200ai_set_ctrl(struct v4l2_ctrl *ctrl)
 {
@@ -1528,7 +1805,9 @@
 					 SC200AI_REG_VALUE_08BIT,
 					 (ctrl->val + sc200ai->cur_mode->height)
 					 & 0xff);
-		sc200ai->cur_vts = ctrl->val + sc200ai->cur_mode->height;
+		if (!ret)
+			sc200ai->cur_vts = ctrl->val + sc200ai->cur_mode->height;
+		sc200ai_modify_fps_info(sc200ai);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc200ai_enable_test_pattern(sc200ai, ctrl->val);
@@ -1591,12 +1870,14 @@
 					    h_blank, h_blank, 1, h_blank);
 	if (sc200ai->hblank)
 		sc200ai->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	sc200ai->cur_fps = mode->max_fps;
 	vblank_def = mode->vts_def - mode->height;
+	sc200ai->cur_vts = mode->vts_def;
 	sc200ai->vblank = v4l2_ctrl_new_std(handler, &sc200ai_ctrl_ops,
 					    V4L2_CID_VBLANK, vblank_def,
 					    SC200AI_VTS_MAX - mode->height,
 					    1, vblank_def);
-	exposure_max = mode->vts_def - 4;
+	exposure_max = 2 * mode->vts_def - 8;
 	sc200ai->exposure = v4l2_ctrl_new_std(handler, &sc200ai_ctrl_ops,
 					      V4L2_CID_EXPOSURE, SC200AI_EXPOSURE_MIN,
 					      exposure_max, SC200AI_EXPOSURE_STEP,
@@ -1641,6 +1922,11 @@
 	u32 id = 0;
 	int ret;
 
+	if (sc200ai->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
 	ret = sc200ai_read_reg(client, SC200AI_REG_CHIP_ID,
 			       SC200AI_REG_VALUE_16BIT, &id);
 	if (id != CHIP_ID) {
@@ -1665,6 +1951,51 @@
 				       sc200ai->supplies);
 }
 
+#ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP
+static void find_terminal_resolution(struct sc200ai *sc200ai)
+{
+	int i = 0;
+	const struct sc200ai_mode *mode = NULL;
+	u32 rk_cam_hdr = get_rk_cam_hdr();
+	u32 rk_cam_w = get_rk_cam_w();
+	u32 rk_cam_h = get_rk_cam_h();
+
+	if (rk_cam_w == 0 || rk_cam_h == 0)
+		goto err_find_res;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		mode = &supported_modes[i];
+		if (mode->width == rk_cam_w && mode->height == rk_cam_h &&
+		    mode->hdr_mode == rk_cam_hdr) {
+			sc200ai->cur_mode = mode;
+			return;
+		}
+	}
+err_find_res:
+	dev_err(&sc200ai->client->dev, "not match %dx%d mode %d\n!",
+		rk_cam_w, rk_cam_h, rk_cam_hdr);
+	sc200ai->cur_mode = &supported_modes[0];
+}
+#else
+static void find_terminal_resolution(struct sc200ai *sc200ai)
+{
+	u32 hdr_mode = 0;
+	struct device_node *node = sc200ai->client->dev.of_node;
+	int i = 0;
+
+	of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		if (hdr_mode == supported_modes[i].hdr_mode) {
+			sc200ai->cur_mode = &supported_modes[i];
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(supported_modes))
+		sc200ai->cur_mode = &supported_modes[0];
+
+}
+#endif
+
 static int sc200ai_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -1674,7 +2005,6 @@
 	struct v4l2_subdev *sd;
 	char facing[2];
 	int ret;
-	u32 i, hdr_mode = 0;
 
 	dev_info(dev, "driver version: %02x.%02x.%02x",
 		 DRIVER_VERSION >> 16,
@@ -1685,7 +2015,6 @@
 	if (!sc200ai)
 		return -ENOMEM;
 
-	of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
 	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
 				   &sc200ai->module_index);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
@@ -1699,15 +2028,10 @@
 		return -EINVAL;
 	}
 
+	sc200ai->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
 	sc200ai->client = client;
-	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
-		if (hdr_mode == supported_modes[i].hdr_mode) {
-			sc200ai->cur_mode = &supported_modes[i];
-			break;
-		}
-	}
-	if (i == ARRAY_SIZE(supported_modes))
-		sc200ai->cur_mode = &supported_modes[0];
+
+	find_terminal_resolution(sc200ai);
 
 	sc200ai->xvclk = devm_clk_get(dev, "xvclk");
 	if (IS_ERR(sc200ai->xvclk)) {
@@ -1715,11 +2039,11 @@
 		return -EINVAL;
 	}
 
-	sc200ai->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	sc200ai->reset_gpio = devm_gpiod_get(dev, "reset", sc200ai->is_thunderboot ? GPIOD_ASIS : GPIOD_OUT_LOW);
 	if (IS_ERR(sc200ai->reset_gpio))
 		dev_warn(dev, "Failed to get reset-gpios\n");
 
-	sc200ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+	sc200ai->pwdn_gpio = devm_gpiod_get(dev, "pwdn", sc200ai->is_thunderboot ? GPIOD_ASIS : GPIOD_OUT_LOW);
 	if (IS_ERR(sc200ai->pwdn_gpio))
 		dev_warn(dev, "Failed to get pwdn-gpios\n");
 
@@ -1775,6 +2099,13 @@
 		goto err_power_off;
 #endif
 
+	if (!sc200ai->cam_sw_inf) {
+		sc200ai->cam_sw_inf = cam_sw_init();
+		cam_sw_clk_init(sc200ai->cam_sw_inf, sc200ai->xvclk, SC200AI_XVCLK_FREQ);
+		cam_sw_reset_pin_init(sc200ai->cam_sw_inf, sc200ai->reset_gpio, 0);
+		cam_sw_pwdn_pin_init(sc200ai->cam_sw_inf, sc200ai->pwdn_gpio, 1);
+	}
+
 	memset(facing, 0, sizeof(facing));
 	if (strcmp(sc200ai->module_facing, "back") == 0)
 		facing[0] = 'b';
@@ -1792,7 +2123,10 @@
 
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
-	pm_runtime_idle(dev);
+	if (sc200ai->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
 
 	return 0;
 
@@ -1821,6 +2155,8 @@
 #endif
 	v4l2_ctrl_handler_free(&sc200ai->ctrl_handler);
 	mutex_destroy(&sc200ai->mutex);
+
+	cam_sw_deinit(sc200ai->cam_sw_inf);
 
 	pm_runtime_disable(&client->dev);
 	if (!pm_runtime_status_suspended(&client->dev))
@@ -1864,8 +2200,12 @@
 	i2c_del_driver(&sc200ai_i2c_driver);
 }
 
+#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
+subsys_initcall(sensor_mod_init);
+#else
 device_initcall_sync(sensor_mod_init);
+#endif
 module_exit(sensor_mod_exit);
 
 MODULE_DESCRIPTION("smartsens sc200ai sensor driver");
-MODULE_LICENSE("GPL v2");
+MODULE_LICENSE("GPL");

--
Gitblit v1.6.2