VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   30 Branches   50 Tags
author: Andy Hu <andy.hu@starfivetech.com> 2023-11-04 19:59:49 +0800 committer: Andy Hu <andy.hu@starfivetech.com> 2023-11-04 19:59:49 +0800 commit: 7e61f06219e05739852bc9d60c37d4fea1ed401e parent: b133739ab239ec9e5cd9bc4a15817145989021e4
Commit Summary:
Merge tag 'JH7110_515_SDK_v5.9.0' into vf2-515-devel
Diffstat:
7 files changed, 1064 insertions, 45 deletions
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
index a6f19c03104e..51e09f50628f 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.c
@@ -12,6 +12,7 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 #include <linux/firmware.h>
+#include <linux/jh7110-isp.h>
 #include "stf_isp_ioctl.h"
 #include "stf_dmabuf.h"
 
@@ -86,16 +87,15 @@ static const struct isp_format isp_formats_st7110_iti[] = {
 	{ MEDIA_BUS_FMT_YUV8_1X24, 8},
 };
 
-#define SINK_FORMATS_INDEX    0
-#define UO_FORMATS_INDEX      1
-#define ITI_FORMATS_INDEX     2
-#define RAW_FORMATS_INDEX     3
-
 static const struct isp_format_table isp_formats_st7110[] = {
-	{ isp_formats_st7110_sink, ARRAY_SIZE(isp_formats_st7110_sink) }, // 0
-	{ isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     // 1
-	{ isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },   // 2
-	{ isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },   // 3
+	{ isp_formats_st7110_sink, ARRAY_SIZE(isp_formats_st7110_sink) }, /* pad 0 */
+	{ isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 1 */
+	{ isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 2 */
+	{ isp_formats_st7110_uo, ARRAY_SIZE(isp_formats_st7110_uo) },     /* pad 3 */
+	{ isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },   /* pad 4 */
+	{ isp_formats_st7110_iti, ARRAY_SIZE(isp_formats_st7110_iti) },   /* pad 5 */
+	{ isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },   /* pad 6 */
+	{ isp_formats_st7110_raw, ARRAY_SIZE(isp_formats_st7110_raw) },   /* pad 7 */
 };
 
 int stf_isp_subdev_init(struct stfcamss *stfcamss)
@@ -195,6 +195,41 @@ static const char * const test_pattern_menu[] = {
 	"Color squares w/ rolling bar",
 };
 
+enum isp_modules_index {
+	imi_obc = 0,
+	imi_oecf,
+	imi_lccf,
+	imi_awb,
+	imi_dbc,
+	imi_ctc,
+	imi_cfa,
+	imi_car,
+	imi_ccm,
+	imi_gmargb,
+	imi_r2y,
+	imi_ycrv,
+	imi_shrp,
+	imi_dnyuv,
+	imi_sat,
+	imi_sc
+};
+
+#define MODULE_ENABLE_REGISTER0				0x10
+#define MODULE_ENABLE_REGISTER1				0xa08
+
+struct module_register_info {
+	__u32 en_reg;
+	__u32 en_nbit;
+	__u32 cfg_reg;
+};
+
+static const struct module_register_info mod_reg_info[] = {
+	{MODULE_ENABLE_REGISTER0, 2, 0x034}, {MODULE_ENABLE_REGISTER0, 4, 0x100}, {MODULE_ENABLE_REGISTER0, 6, 0x050}, {MODULE_ENABLE_REGISTER0, 7, 0x280},
+	{MODULE_ENABLE_REGISTER1, 22, 0xa14}, {MODULE_ENABLE_REGISTER1, 21, 0xa10}, {MODULE_ENABLE_REGISTER1, 1, 0xa1c}, {MODULE_ENABLE_REGISTER1, 2, 0x000},
+	{MODULE_ENABLE_REGISTER1, 3, 0xc40}, {MODULE_ENABLE_REGISTER1, 4, 0xe00}, {MODULE_ENABLE_REGISTER1, 5, 0xe40}, {MODULE_ENABLE_REGISTER1, 19, 0xf00},
+	{MODULE_ENABLE_REGISTER1, 7, 0xe80}, {MODULE_ENABLE_REGISTER1, 17, 0xc00}, {MODULE_ENABLE_REGISTER1, 8, 0xa30}, {MODULE_ENABLE_REGISTER0, 17, 0x09c}
+};
+
 #define ISP_TEST_ENABLE			BIT(7)
 #define ISP_TEST_ROLLING		BIT(6)	/* rolling horizontal bar */
 #define ISP_TEST_TRANSPARENT		BIT(5)
@@ -260,11 +295,459 @@ static int isp_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
 	return 0;
 }
 
+#define CREATE_REG_VALUE_FUNCTION(type)	\
+	static u32 create_reg_value_##type(const type * value, s32 size, u32 mask, s32 nbits) {	\
+	s32 npos = 0;	\
+	u32 res = 0;	\
+	s32 sz = size;	\
+	s32 i = 0;	\
+	if(size * nbits > 32) sz = 32 / nbits;	\
+	for(i = 0; i < sz; i++, npos += nbits, value++) res |= (u32)(value[0] & mask) << npos;	\
+	return res;	\
+}
+
+CREATE_REG_VALUE_FUNCTION(u8);
+CREATE_REG_VALUE_FUNCTION(u16);
+#define CREATE_REG_VALUE(type, value, size, mask, nbits) create_reg_value_##type(value, size, mask, nbits)
+
+#define FILL_ISP_REGS_FUNC(type)	\
+static void fill_isp_regs_##type(void __iomem *ispbase, u32 offset, const type * value, s32 size, u32 mask, u32 nbits) {	\
+	s32 i;	\
+	for(i = 0; i < size; i++, value++)	\
+		reg_write(ispbase, offset + i * 4, (u32)(value[0] & mask) << nbits);	\
+}
+FILL_ISP_REGS_FUNC(u32);
+FILL_ISP_REGS_FUNC(u8);
+FILL_ISP_REGS_FUNC(u16);
+
+#define FILL_ISP_REGS(type, ispbase, offset, value, size, mask, nbits)	\
+	fill_isp_regs_##type(ispbase, offset, value, size, mask, nbits)
+
+static void fill_regs_with_zero(void __iomem *ispbase, u32 offset, u32 size)
+{
+	u32 i;
+	for(i = 0; i < size; i++, offset += 4)
+		reg_write(ispbase, offset, 0);
+}
+
+static int isp_set_ctrl_wb(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_awb];
+	const struct jh7110_isp_wb_setting * setting = (const struct jh7110_isp_wb_setting *)value;
+	const struct jh7110_isp_wb_gain * gains = &setting->gains;
+	u32 r_g = (((u32)gains->gain_r << 16) | gains->gain_r) & 0x03ff03ff;
+	u32 g_g = (((u32)gains->gain_g << 16) | gains->gain_g) & 0x03ff03ff;
+	u32 b_g = (((u32)gains->gain_b << 16) | gains->gain_b) & 0x03ff03ff;
+	u32 reg_addr = reg_info->cfg_reg + 16 * sizeof(u32);
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	fill_regs_with_zero(ispbase, reg_info->cfg_reg, 16);
+
+	reg_write(ispbase, reg_addr, r_g);
+	reg_write(ispbase, reg_addr + 1 * 4, r_g);
+	reg_write(ispbase, reg_addr + 2 * 4, g_g);
+	reg_write(ispbase, reg_addr + 3 * 4, g_g);
+	reg_write(ispbase, reg_addr + 4 * 4, g_g);
+	reg_write(ispbase, reg_addr + 5 * 4, g_g);
+	reg_write(ispbase, reg_addr + 6 * 4, b_g);
+	reg_write(ispbase, reg_addr + 7 * 4, b_g);
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_car(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_car];
+	const struct jh7110_isp_car_setting * setting = (const struct jh7110_isp_car_setting *)value;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_ccm(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_ccm];
+	const struct jh7110_isp_ccm_setting * setting = (const struct jh7110_isp_ccm_setting *)value;
+	const struct jh7110_isp_ccm_smlow * ccm = (const struct jh7110_isp_ccm_smlow *)(&(setting->ccm_smlow));
+	u32 reg_addr = reg_info->cfg_reg + 12 * sizeof(u32);
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	reg_write(ispbase, reg_info->cfg_reg, 6 << 16);
+	fill_regs_with_zero(ispbase, reg_info->cfg_reg + 4, 11);
+
+	FILL_ISP_REGS(u32, ispbase, reg_addr, (u32 *)ccm, 12, 0x7ff, 0);
+
+	reg_addr += 12 * 4;
+	fill_regs_with_zero(ispbase, reg_addr, 2);
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_cfa(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_cfa];
+	const struct jh7110_isp_cfa_setting * setting = (const struct jh7110_isp_cfa_setting *)value;
+	const struct jh7110_isp_cfa_params * cfg = (const struct jh7110_isp_cfa_params *)(&(setting->settings));
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	reg_write(ispbase, reg_addr, ((u32)(cfg->cross_cov & 0x3) << 4) | (cfg->hv_width & 0xf));
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_ctc(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_ctc];
+	const struct jh7110_isp_ctc_setting * setting = (const struct jh7110_isp_ctc_setting *)value;
+	const struct jh7110_isp_ctc_params * cfg = (const struct jh7110_isp_ctc_params *)(&(setting->settings));
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	u32 reg_value = (u32)(((cfg->saf_mode & 1) << 1) | (cfg->daf_mode & 1)) << 30;
+
+	reg_value |= ((u32)(cfg->max_gt & 0x3ff) << 16) | (cfg->min_gt & 0x3ff);
+	reg_write(ispbase, reg_addr, reg_value);
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_dbc(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_dbc];
+	const struct jh7110_isp_dbc_setting * setting = (const struct jh7110_isp_dbc_setting *)value;
+	const struct jh7110_isp_dbc_params * cfg = (const struct jh7110_isp_dbc_params *)(&(setting->settings));
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	reg_write(ispbase, reg_addr, ((u32)(cfg->bad_gt & 0x3ff) << 16) | (cfg->bad_xt & 0x3ff));
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_dnyuv(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_dnyuv];
+	const struct jh7110_isp_dnyuv_setting * setting = (const struct jh7110_isp_dnyuv_setting *)value;
+	const struct jh7110_isp_dnyuv_params * cfg = (const struct jh7110_isp_dnyuv_params *)(&(setting->settings));
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+
+	reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, cfg->y_sweight, 6, 0x7, 4));
+	reg_write(ispbase, reg_addr + 4, CREATE_REG_VALUE(u8, &cfg->y_sweight[6], 4, 0x7, 4));
+	reg_write(ispbase, reg_addr + 8, CREATE_REG_VALUE(u8, cfg->uv_sweight, 6, 0x7, 4));
+	reg_write(ispbase, reg_addr + 12, CREATE_REG_VALUE(u8, &cfg->uv_sweight[6], 4, 0x7, 4));
+	reg_write(ispbase, reg_addr + 16, CREATE_REG_VALUE(u16, &cfg->y_curve[0], 2, 0x3ff, 16));
+	reg_write(ispbase, reg_addr + 20, CREATE_REG_VALUE(u16, &cfg->y_curve[2], 2, 0x3ff, 16));
+	reg_write(ispbase, reg_addr + 24, CREATE_REG_VALUE(u16, &cfg->y_curve[4], 2, 0x3ff, 16));
+	reg_write(ispbase, reg_addr + 28, CREATE_REG_VALUE(u16, &cfg->uv_curve[0], 2, 0x3ff, 16));
+	reg_write(ispbase, reg_addr + 32, CREATE_REG_VALUE(u16, &cfg->uv_curve[2], 2, 0x3ff, 16));
+	reg_write(ispbase, reg_addr + 36, CREATE_REG_VALUE(u16, &cfg->uv_curve[4], 2, 0x3ff, 16));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_gmargb(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_gmargb];
+	const struct jh7110_isp_gmargb_setting * setting = (const struct jh7110_isp_gmargb_setting *)value;
+	const struct jh7110_isp_gmargb_point * curve = setting->curve;
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	for(i = 0; i < 15; i++, curve++, reg_addr += 4)
+		reg_write(ispbase, reg_addr, ((u32)curve->sg_val << 16) | (curve->g_val & 0x3ff));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_lccf(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_lccf];
+	const struct jh7110_isp_lccf_setting * setting = (const struct jh7110_isp_lccf_setting *)value;
+	const s16 * params = (s16 *)(&setting->r_param);
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	reg_write(ispbase, reg_addr, ((u32)(setting->circle.center_y & 0x7fff) << 16) | (setting->circle.center_x & 0x7fff));
+	reg_write(ispbase, reg_addr + 8, setting->circle.radius & 0xf);
+	reg_addr += 0x90;
+	for(i = 0; i < 4; i++, reg_addr += 4, params += 2)
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x1fff, 16));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_obc(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_obc];
+	const struct jh7110_isp_blacklevel_setting * setting = (const struct jh7110_isp_blacklevel_setting *)value;
+	const u8 * params = (u8 *)setting->gain;
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	reg_write(ispbase, reg_addr, ((u32)(setting->win_size.height & 0xf) << 4) | (setting->win_size.width & 0xf));
+
+	reg_addr += 0x2ac;	//0x2e0
+	for(i = 0; i < 8; i++, reg_addr += 4, params += 4)
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 4, 0xff, 8));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_oecf(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_oecf];
+	const struct jh7110_isp_oecf_setting * setting = (const struct jh7110_isp_oecf_setting *)value;
+	const struct jh7110_isp_oecf_point * pts = (struct jh7110_isp_oecf_point *)(setting->r_curve);
+	u32 reg_x_addr = reg_info->cfg_reg;
+	u32 reg_y_addr = reg_info->cfg_reg + 0x080;
+	u32 reg_s_addr = reg_info->cfg_reg + 0x100;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	u32 x, y, slope;
+	s32 i;
+	
+	for(i = 0; i < 32; i++, reg_x_addr += 4, reg_y_addr += 4, reg_s_addr += 4) {
+		x = pts->x & 0x3ff;
+		y = pts->y & 0x3ff;
+		slope = pts->slope & 0x3ff;
+		pts++;
+		x |= ((pts->x & 0x3ff) << 16);
+		y |= ((pts->y & 0x3ff) << 16);
+		slope |= ((pts->slope & 0x3ff) << 16);
+		pts++;
+
+		reg_write(ispbase, reg_x_addr, x);
+		reg_write(ispbase, reg_y_addr, y);
+		reg_write(ispbase, reg_s_addr, slope);
+	}
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_r2y(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_r2y];
+	const struct jh7110_isp_r2y_setting * setting = (const struct jh7110_isp_r2y_setting *)value;
+	const s16 * params = setting->matrix.m;
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	for(i = 0; i < 9; i++, reg_addr += 4)
+		reg_write(ispbase, reg_addr, (u32)(params[i] & 0x1ff));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+
+static int isp_set_ctrl_sat(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_sat];
+	const struct jh7110_isp_sat_setting * setting = (const struct jh7110_isp_sat_setting *)value;
+	const u16 * params = (u16 *)(&setting->sat_info);
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	for(i = 0; i < 3; i++, reg_addr += 4, params += 2)
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0xfff, 16));
+	reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, &setting->hue_info.cos, 2, 0x3ff, 16));
+	reg_addr += 4;
+	reg_write(ispbase, reg_addr, setting->sat_info.cmsf & 0xf);
+
+	params = (u16 *)(&setting->curve);
+	reg_addr += 0x14;		// 0xa54
+	for(i = 0; i < 2; i++, reg_addr += 4, params += 2)
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u16, params, 2, 0x3fff, 16));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_shrp(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_shrp];
+	const struct jh7110_isp_sharp_setting * setting = (const struct jh7110_isp_sharp_setting *)value;
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	s32 i;
+	
+	for(i = 0; i < 4; i++, reg_addr += 4)
+		reg_write(ispbase, reg_addr, ((u32)(setting->strength.diff[i] & 0x3ff) << 16) | ((u32)(setting->weight.weight[i] & 0xf) << 8));
+	FILL_ISP_REGS(u8, ispbase, reg_addr, (u8 *)(&setting->weight.weight[4]), 15 - 4, 0xf, 8);
+	reg_addr += (15 - 4) * 4;
+
+	for(i = 0; i < 3; i++, reg_addr += 4)
+		reg_write(ispbase, reg_addr, ((u32)(setting->strength.f[i] & 0x7f) << 24) | (setting->strength.s[i] & 0x1fffff));
+
+	reg_addr += 3 * 4;
+	reg_write(ispbase, reg_addr, ((u32)(setting->pdirf & 0xf) << 28) | ((u32)(setting->ndirf & 0xf) << 24) | (setting->weight.recip_wei_sum & 0x3fffff));
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_ycrv(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_ycrv];
+	const struct jh7110_isp_ycrv_setting * setting = (const struct jh7110_isp_ycrv_setting *)value;
+	u32 reg_addr = reg_info->cfg_reg;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	
+	FILL_ISP_REGS(u16, ispbase, reg_addr, (u16 *)(setting->curve.y), 64, 0x3ff, 0);
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+	
+	return 0;
+}
+
+static int isp_set_ctrl_sc(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct module_register_info * reg_info = &mod_reg_info[imi_sc];
+	const struct jh7110_isp_sc_setting * setting = (const struct jh7110_isp_sc_setting *)value;
+	const u8 * params = setting->awb_config.awb_cw;
+	u32 reg_addr = 0x00;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	u32 weight_cfg[6] = {0};
+	u32 * weight = weight_cfg;
+	u32 * w_diff = weight_cfg + 2;
+	s32 i;
+
+	// SC dumping axi id
+	reg_write(ispbase, 0x9c, 1 << 24);
+
+	// SC frame crop
+	reg_write(ispbase, 0xb8, ((u32)(setting->crop_config.v_start) << 16) | setting->crop_config.h_start);
+
+	// SC config1
+	reg_write(ispbase, 0xbc, ((u32)(setting->awb_config.sel) << 30) | ((u32)(setting->awb_config.awb_ps_grb_ba) << 16) | 
+		((u32)(setting->crop_config.sw_height) << 8) | setting->crop_config.sw_width);
+
+	// SC decimation config
+	reg_write(ispbase, 0xd8, ((u32)(setting->crop_config.vkeep) << 24) | ((u32)(setting->crop_config.vperiod) << 16) | 
+		((u32)(setting->crop_config.hkeep) << 8) | setting->crop_config.hperiod);
+
+	// SC AWB pixel sum config
+	reg_write(ispbase, 0xc4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_rl, 4, 0xff, 8));
+	reg_write(ispbase, 0xc8, CREATE_REG_VALUE(u8, &setting->awb_config.ws_ps_config.awb_ps_bl, 4, 0xff, 8));
+	reg_write(ispbase, 0xcc, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grl, 2, 0xffff, 16));
+	reg_write(ispbase, 0xd0, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_gbl, 2, 0xffff, 16));
+	reg_write(ispbase, 0xd4, CREATE_REG_VALUE(u16, &setting->awb_config.ws_ps_config.awb_ps_grbl, 2, 0xffff, 16));
+
+	// AF register
+	reg_write(ispbase, 0xc0, 
+		((u32)(setting->af_config.es_hor_thr & 0x1ff) << 16) |
+		((u32)(setting->af_config.es_ver_thr & 0xff) << 8) |
+		((setting->af_config.ver_en & 0x1) << 3) |
+		((setting->af_config.hor_en & 0x1) << 2) |
+		((setting->af_config.es_sum_mode & 0x1) << 1) |
+		(setting->af_config.es_hor_mode & 0x1));
+
+	// AWB weight sum register
+	reg_write(ispbase, 0x5d0, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_rl, 4, 0xff, 8));
+	reg_write(ispbase, 0x5d4, CREATE_REG_VALUE(u8, &setting->awb_config.ws_config.awb_ws_gbl, 4, 0xff, 8));
+
+	// AWB weight value point
+	reg_addr = 0x4d0;
+	for(i = 0; i < 13; i++) {
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 8, 0xf, 4));
+		reg_addr += 4;
+		params += 8;
+		reg_write(ispbase, reg_addr, CREATE_REG_VALUE(u8, params, 5, 0xf, 4));
+		reg_addr += 4;
+		params += 5;
+	}
+
+	// AWB intensity weight curve register
+	reg_addr = 0x538;
+	for(i = 0; i < 4; i++) {
+		weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
+		w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
+	}
+	for(w_diff++; i < 8; i++) {
+		weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
+		w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
+	}
+	for(weight++, w_diff++; i < 12; i++) {
+		weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
+		w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
+	}
+	for(w_diff++; i < 16; i++) {
+		weight[0] |= (setting->awb_config.pts[i].weight & 0xf) << (i * 4);
+		w_diff[0] |= ((((s16)(setting->awb_config.pts[i + 1].weight & 0xf) - (s16)(setting->awb_config.pts[i].weight & 0xf)) * 2) & 0xff) << (i * 8);
+	}
+
+	FILL_ISP_REGS(u32, ispbase, reg_addr, weight_cfg, 6, 0xffffffff, 0);
+
+	reg_set_bit(ispbase, reg_info->en_reg, 1 << reg_info->en_nbit, setting->enabled ? 1 << reg_info->en_nbit : 0);
+
+	return 0;
+}
+
+static int isp_set_ctrl_outss(struct stf_isp_dev *isp_dev, const void * value)
+{
+	const struct jh7110_isp_outss_setting * setting = (const struct jh7110_isp_outss_setting *)value;
+	struct stf_vin_dev *vin = isp_dev->stfcamss->vin;
+	void __iomem *ispbase = vin->isp_base;
+	u32 reg_addr = !setting->which ? 0xa9c : 0xab4;
+
+	if(!setting->stride)
+		return 0;
+
+	// Output Image Stride Register, 8-byte(64bit) granularity.
+	reg_write(ispbase, reg_addr, setting->stride);
+	reg_write(ispbase, reg_addr + 4, ((setting->hsm << 16) | (setting->hsm & 0x3)));
+	reg_write(ispbase, reg_addr + 8, ((setting->vsm << 16) | (setting->vsm & 0x3)));
+
+	return 0;
+}
+
 static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
 	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
-	int ret;
+	int ret = 0;
 
 	/*
 	 * If the device is not powered up by the host driver do
@@ -309,6 +792,58 @@ static int isp_s_ctrl(struct v4l2_ctrl *ctrl)
 	case V4L2_CID_VFLIP:
 		ret = isp_set_ctrl_vflip(isp_dev, ctrl->val);
 		break;
+	case V4L2_CID_USER_JH7110_ISP_WB_SETTING:
+		ret = isp_set_ctrl_wb(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_CAR_SETTING:
+		ret = isp_set_ctrl_car(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_CCM_SETTING:
+		ret = isp_set_ctrl_ccm(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_CFA_SETTING:
+		ret = isp_set_ctrl_cfa(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_CTC_SETTING:
+		ret = isp_set_ctrl_ctc(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_DBC_SETTING:
+		ret = isp_set_ctrl_dbc(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING:
+		ret = isp_set_ctrl_dnyuv(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING:
+		ret = isp_set_ctrl_gmargb(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_LCCF_SETTING:
+		ret = isp_set_ctrl_lccf(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_OBC_SETTING:
+		ret = isp_set_ctrl_obc(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_OECF_SETTING:
+		ret = isp_set_ctrl_oecf(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_R2Y_SETTING:
+		ret = isp_set_ctrl_r2y(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_SAT_SETTING:
+		ret = isp_set_ctrl_sat(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_SHRP_SETTING:
+		ret = isp_set_ctrl_shrp(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_YCRV_SETTING:
+		ret = isp_set_ctrl_ycrv(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_STAT_SETTING:
+		ret = isp_set_ctrl_sc(isp_dev, ctrl->p_new.p_u8);
+		break;
+	case V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING:
+	case V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING:
+		ret = isp_set_ctrl_outss(isp_dev, ctrl->p_new.p_u8);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
@@ -322,12 +857,232 @@ static const struct v4l2_ctrl_ops isp_ctrl_ops = {
 	.s_ctrl = isp_s_ctrl,
 };
 
+struct v4l2_ctrl_config isp_ctrl[] = {
+	[0] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "WB Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_WB_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_wb_setting),
+		.flags		= 0,
+	},
+	[1] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "Car Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_CAR_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_car_setting),
+		.flags		= 0,
+	},
+	[2] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "CCM Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_CCM_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_ccm_setting),
+		.flags		= 0,
+	},
+	[3] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "CFA Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_CFA_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_cfa_setting),
+		.flags		= 0,
+	},
+	[4] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "CTC Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_CTC_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_ctc_setting),
+		.flags		= 0,
+	},
+	[5] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "CTC Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_DBC_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_dbc_setting),
+		.flags		= 0,
+	},
+	[6] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "DNYUV Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_dnyuv_setting),
+		.flags		= 0,
+	},
+	[7] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "DNYUV Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_gmargb_setting),
+		.flags		= 0,
+	},
+	[8] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "LCCF Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_LCCF_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_lccf_setting),
+		.flags		= 0,
+	},
+	[9] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "OBC Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_OBC_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_blacklevel_setting),
+		.flags		= 0,
+	},
+	[10] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "OECF Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_OECF_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_oecf_setting),
+		.flags		= 0,
+	},
+	[11] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "R2Y Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_R2Y_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_r2y_setting),
+		.flags		= 0,
+	},
+	[12] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "SAT Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_SAT_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_sat_setting),
+		.flags		= 0,
+	},
+	[13] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "SAT Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_SHRP_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_sharp_setting),
+		.flags		= 0,
+	},
+	[14] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "YCRV Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_YCRV_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_ycrv_setting),
+		.flags		= 0,
+	},
+	[15] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "SC Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_STAT_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_sc_setting),
+		.flags		= 0,
+	},
+	[16] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "OUTSS Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_outss_setting),
+		.flags		= 0,
+	},
+	[17] = {
+		.ops		= &isp_ctrl_ops,
+		.type		= V4L2_CTRL_TYPE_U8,
+		.def		= 0,
+		.min		= 0x00,
+		.max		= 0xff,
+		.step		= 1,
+		.name		= "OUTSS Setting",
+		.id		= V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING,
+		.dims[0]	= sizeof(struct jh7110_isp_outss_setting),
+		.flags		= 0,
+	},
+};
+
 static int isp_init_controls(struct stf_isp_dev *isp_dev)
 {
 	const struct v4l2_ctrl_ops *ops = &isp_ctrl_ops;
 	struct isp_ctrls *ctrls = &isp_dev->ctrls;
 	struct v4l2_ctrl_handler *hdl = &ctrls->handler;
 	int ret;
+	int i;
 
 	v4l2_ctrl_handler_init(hdl, 32);
 
@@ -378,6 +1133,10 @@ static int isp_init_controls(struct stf_isp_dev *isp_dev)
 				       V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
 				       V4L2_CID_POWER_LINE_FREQUENCY_50HZ);
 
+	for (i = 0; i < ARRAY_SIZE(isp_ctrl); i++)
+		v4l2_ctrl_new_custom(hdl, &isp_ctrl[i], NULL);
+
+
 	if (hdl->error) {
 		ret = hdl->error;
 		goto free_ctrls;
@@ -537,7 +1296,7 @@ static int isp_match_sensor_format_get_index(struct stf_isp_dev *isp_dev)
 
 	st_debug(ST_ISP, "Got sensor format 0x%x !!\n", fmt.format.code);
 
-	formats = &isp_dev->formats[SINK_FORMATS_INDEX];
+	formats = &isp_dev->formats[0];		/* isp sink format */
 	for (idx = 0; idx < formats->nfmts; idx++) {
 		if (formats->fmts[idx].code == fmt.format.code) {
 			st_info(ST_ISP,
@@ -581,11 +1340,10 @@ static void isp_try_format(struct stf_isp_dev *isp_dev,
 	u32 code = fmt->code;
 	u32 bpp;
 
-	switch (pad) {
-	case STF_ISP_PAD_SINK:
+	if (pad == STF_ISP_PAD_SINK) {
 		/* Set format on sink pad */
 
-		formats = &isp_dev->formats[SINK_FORMATS_INDEX];
+		formats = &isp_dev->formats[pad];
 		fmt->width = clamp_t(u32,
 				fmt->width, STFCAMSS_FRAME_MIN_WIDTH,
 				STFCAMSS_FRAME_MAX_WIDTH);
@@ -597,24 +1355,8 @@ static void isp_try_format(struct stf_isp_dev *isp_dev,
 		fmt->field = V4L2_FIELD_NONE;
 		fmt->colorspace = V4L2_COLORSPACE_SRGB;
 		fmt->flags = 0;
-
-		break;
-
-	case STF_ISP_PAD_SRC:
-	case STF_ISP_PAD_SRC_SS0:
-	case STF_ISP_PAD_SRC_SS1:
-		formats = &isp_dev->formats[UO_FORMATS_INDEX];
-		break;
-
-	case STF_ISP_PAD_SRC_ITIW:
-	case STF_ISP_PAD_SRC_ITIR:
-		formats = &isp_dev->formats[ITI_FORMATS_INDEX];
-		break;
-
-	case STF_ISP_PAD_SRC_RAW:
-	case STF_ISP_PAD_SRC_SCD_Y:
-		formats = &isp_dev->formats[RAW_FORMATS_INDEX];
-		break;
+	} else {
+		formats = &isp_dev->formats[pad];
 	}
 
 	i = isp_match_format_get_index(formats, fmt->code, pad);
@@ -682,21 +1424,11 @@ static int isp_enum_mbus_code(struct v4l2_subdev *sd,
 	struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd);
 	const struct isp_format_table *formats;
 
-	if (code->index >= isp_dev->nformats)
+	if (code->index >= isp_dev->formats[code->pad].nfmts)
 		return -EINVAL;
-	if (code->pad == STF_ISP_PAD_SINK) {
-		formats = &isp_dev->formats[SINK_FORMATS_INDEX];
-		code->code = formats->fmts[code->index].code;
-	} else {
-		struct v4l2_mbus_framefmt *sink_fmt;
-
-		sink_fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK,
-					code->which);
 
-		code->code = sink_fmt->code;
-		if (!code->code)
-			return -EINVAL;
-	}
+	formats = &isp_dev->formats[code->pad];
+	code->code = formats->fmts[code->index].code;
 	code->flags = 0;
 
 	return 0;
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_isp.h b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
index 5924503f536e..d740b1fe1aa1 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_isp.h
@@ -18,7 +18,7 @@
 
 #define ISP_SCD_BUFFER_SIZE     (19 * 256 * 4)  // align 128
 #define ISP_YHIST_BUFFER_SIZE   (64 * 4)
-#define ISP_SCD_Y_BUFFER_SIZE   (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)
+#define ISP_SCD_Y_BUFFER_SIZE   (ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE + 2)
 #define ISP_RAW_DATA_BITS       12
 #define SCALER_RATIO_MAX        1  // no compose function
 #define STF_ISP_REG_OFFSET_MAX  0x0FFF
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_video.c b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
index ac8f98a8cec9..8a5e5a846f96 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.c
@@ -340,8 +340,10 @@ static int video_buf_init(struct vb2_buffer *vb)
 	}
 
 	if (stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC)
-		== STF_ISP_PAD_SRC_SCD_Y)
+		== STF_ISP_PAD_SRC_SCD_Y) {
 		buffer->addr[1] = buffer->addr[0] + ISP_YHIST_BUFFER_SIZE;
+		buffer->vaddr_sc = vb2_plane_vaddr(vb, 0);
+	}
 
 	return 0;
 }
@@ -1278,6 +1280,17 @@ int video_s_selection(struct file *file, void *fh,
 	return ret;
 }
 
+static int stf_video_subscribe_event(struct v4l2_fh *fh,
+				   const struct v4l2_event_subscription *sub)
+{
+	switch (sub->type) {
+	case V4L2_EVENT_FRAME_SYNC:
+		return v4l2_event_subscribe(fh, sub, 2, NULL);
+	default:
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	}
+}
+
 static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
 	.vidioc_querycap                = video_querycap,
 	.vidioc_enum_fmt_vid_cap        = video_enum_fmt,
@@ -1302,6 +1315,8 @@ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops = {
 	.vidioc_s_parm                  = video_s_parm,
 	.vidioc_s_selection             = video_s_selection,
 	.vidioc_g_selection             = video_g_selection,
+	.vidioc_subscribe_event 		= stf_video_subscribe_event,
+	.vidioc_unsubscribe_event 		= v4l2_event_unsubscribe,
 };
 
 static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
@@ -1328,6 +1343,8 @@ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_mp = {
 	.vidioc_s_parm                  = video_s_parm,
 	.vidioc_s_selection             = video_s_selection,
 	.vidioc_g_selection             = video_g_selection,
+	.vidioc_subscribe_event 		= stf_video_subscribe_event,
+	.vidioc_unsubscribe_event 		= v4l2_event_unsubscribe,
 };
 
 static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
@@ -1347,6 +1364,8 @@ static const struct v4l2_ioctl_ops stf_vid_ioctl_ops_out = {
 	.vidioc_prepare_buf             = vb2_ioctl_prepare_buf,
 	.vidioc_streamon                = vb2_ioctl_streamon,
 	.vidioc_streamoff               = vb2_ioctl_streamoff,
+	.vidioc_subscribe_event 		= stf_video_subscribe_event,
+	.vidioc_unsubscribe_event 		= v4l2_event_unsubscribe,
 };
 
 static int video_open(struct file *file)
@@ -1511,7 +1530,7 @@ int stf_video_register(struct stfcamss_video *video,
 			V4L2_CAP_VIDEO_CAPTURE;
 		vdev->vfl_dir = VFL_DIR_RX;
 	}
-	vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
+	vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
 	if (video->type == V4L2_CAP_VIDEO_OUTPUT)
 		vdev->ioctl_ops = &stf_vid_ioctl_ops_out;
 	else
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_video.h b/drivers/media/platform/starfive/v4l2_driver/stf_video.h
index c7b6fa7a9dcd..cf99be2df8db 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_video.h
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_video.h
@@ -28,6 +28,7 @@
 struct stfcamss_buffer {
 	struct vb2_v4l2_buffer vb;
 	dma_addr_t addr[3];
+	void *vaddr_sc;		/* Use for isp sc data */
 	struct list_head queue;
 	int sizeimage;
 };
diff --git a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
index 5758ab77d7ea..dc30b018e684 100644
--- a/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
+++ b/drivers/media/platform/starfive/v4l2_driver/stf_vin.c
@@ -9,6 +9,7 @@
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
+#include <media/v4l2-event.h>
 
 #include "stfcamss.h"
 
@@ -1133,6 +1134,9 @@ static void vin_buffer_done(struct vin_line *line, struct vin_params *params)
 	struct vin_output *output = &line->output;
 	unsigned long flags;
 	u64 ts = ktime_get_ns();
+	struct v4l2_event event = {
+		.type = V4L2_EVENT_FRAME_SYNC,
+	};
 
 	if (output->state == VIN_OUTPUT_OFF
 		|| output->state == VIN_OUTPUT_RESERVED)
@@ -1141,6 +1145,25 @@ static void vin_buffer_done(struct vin_line *line, struct vin_params *params)
 	spin_lock_irqsave(&line->output_lock, flags);
 
 	while ((ready_buf = vin_buf_get_ready(output))) {
+		//if (line->id >= VIN_LINE_ISP && line->id <= VIN_LINE_ISP_SS1) {
+		if (line->id == VIN_LINE_ISP_SCD_Y) {
+#define ADDR_REG_YHIST_ACC_0               0x0D00
+			struct stf_vin2_dev *vin_dev = line_to_vin2_dev(line);
+			struct stf_vin_dev *vin = vin_dev->stfcamss->vin;
+			void __iomem *ispbase = vin->isp_base;
+			u32 y_hist_reg_addr = ADDR_REG_YHIST_ACC_0;
+			u32 * y_hist_addr = (u32 *)ready_buf->vaddr_sc;
+			s32 i = 0;
+
+			for(i = 0; i < 64; i++, y_hist_reg_addr += 4)
+				y_hist_addr[i] = reg_read(ispbase, y_hist_reg_addr);
+
+			event.u.frame_sync.frame_sequence = output->sequence;
+			v4l2_event_queue(&(line->video_out.vdev), &event);
+			//v4l2_event_queue(line->subdev.devnode, &event);
+			//pr_info("----------frame sync-----------\n");
+		}
+
 		ready_buf->vb.vb2_buf.timestamp = ts;
 		ready_buf->vb.sequence = output->sequence++;
 
@@ -1234,9 +1257,14 @@ static void vin_change_buffer(struct vin_line *line)
 			scd_type = vin_dev->hw_ops->vin_isp_get_scd_type(vin_dev);
 			ready_buf->vb.flags &= ~(V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME);
 			if (scd_type == AWB_TYPE)
+			{
 				ready_buf->vb.flags |= V4L2_BUF_FLAG_PFRAME;
-			else
+				*((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0xffff;
+			}else{
 				ready_buf->vb.flags |= V4L2_BUF_FLAG_BFRAME;
+				*((u16 *)(ready_buf->vaddr_sc + ISP_SCD_BUFFER_SIZE + ISP_YHIST_BUFFER_SIZE)) = 0;
+			}
+
 			if (!output->frame_skip) {
 				output->frame_skip = ISP_AWB_OECF_SKIP_FRAME;
 				scd_type = scd_type == AWB_TYPE ? OECF_TYPE : AWB_TYPE;
@@ -1393,7 +1421,7 @@ int stf_vin_register(struct stf_vin2_dev *vin_dev, struct v4l2_device *v4l2_dev)
 
 		v4l2_subdev_init(sd, &vin_v4l2_ops);
 		sd->internal_ops = &vin_v4l2_internal_ops;
-		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+		sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
 		snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
 			STF_VIN_NAME, 0, sub_name);
 		v4l2_set_subdevdata(sd, &vin_dev->line[i]);
diff --git a/include/uapi/linux/jh7110-isp.h b/include/uapi/linux/jh7110-isp.h
new file mode 100644
index 000000000000..33e0da35c291
--- /dev/null
+++ b/include/uapi/linux/jh7110-isp.h
@@ -0,0 +1,351 @@
+/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */
+/*
+ * jh7110-isp.h
+ *
+ * JH7110 ISP driver - user space header file.
+ *
+ * Copyright © 2023 Starfive Technology Co., Ltd.
+ *
+ * Author: Su Zejian (zejian.su@starfivetech.com)
+ *
+ */
+
+#ifndef __JH7110_ISP_H_
+#define __JH7110_ISP_H_
+
+#include <linux/v4l2-controls.h>
+
+#define V4L2_CID_USER_JH7110_ISP_BASE				(V4L2_CID_USER_BASE + 0x1170)
+
+#define V4L2_CID_USER_JH7110_ISP_WB_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0001)
+#define V4L2_CID_USER_JH7110_ISP_CAR_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0002)
+#define V4L2_CID_USER_JH7110_ISP_CCM_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0003)
+#define V4L2_CID_USER_JH7110_ISP_CFA_SETTING		\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0004)
+#define V4L2_CID_USER_JH7110_ISP_CTC_SETTING		\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0005)
+#define V4L2_CID_USER_JH7110_ISP_DBC_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0006)
+#define V4L2_CID_USER_JH7110_ISP_DNYUV_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0007)
+#define V4L2_CID_USER_JH7110_ISP_GMARGB_SETTING		\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0008)
+#define V4L2_CID_USER_JH7110_ISP_LCCF_SETTING \
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0009)
+#define V4L2_CID_USER_JH7110_ISP_OBC_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000a)
+#define V4L2_CID_USER_JH7110_ISP_OECF_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000b)
+#define V4L2_CID_USER_JH7110_ISP_R2Y_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000c)
+#define V4L2_CID_USER_JH7110_ISP_SAT_SETTING		\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000d)
+#define V4L2_CID_USER_JH7110_ISP_SHRP_SETTING		\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000e)
+#define V4L2_CID_USER_JH7110_ISP_YCRV_SETTING	\
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x000f)
+#define V4L2_CID_USER_JH7110_ISP_STAT_SETTING \
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0010)
+#define V4L2_CID_USER_JH7110_ISP_OUTSS0_SETTING \
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0011)
+#define V4L2_CID_USER_JH7110_ISP_OUTSS1_SETTING \
+				(V4L2_CID_USER_JH7110_ISP_BASE + 0x0012)
+
+struct jh7110_isp_wb_gain {
+	__u16 gain_r;
+	__u16 gain_g;
+	__u16 gain_b;
+};
+
+struct jh7110_isp_wb_setting {
+	__u32 enabled;
+	struct jh7110_isp_wb_gain gains;
+};
+
+struct jh7110_isp_car_setting {
+	__u32 enabled;
+};
+
+struct jh7110_isp_ccm_smlow {
+	__s32 ccm[3][3];
+	__s32 offsets[3];
+};
+
+struct jh7110_isp_ccm_setting {
+	__u32 enabled;
+	struct jh7110_isp_ccm_smlow ccm_smlow;
+};
+
+struct jh7110_isp_cfa_params {
+	__s32 hv_width;
+	__s32 cross_cov;
+};
+
+struct jh7110_isp_cfa_setting {
+	__u32 enabled;
+	struct jh7110_isp_cfa_params settings;
+};
+
+struct jh7110_isp_ctc_params {
+	__u8 saf_mode;
+	__u8 daf_mode;
+	__s32 max_gt;
+	__s32 min_gt;
+};
+
+struct jh7110_isp_ctc_setting {
+	__u32 enabled;
+	struct jh7110_isp_ctc_params settings;
+};
+
+struct jh7110_isp_dbc_params {
+	__s32 bad_gt;
+	__s32 bad_xt;
+};
+
+struct jh7110_isp_dbc_setting {
+	__u32 enabled;
+	struct jh7110_isp_dbc_params settings;
+};
+
+struct jh7110_isp_dnyuv_params {
+	__u8 y_sweight[10];
+	__u16 y_curve[7];
+	__u8 uv_sweight[10];
+	__u16 uv_curve[7];
+};
+
+struct jh7110_isp_dnyuv_setting {
+	__u32 enabled;
+	struct jh7110_isp_dnyuv_params settings;
+};
+
+struct jh7110_isp_gmargb_point {
+	__u16 g_val;
+	__u16 sg_val;
+};
+
+struct jh7110_isp_gmargb_setting {
+	__u32 enabled;
+	struct jh7110_isp_gmargb_point curve[15];
+};
+
+struct jh7110_isp_lccf_circle {
+	__s16 center_x;
+	__s16 center_y;
+	__u8 radius;
+};
+
+struct jh7110_isp_lccf_curve_param {
+	__s16 f1;
+	__s16 f2;
+};
+
+struct jh7110_isp_lccf_setting {
+	__u32 enabled;
+	struct jh7110_isp_lccf_circle circle;
+	struct jh7110_isp_lccf_curve_param r_param;
+	struct jh7110_isp_lccf_curve_param gr_param;
+	struct jh7110_isp_lccf_curve_param gb_param;
+	struct jh7110_isp_lccf_curve_param b_param;
+};
+
+struct jh7110_isp_blacklevel_win_size {
+	__u32 width;
+	__u32 height;
+};
+
+struct jh7110_isp_blacklevel_gain {
+	__u8 tl_gain;
+	__u8 tr_gain;
+	__u8 bl_gain;
+	__u8 br_gain;
+};
+
+struct jh7110_isp_blacklevel_offset {
+	__u8 tl_offset;
+	__u8 tr_offset;
+	__u8 bl_offset;
+	__u8 br_offset;
+};
+
+struct jh7110_isp_blacklevel_setting {
+	__u32 enabled;
+	struct jh7110_isp_blacklevel_win_size win_size;
+	struct jh7110_isp_blacklevel_gain gain[4];
+	struct jh7110_isp_blacklevel_offset offset[4];
+};
+
+struct jh7110_isp_oecf_point {
+	__u16 x;
+	__u16 y;
+	__s16 slope;
+};
+
+struct jh7110_isp_oecf_setting {
+	__u32 enabled;
+	struct jh7110_isp_oecf_point r_curve[16];
+	struct jh7110_isp_oecf_point gr_curve[16];
+	struct jh7110_isp_oecf_point gb_curve[16];
+	struct jh7110_isp_oecf_point b_curve[16];
+};
+
+struct jh7110_isp_r2y_matrix {
+	__s16 m[9];
+};
+
+struct jh7110_isp_r2y_setting {
+	__u32 enabled;
+	struct jh7110_isp_r2y_matrix matrix;
+};
+
+struct jh7110_isp_sat_curve {
+	__s16 yi_min;
+	__s16 yo_ir;
+	__s16 yo_min;
+	__s16 yo_max;
+};
+
+struct jh7110_isp_sat_hue_info {
+	__s16 cos;
+	__s16 sin;
+};
+
+struct jh7110_isp_sat_info {
+	__s16 gain_cmab;
+	__s16 gain_cmmd;
+	__s16 threshold_cmb;
+	__s16 threshold_cmd;
+	__s16 offset_u;
+	__s16 offset_v;
+	__s16 cmsf;
+};
+
+struct jh7110_isp_sat_setting {
+	__u32 enabled;
+	struct jh7110_isp_sat_curve curve;
+	struct jh7110_isp_sat_hue_info hue_info;
+	struct jh7110_isp_sat_info sat_info;
+};
+
+struct jh7110_isp_sharp_weight {
+	__u8 weight[15];
+	__u32 recip_wei_sum;
+};
+
+struct jh7110_isp_sharp_strength {
+	__s16 diff[4];
+	__s16 f[3];
+	__s32 s[3];
+};
+
+struct jh7110_isp_sharp_setting {
+	__u32 enabled;
+	struct jh7110_isp_sharp_weight weight;
+	struct jh7110_isp_sharp_strength strength;
+	__s8 pdirf;
+	__s8 ndirf;
+};
+
+struct jh7110_isp_ycrv_curve {
+	__s16 y[64];
+};
+
+struct jh7110_isp_ycrv_setting {
+	__u32 enabled;
+	struct jh7110_isp_ycrv_curve curve;
+};
+
+struct jh7110_isp_sc_config {
+	__u16 h_start;
+	__u16 v_start;
+	__u8 sw_width;
+	__u8 sw_height;
+	__u8 hperiod;
+	__u8 hkeep;
+	__u8 vperiod;
+	__u8 vkeep;
+};
+
+struct jh7110_isp_sc_af_config {
+	__u8 es_hor_mode;
+	__u8 es_sum_mode;
+	__u8 hor_en;
+	__u8 ver_en;
+	__u8 es_ver_thr;
+	__u16 es_hor_thr;
+};
+
+struct jh7110_isp_sc_awb_ps {
+	__u8 awb_ps_rl;
+	__u8 awb_ps_ru;
+	__u8 awb_ps_gl;
+	__u8 awb_ps_gu;
+	__u8 awb_ps_bl;
+	__u8 awb_ps_bu;
+	__u8 awb_ps_yl;
+	__u8 awb_ps_yu;
+	__u16 awb_ps_grl;
+	__u16 awb_ps_gru;
+	__u16 awb_ps_gbl;
+	__u16 awb_ps_gbu;
+	__u16 awb_ps_grbl;
+	__u16 awb_ps_grbu;
+};
+
+struct jh7110_isp_sc_awb_ws {
+	__u8 awb_ws_rl;
+	__u8 awb_ws_ru;
+	__u8 awb_ws_grl;
+	__u8 awb_ws_gru;
+	__u8 awb_ws_gbl;
+	__u8 awb_ws_gbu;
+	__u8 awb_ws_bl;
+	__u8 awb_ws_bu;
+};
+
+
+struct jh7110_isp_sc_awb_point {
+	__u16 intensity;
+	__u8 weight;
+};
+
+struct jh7110_isp_sc_awb_config {
+	struct jh7110_isp_sc_awb_ps ws_ps_config;
+	__u8 awb_ps_grb_ba;
+	__u8 sel;
+	struct jh7110_isp_sc_awb_ws ws_config;
+	__u8 awb_cw[169];
+	struct jh7110_isp_sc_awb_point pts[17];
+};
+
+struct jh7110_isp_sc_setting {
+	__u32 enabled;
+	struct jh7110_isp_sc_config crop_config;
+	struct jh7110_isp_sc_af_config af_config;
+	struct jh7110_isp_sc_awb_config awb_config;
+};
+
+struct jh7110_isp_outss_setting {
+	__u8 which;
+	__u16 stride;	// Output Image Stride Register, 8-byte(64bit) granularity.
+	__u8 hsm;		// horizontal scale mode
+	__u32 hsf;		// horizontal scale factor (time 4096)
+	__u8 vsm;		// vertical scale mode
+	__u32 vsf;		// vertical scale factor (time 4096)
+};
+
+struct jh7110_isp_sc_buffer {
+	__u32 y_histogram[64];
+	__u32 reserv0[33];
+	__u32 bright_sc[4096];
+	__u32 reserv1[96];
+	__u32 ae_hist_y[128];
+	__u32 reserv2[511];
+	__u16 flag;
+};
+
+#endif
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 5532b5f68493..efc2e53b84a9 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -212,6 +212,12 @@ enum v4l2_colorfx {
  */
 #define V4L2_CID_USER_CCS_BASE			(V4L2_CID_USER_BASE + 0x10f0)
 
+/*
+ * The base for the jh7110-isp driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_JH7110_ISP_BASE		(V4L2_CID_USER_BASE + 0x1170)
+
 /* MPEG-class control IDs */
 /* The MPEG controls are applicable to all codec controls
  * and the 'MPEG' part of the define is historical */