rv1126 imx307修改驱动使能图像垂直反转180度

news2024/11/26 6:54:51

查看imx307芯片手册

修改,3007地址的bit0为1

修改imx307驱动代码加入这一句代码,直接写死,这样拍出来的照片就使垂直翻转180度的了

// SPDX-License-Identifier: GPL-2.0
/*
 * imx307 driver
 *
 * Copyright (C) 2020 Rockchip Electronics Co., Ltd.
 * v1.0x01.0x01 support lvds interface,include linear and hdr transmission via vipcap
 * support mipi linear mode
 * v1.0x01.0x02
 * 1.fixed lvds output data offset, because lvds regards ob line as valid data output
 * 2.support test pattern
 * v1.0x01.0x03 update frame rate from 25fps to 30fps
 * v1.0x01.0x04 update max exposure and formula
 *	shs1 = vts - (line + 1)
 * V0.0X01.0X05 add quick stream on/off
 * V0.0X01.0X06 support lvds 2lane
 */

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/rk-camera-module.h>
#include <linux/of_graph.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>
#include <linux/pinctrl/consumer.h>
#include <linux/rk-preisp.h>

#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x06)
#ifndef V4L2_CID_DIGITAL_GAIN
#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
#endif

#define IMX307_LINK_FREQ_111M		111370000
#define IMX307_LINK_FREQ_222M		222750000
#define IMX307_2LANES			2
#define IMX307_4LANES			4
#define IMX307_BITS_PER_SAMPLE		10

/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
#define IMX307_PIXEL_RATE_NORMAL (IMX307_LINK_FREQ_111M * 2 / 10 * IMX307_4LANES)
#define IMX307_PIXEL_RATE_HDR (IMX307_LINK_FREQ_222M * 2 / 10 * IMX307_4LANES)

#define IMX307_XVCLK_FREQ		37125000

#define CHIP_ID				0xb2
#define IMX307_REG_CHIP_ID		0x301e

#define IMX307_REG_CTRL_MODE		0x3000
#define IMX307_MODE_SW_STANDBY		0x1
#define IMX307_MODE_STREAMING		0x0

#define IMX307_REG_SHS1_H		0x3022
#define IMX307_REG_SHS1_M		0x3021
#define IMX307_REG_SHS1_L		0x3020

#define IMX307_REG_SHS2_H		0x3026
#define IMX307_REG_SHS2_M		0x3025
#define IMX307_REG_SHS2_L		0x3024

#define IMX307_REG_RHS1_H		0x3032
#define IMX307_REG_RHS1_M		0x3031
#define IMX307_REG_RHS1_L		0x3030

#define IMX307_FETCH_HIGH_BYTE_EXP(VAL)	(((VAL) >> 16) & 0x0F)
#define IMX307_FETCH_MID_BYTE_EXP(VAL)	(((VAL) >> 8) & 0xFF)
#define IMX307_FETCH_LOW_BYTE_EXP(VAL)	((VAL) & 0xFF)

#define	IMX307_EXPOSURE_MIN		2
#define	IMX307_EXPOSURE_STEP		1
#define IMX307_VTS_MAX			0x7fff

#define IMX307_GAIN_SWITCH_REG		0x3009
#define IMX307_REG_LF_GAIN		0x3014
#define IMX307_REG_SF_GAIN		0x30f2
#define IMX307_GAIN_MIN			0x00
#define IMX307_GAIN_MAX			0xee
#define IMX307_GAIN_STEP		1
#define IMX307_GAIN_DEFAULT		0x00

#define IMX307_GROUP_HOLD_REG		0x3001
#define IMX307_GROUP_HOLD_START		0x01
#define IMX307_GROUP_HOLD_END		0x00

#define USED_TEST_PATTERN
#ifdef USED_TEST_PATTERN
#define IMX307_REG_TEST_PATTERN		0x308c
#define	IMX307_TEST_PATTERN_ENABLE	BIT(0)
#endif

#define IMX307_REG_VTS_H		0x301a
#define IMX307_REG_VTS_M		0x3019
#define IMX307_REG_VTS_L		0x3018
#define IMX307_FETCH_HIGH_BYTE_VTS(VAL)	(((VAL) >> 16) & 0x03)
#define IMX307_FETCH_MID_BYTE_VTS(VAL)	(((VAL) >> 8) & 0xFF)
#define IMX307_FETCH_LOW_BYTE_VTS(VAL)	((VAL) & 0xFF)

#define REG_NULL			0xFFFF
#define REG_DELAY			0xFFFE

#define IMX307_REG_VALUE_08BIT		1
#define IMX307_REG_VALUE_16BIT		2
#define IMX307_REG_VALUE_24BIT		3

static bool g_isHCG;

#define IMX307_NAME			"imx307"

#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"

#define IMX307_FLIP_REG			0x3007
#define MIRROR_BIT_MASK			BIT(1)
#define FLIP_BIT_MASK			BIT(0)

#define RHS1 0X0B

static const char * const imx307_supply_names[] = {
	"avdd",		/* Analog power */
	"dovdd",	/* Digital I/O power */
	"dvdd",		/* Digital core power */
};

#define IMX307_NUM_SUPPLIES ARRAY_SIZE(imx307_supply_names)

struct regval {
	u16 addr;
	u8 val;
};

struct imx307_mode {
	u32 bus_fmt;
	u32 width;
	u32 height;
	struct v4l2_fract max_fps;
	u32 hts_def;
	u32 vts_def;
	u32 exp_def;
	const struct regval *reg_list;
	u32 hdr_mode;
	struct rkmodule_lvds_cfg lvds_cfg;
	u32 freq_idx;
	u32 lanes;
	u32 bpp;
};

struct imx307 {
	struct i2c_client	*client;
	struct clk		*xvclk;
	struct gpio_desc	*reset_gpio;
	struct gpio_desc	*pwdn_gpio;
	struct regulator_bulk_data supplies[IMX307_NUM_SUPPLIES];

	struct pinctrl		*pinctrl;
	struct pinctrl_state	*pins_default;
	struct pinctrl_state	*pins_sleep;

	struct v4l2_subdev	subdev;
	struct media_pad	pad;
	struct v4l2_ctrl_handler ctrl_handler;
	struct v4l2_ctrl	*exposure;
	struct v4l2_ctrl	*anal_gain;
	struct v4l2_ctrl	*digi_gain;
	struct v4l2_ctrl	*hblank;
	struct v4l2_ctrl	*vblank;
	struct v4l2_ctrl	*pixel_rate;
	struct v4l2_ctrl	*link_freq;
	struct v4l2_ctrl	*h_flip;
	struct v4l2_ctrl	*v_flip;
#ifdef USED_TEST_PATTERN
	struct v4l2_ctrl	*test_pattern;
#endif
	struct mutex		mutex;
	bool			streaming;
	bool			power_on;
	const struct imx307_mode *support_modes;
	u32			support_modes_num;
	const struct imx307_mode *cur_mode;
	u32			module_index;
	const char		*module_facing;
	const char		*module_name;
	const char		*len_name;
	u32			cur_vts;
	bool			has_init_exp;
	struct preisp_hdrae_exp_s init_hdrae_exp;
	struct v4l2_fwnode_endpoint bus_cfg;
	u8			flip;
};

#define to_imx307(sd) container_of(sd, struct imx307, subdev)

/*
 * Xclk 37.125Mhz
 */
static const struct regval imx307_global_regs[] = {
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 30fps
 * lvds_datarate per lane 111Mbps 2 lane
 */
static const struct regval imx307_linear_1920x1080_30fps_lvds_2lane_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x00},
	//{0x3007, 0x01},
	{0x3009, 0x02},
	{0x300a, 0x3c},
	{0x3010, 0x21},
	{0x3011, 0x0a},
	{0x3018, 0x65},
	{0x3019, 0x04},
	{0x301c, 0x30},
	{0x301d, 0x11},
	{0x3046, 0xD0},
	{0x304b, 0x0a},
	{0x305c, 0x18},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x1d},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3480, 0x49},
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 15fps
 * lvds_datarate per lane 222Mbps 2 lane
 */
static const struct regval imx307_hdr2_1920x1080_lvds_2lane_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x00},
	//{0x3007, 0x01},
	{0x3009, 0x02},
	{0x300a, 0x3c},
	{0x300c, 0x11},
	{0x3010, 0x21},
	{0x3011, 0x0a},
	{0x3014, 0x0f},
	{0x3018, 0x65},/* VMAX L */
	{0x3019, 0x04},/* VMAX M */
	{0x301c, 0x30},/* HMAX L */
	{0x301d, 0x11},/* HMAX H */
	{0x3020, 0x02},//hdr+ shs1 l  short
	{0x3021, 0x00},//hdr+ shs1 m
	{0x3024, 0x49},//hdr+ shs2 l
	{0x3025, 0x04},//hdr+ shs2 m
	{0x3030, RHS1},//hdr+ IMX327_RHS1
	{0x3031, 0x00},//hdr+IMX327_RHS1
	{0x3045, 0x03},//hdr+
	{0x3046, 0xd0},
	{0x305c, 0x18},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x30d2, 0x19},
	{0x30d7, 0x03},
	{0x3106, 0x10},
	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x1d},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3480, 0x49},
	{0x31a0, 0xb4},
	{0x31a1, 0x02},
	{0x303c, 0x04},//Y offset
	{0x303d, 0x00},
	{0x303e, 0x41},
	{0x303f, 0x04},//height
	{0x303A, 0x08},//hdr+
	{0x3010, 0x61},//hdr+ gain 1frame FPGC
	{0x3014, 0x00},//hdr+ gain 1frame long
	{0x30F0, 0x64},//hdr+ gain 2frame FPGC
	{0x30f2, 0x00},//hdr+ gain 2frame short
	{0x3002, 0x00},
	{0x304B, 0x0a},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 30fps
 * lvds_datarate per lane 222.75Mbps 4 lane
 */
static const struct regval imx307_linear_1920x1080_30fps_lvds_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x00},
	//{0x3007, 0x01},
	{0x3009, 0x02},
	{0x300a, 0x3c},
	{0x3010, 0x21},
	{0x3011, 0x0a},
	{0x3018, 0x65},
	{0x3019, 0x04},
	{0x301c, 0x30},
	{0x301d, 0x11},
	{0x3046, 0xe0},
	{0x304b, 0x0a},
	{0x305c, 0x18},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x1d},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3480, 0x49},
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 60fps
 * lvds_datarate per lane 445.5Mbps 4 lane
 */
static const struct regval imx307_linear_1920x1080_60fps_lvds_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x00},
	//{0x3007, 0x01},
	{0x3009, 0x01},
	{0x300a, 0x3c},
	{0x3010, 0x21},
	{0x3011, 0x0a},
	{0x3018, 0x65},
	{0x3019, 0x04},
	{0x301c, 0x98},
	{0x301d, 0x08},
	{0x3046, 0xe0},
	{0x304b, 0x0a},
	{0x305c, 0x18},
	{0x305d, 0x00},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x1d},
	{0x313b, 0x41},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3480, 0x49},
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 30fps
 * lvds_datarate per lane 445.5Mbps 4 lane
 */
static const struct regval imx307_hdr2_1920x1080_lvds_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x40},
	//{0x3007, 0x41},
	{0x3009, 0x01},
	{0x300a, 0x3c},
	{0x300c, 0x11},
	{0x3011, 0x02},
	{0x3018, 0xc4},/* VMAX L */
	{0x3019, 0x04},/* VMAX M */
	{0x301c, 0xec},/* HMAX L */
	{0x301d, 0x07},/* HMAX H */
	{0x3020, 0x02},//hdr+ shs1 l  short
	{0x3021, 0x00},//hdr+ shs1 m
	{0x3024, 0xc9},//hdr+ shs2 l
	{0x3025, 0x07},//hdr+ shs2 m
	{0x3030, 0xe1},//hdr+ IMX327_RHS1
	{0x3031, 0x00},//hdr+IMX327_RHS1
	{0x3045, 0x03},//hdr+
	{0x3046, 0xe0},
	{0x304b, 0x0a},
	{0x305c, 0x18},
	{0x305d, 0x03},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x30d2, 0x19},
	{0x30d7, 0x03},
	{0x3106, 0x11},
	{0x3129, 0x1d},
	{0x313b, 0x61},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3414, 0x00},
	{0x3415, 0x00},
	{0x3480, 0x49},
	{0x31a0, 0xb4},
	{0x31a1, 0x02},
	{0x303c, 0x04},//Y offset
	{0x303d, 0x00},
	{0x303e, 0x41},
	{0x303f, 0x04},//height
	{0x303A, 0x08},//hdr+
	{0x3010, 0x61},//hdr+ gain 1frame FPGC
	{0x3014, 0x00},//hdr+ gain 1frame long
	{0x30F0, 0x64},//hdr+ gain 2frame FPGC
	{0x30f2, 0x00},//hdr+ gain 2frame short
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 30fps
 * mipi_datarate per lane 222.75Mbps 4 lane
 */
static const struct regval imx307_linear_1920x1080_mipi_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x00},
	//{0x3007, 0x01},
	{0x3009, 0x02},
	{0x300A, 0x3c},
	{0x3010, 0x21},
	{0x3011, 0x0a},
	{0x3018, 0x65},
	{0x3019, 0x04},
	{0x301C, 0x30},
	{0x301D, 0x11},
	{0x3046, 0x00},
	{0x304B, 0x0A},
	{0x305C, 0x18},
	{0x305D, 0x03},
	{0x305E, 0x20},
	{0x305F, 0x01},
	{0x309E, 0x4A},
	{0x309F, 0x4A},
	{0x311c, 0x0e},
	{0x3128, 0x04},
	{0x3129, 0x1d},
	{0x313B, 0x41},
	{0x315E, 0x1A},
	{0x3164, 0x1A},
	{0x317C, 0x12},
	{0x31EC, 0x37},
	{0x3405, 0x20},
	{0x3407, 0x03},
	{0x3414, 0x0A},
	{0x3418, 0x49},
	{0x3419, 0x04},
	{0x3441, 0x0a},
	{0x3442, 0x0a},
	{0x3443, 0x03},
	{0x3444, 0x20},
	{0x3445, 0x25},
	{0x3446, 0x47},
	{0x3447, 0x00},
	{0x3448, 0x1f},
	{0x3449, 0x00},
	{0x344A, 0x17},
	{0x344B, 0x00},
	{0x344C, 0x0F},
	{0x344D, 0x00},
	{0x344E, 0x17},
	{0x344F, 0x00},
	{0x3450, 0x47},
	{0x3451, 0x00},
	{0x3452, 0x0F},
	{0x3453, 0x00},
	{0x3454, 0x0f},
	{0x3455, 0x00},
	{0x3472, 0x9c},
	{0x3473, 0x07},
	{0x3480, 0x49},
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * Xclk 37.125Mhz
 * max_framerate 30fps
 * mipi_datarate per lane 445.5Mbps 4 lane
 */
static const struct regval imx307_hdr2_1920x1080_mipi_regs[] = {
	{0x3003, 0x01},
	{REG_DELAY, 0x10},
	{0x3000, 0x01},
	{0x3001, 0x00},
	{0x3002, 0x01},
	{0x3005, 0x00},
	{0x3007, 0x40},
	//{0x3007, 0x41},
	{0x3009, 0x01},
	{0x300a, 0x3c},
	{0x300c, 0x11}, //hdr+
	{0x3011, 0x02},
	{0x3018, 0xc4},/* VMAX L */
	{0x3019, 0x04},/* VMAX M */
	{0x301a, 0x00},
	{0x301c, 0xEc},/* HMAX L */
	{0x301d, 0x07},/* HMAX H */
	{0x3045, 0x05},//hdr+
	{0x3046, 0x00},
	{0x304b, 0x0a},
	{0x305c, 0x18},
	{0x305d, 0x03},
	{0x305e, 0x20},
	{0x305f, 0x01},
	{0x309e, 0x4a},
	{0x309f, 0x4a},
	{0x30d2, 0x19},
	{0x30d7, 0x03},
	{0x3106, 0x11},//hdr+
	{0x3129, 0x1d},
	{0x313b, 0x61},
	{0x315e, 0x1a},
	{0x3164, 0x1a},
	{0x317c, 0x12},
	{0x31ec, 0x37},
	{0x3405, 0x10},
	{0x3407, 0x03},
	{0x3414, 0x00},
	{0x3415, 0x00},//hdr+
	{0x3418, 0x72},
	{0x3419, 0x09},
	{0x3441, 0x0a},
	{0x3442, 0x0a},
	{0x3443, 0x03},
	{0x3444, 0x20},
	{0x3445, 0x25},
	{0x3446, 0x57},
	{0x3447, 0x00},
	{0x3448, 0x37},//37?
	{0x3449, 0x00},
	{0x344a, 0x1f},
	{0x344b, 0x00},
	{0x344c, 0x1f},
	{0x344d, 0x00},
	{0x344e, 0x1f},
	{0x344f, 0x00},
	{0x3450, 0x77},
	{0x3451, 0x00},
	{0x3452, 0x1f},
	{0x3453, 0x00},
	{0x3454, 0x17},
	{0x3455, 0x00},
	{0x3472, 0xa0},
	{0x3473, 0x07},
	{0x347b, 0x23},
	{0x3480, 0x49},
	{0x31a0, 0xb4},//hdr+
	{0x31a1, 0x02},//hdr+
	{0x3020, 0x02},//hdr+ shs1 l  short
	{0x3021, 0x00},//hdr+ shs1 m
	{0x3022, 0x00},//hdr+ shs1 h
	{0x3030, 0xe1},//hdr+ IMX307_RHS1
	{0x3031, 0x00},//hdr+IMX307_RHS1
	{0x3032, 0x00},//hdr+
	{0x31A0, 0xe8},//hdr+ HBLANK1
	{0x31A1, 0x01},//hdr+
	{0x303c, 0x04},
	{0x303d, 0x00},
	{0x303e, 0x41},
	{0x303f, 0x04},
	{0x303A, 0x08},//hdr+
	{0x3024, 0xc9},//hdr+ shs2 l
	{0x3025, 0x06},//hdr+ shs2 m
	{0x3026, 0x00},//hdr+ shs2 h
	{0x3010, 0x61},//hdr+ gain 1frame FPGC
	{0x3014, 0x00},//hdr+ gain 1frame long
	{0x30F0, 0x64},//hdr+ gain 2frame FPGC
	{0x30f2, 0x00},//hdr+ gain 2frame short
	{0x3002, 0x00},
	{REG_NULL, 0x00},
};

/*
 * The width and height must be configured to be
 * the same as the current output resolution of the sensor.
 * The input width of the isp needs to be 16 aligned.
 * The input height of the isp needs to be 8 aligned.
 * If the width or height does not meet the alignment rules,
 * you can configure the cropping parameters with the following function to
 * crop out the appropriate resolution.
 * struct v4l2_subdev_pad_ops {
 *	.get_selection
 * }
 */

static const struct imx307_mode lvds_2lane_supported_modes[] = {
	{
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1110,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.exp_def = 0x03fe,
		.hts_def = 0x1130,
		.vts_def = 0x0465,
		.reg_list = imx307_linear_1920x1080_30fps_lvds_2lane_regs,
		.hdr_mode = NO_HDR,
		.lanes = 2,
		.freq_idx = 0,
		.bpp = 10,
		.lvds_cfg = {
			.mode = LS_FIRST,
			.frm_sync_code[LVDS_CODE_GRP_LINEAR] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x200,
						.eav = 0x274,
					},
					.blk = {
						.sav = 0x2ac,
						.eav = 0x2d8,
					},
				},
			},
		},
	},
	{
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1098,
		.max_fps = {
			.numerator = 10000,
			.denominator = 150000,
		},
		.exp_def = 0x0473,
		.hts_def = 0x07ec,
		.vts_def = 0x04c4 * 2,
		.reg_list = imx307_hdr2_1920x1080_lvds_2lane_regs,
		.hdr_mode = HDR_X2,
		.lanes = 2,
		.freq_idx = 1,
		.bpp = 10,
		.lvds_cfg = {
			.mode  = SONY_DOL_HDR_1,
			.frm_sync_code[LVDS_CODE_GRP_LONG] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x001,
						.eav = 0x075,
					},
					.blk = {
						.sav = 0x0ac,
						.eav = 0x0d8,
					},
				},
				.even_sync_code = {
					.act = {
						.sav = 0x101,
						.eav = 0x175,
					},
					.blk = {
						.sav = 0x1ac,
						.eav = 0x1d8,
					},
				},
			},
			.frm_sync_code[LVDS_CODE_GRP_SHORT] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x002,
						.eav = 0x076,
					},
					.blk = {
						.sav = 0x0ac,
						.eav = 0x0d8,
					},
				},
				.even_sync_code = {
					.act = {
						.sav = 0x102,
						.eav = 0x176,
					},
					.blk = {
						.sav = 0x1ac,
						.eav = 0x1d8,
					},
				},
			},
		},
	},
};

static const struct imx307_mode lvds_supported_modes[] = {
	{
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1110,
		.max_fps = {
			.numerator = 10000,
			.denominator = 600000,
		},
		.exp_def = 0x03fe,
		.hts_def = 0x0889,
		.vts_def = 0x0465,
		.reg_list = imx307_linear_1920x1080_60fps_lvds_regs,
		.hdr_mode = NO_HDR,
		.lanes = 4,
		.freq_idx = 0,
		.bpp = 10,
		.lvds_cfg = {
			.mode = LS_FIRST,
			.frm_sync_code[LVDS_CODE_GRP_LINEAR] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x200,
						.eav = 0x274,
					},
					.blk = {
						.sav = 0x2ac,
						.eav = 0x2d8,
					},
				},
			},
		},
	}, {
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1110,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.exp_def = 0x03fe,
		.hts_def = 0x1130,
		.vts_def = 0x0465,
		.reg_list = imx307_linear_1920x1080_30fps_lvds_regs,
		.hdr_mode = NO_HDR,
		.lanes = 4,
		.freq_idx = 0,
		.bpp = 10,
		.lvds_cfg = {
			.mode = LS_FIRST,
			.frm_sync_code[LVDS_CODE_GRP_LINEAR] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x200,
						.eav = 0x274,
					},
					.blk = {
						.sav = 0x2ac,
						.eav = 0x2d8,
					},
				},
			},
		},
	},
	{
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1098,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.exp_def = 0x0473,
		.hts_def = 0x07ec,
		.vts_def = 0x04c4 * 2,
		.reg_list = imx307_hdr2_1920x1080_lvds_regs,
		.hdr_mode = HDR_X2,
		.lanes = 4,
		.freq_idx = 1,
		.bpp = 10,
		.lvds_cfg = {
			.mode  = SONY_DOL_HDR_1,
			.frm_sync_code[LVDS_CODE_GRP_LONG] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x001,
						.eav = 0x075,
					},
					.blk = {
						.sav = 0x0ac,
						.eav = 0x0d8,
					},
				},
				.even_sync_code = {
					.act = {
						.sav = 0x101,
						.eav = 0x175,
					},
					.blk = {
						.sav = 0x1ac,
						.eav = 0x1d8,
					},
				},
			},
			.frm_sync_code[LVDS_CODE_GRP_SHORT] = {
				.odd_sync_code = {
					.act = {
						.sav = 0x002,
						.eav = 0x076,
					},
					.blk = {
						.sav = 0x0ac,
						.eav = 0x0d8,
					},
				},
				.even_sync_code = {
					.act = {
						.sav = 0x102,
						.eav = 0x176,
					},
					.blk = {
						.sav = 0x1ac,
						.eav = 0x1d8,
					},
				},
			},
		},
	},
};

static const struct imx307_mode mipi_supported_modes[] = {
	{
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1948,
		.height = 1097,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.exp_def = 0x03fe,
		.hts_def = 0x1130,
		.vts_def = 0x0465,
		.reg_list = imx307_linear_1920x1080_mipi_regs,
		.hdr_mode = NO_HDR,
		.lanes = 4,
		.freq_idx = 0,
		.bpp = 10,
	}, {
		.bus_fmt = MEDIA_BUS_FMT_SRGGB10_1X10,
		.width = 1952,
		.height = 1089,
		.max_fps = {
			.numerator = 10000,
			.denominator = 300000,
		},
		.exp_def = 0x0473,
		.hts_def = 0x07ec,
		.vts_def = 0x04c4 * 2,
		.reg_list = imx307_hdr2_1920x1080_mipi_regs,
		.hdr_mode = HDR_X2,
		.lanes = 4,
		.freq_idx = 1,
		.bpp = 10,
	},
};

static const s64 link_freq_menu_items[] = {
	IMX307_LINK_FREQ_111M,
	IMX307_LINK_FREQ_222M
};

#ifdef USED_TEST_PATTERN
static const char * const imx307_test_pattern_menu[] = {
	"Disabled",
	"Bar Type 1",
	"Bar Type 2",
	"Bar Type 3",
	"Bar Type 4",
	"Bar Type 5",
	"Bar Type 6",
	"Bar Type 7",
	"Bar Type 8",
	"Bar Type 9",
	"Bar Type 10",
	"Bar Type 11",
	"Bar Type 12",
	"Bar Type 13",
	"Bar Type 14",
	"Bar Type 15"
};
#endif

/* Write registers up to 4 at a time */
static int imx307_write_reg(struct i2c_client *client, u16 reg,
			    u32 len, u32 val)
{
	u32 buf_i, val_i;
	u8 buf[6];
	u8 *val_p;
	__be32 val_be;

	if (len > 4)
		return -EINVAL;

	buf[0] = reg >> 8;
	buf[1] = reg & 0xff;

	val_be = cpu_to_be32(val);
	val_p = (u8 *)&val_be;
	buf_i = 2;
	val_i = 4 - len;

	while (val_i < 4)
		buf[buf_i++] = val_p[val_i++];

	if (i2c_master_send(client, buf, len + 2) != len + 2)
		return -EIO;

	return 0;
}

static int imx307_write_array(struct i2c_client *client,
			      const struct regval *regs)
{
	u32 i;
	int ret = 0;

	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
		if (unlikely(regs[i].addr == REG_DELAY))
			usleep_range(regs[i].val * 1000, regs[i].val * 2000);
		else
			ret = imx307_write_reg(client, regs[i].addr,
				IMX307_REG_VALUE_08BIT,
				regs[i].val);

	return ret;
}

/* Read registers up to 4 at a time */
static int imx307_read_reg(struct i2c_client *client, u16 reg,
			   unsigned int len, u32 *val)
{
	struct i2c_msg msgs[2];
	u8 *data_be_p;
	__be32 data_be = 0;
	__be16 reg_addr_be = cpu_to_be16(reg);
	int ret;

	if (len > 4 || !len)
		return -EINVAL;

	data_be_p = (u8 *)&data_be;
	/* Write register address */
	msgs[0].addr = client->addr;
	msgs[0].flags = 0;
	msgs[0].len = 2;
	msgs[0].buf = (u8 *)&reg_addr_be;

	/* Read data from register */
	msgs[1].addr = client->addr;
	msgs[1].flags = I2C_M_RD;
	msgs[1].len = len;
	msgs[1].buf = &data_be_p[4 - len];

	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
	if (ret != ARRAY_SIZE(msgs))
		return -EIO;

	*val = be32_to_cpu(data_be);
	return 0;
}

static int imx307_get_reso_dist(const struct imx307_mode *mode,
				 struct v4l2_mbus_framefmt *framefmt)
{
	return abs(mode->width - framefmt->width) +
	       abs(mode->height - framefmt->height);
}

static const struct imx307_mode *
imx307_find_best_fit(struct imx307 *imx307, struct v4l2_subdev_format *fmt)
{
	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
	int dist;
	int cur_best_fit = 0;
	int cur_best_fit_dist = -1;
	unsigned int i;

	for (i = 0; i < imx307->support_modes_num; i++) {
		dist = imx307_get_reso_dist(&imx307->support_modes[i], framefmt);
		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
			cur_best_fit_dist = dist;
			cur_best_fit = i;
		}
	}
	return &imx307->support_modes[cur_best_fit];
}

static int imx307_set_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct imx307 *imx307 = to_imx307(sd);
	const struct imx307_mode *mode;
	s64 h_blank, vblank_def;
	s32 dst_link_freq = 0;
	s64 dst_pixel_rate = 0;

	mutex_lock(&imx307->mutex);

	mode = imx307_find_best_fit(imx307, fmt);
	fmt->format.code = mode->bus_fmt;
	fmt->format.width = mode->width;
	fmt->format.height = mode->height;
	fmt->format.field = V4L2_FIELD_NONE;
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
#else
		mutex_unlock(&imx307->mutex);
		return -ENOTTY;
#endif
	} else {
		imx307->cur_mode = mode;
		h_blank = mode->hts_def - mode->width;
		__v4l2_ctrl_modify_range(imx307->hblank, h_blank,
					 h_blank, 1, h_blank);
		vblank_def = mode->vts_def - mode->height;
		__v4l2_ctrl_modify_range(imx307->vblank, vblank_def,
					 IMX307_VTS_MAX - mode->height,
					 1, vblank_def);
		dst_link_freq = mode->freq_idx;
		dst_pixel_rate = (u32)link_freq_menu_items[mode->freq_idx] / mode->bpp * 2 * mode->lanes;
		__v4l2_ctrl_s_ctrl_int64(imx307->pixel_rate,
					 dst_pixel_rate);
		__v4l2_ctrl_s_ctrl(imx307->link_freq,
				   dst_link_freq);
		imx307->cur_vts = mode->vts_def;
	}

	mutex_unlock(&imx307->mutex);

	return 0;
}

static int imx307_get_fmt(struct v4l2_subdev *sd,
			  struct v4l2_subdev_pad_config *cfg,
			  struct v4l2_subdev_format *fmt)
{
	struct imx307 *imx307 = to_imx307(sd);
	const struct imx307_mode *mode = imx307->cur_mode;

	mutex_lock(&imx307->mutex);
	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
#else
		mutex_unlock(&imx307->mutex);
		return -ENOTTY;
#endif
	} else {
		fmt->format.width = mode->width;
		fmt->format.height = mode->height;
		fmt->format.code = mode->bus_fmt;
		fmt->format.field = V4L2_FIELD_NONE;
	}
	mutex_unlock(&imx307->mutex);
	return 0;
}

static int imx307_enum_mbus_code(struct v4l2_subdev *sd,
				 struct v4l2_subdev_pad_config *cfg,
				 struct v4l2_subdev_mbus_code_enum *code)
{
	struct imx307 *imx307 = to_imx307(sd);
	const struct imx307_mode *mode = imx307->cur_mode;

	if (code->index != 0)
		return -EINVAL;
	code->code = mode->bus_fmt;

	return 0;
}

static int imx307_enum_frame_sizes(struct v4l2_subdev *sd,
				   struct v4l2_subdev_pad_config *cfg,
				   struct v4l2_subdev_frame_size_enum *fse)
{
	struct imx307 *imx307 = to_imx307(sd);

	if (fse->index >= imx307->support_modes_num)
		return -EINVAL;

	if (fse->code != imx307->support_modes[fse->index].bus_fmt)
		return -EINVAL;

	fse->min_width  = imx307->support_modes[fse->index].width;
	fse->max_width  = imx307->support_modes[fse->index].width;
	fse->max_height = imx307->support_modes[fse->index].height;
	fse->min_height = imx307->support_modes[fse->index].height;

	return 0;
}

#ifdef USED_TEST_PATTERN
static int imx307_enable_test_pattern(struct imx307 *imx307, u32 pattern)
{
	u32 val = 0;

	imx307_read_reg(imx307->client,
			IMX307_REG_TEST_PATTERN,
			IMX307_REG_VALUE_08BIT,
			&val);
	if (pattern) {
		val = ((pattern - 1) << 4) | IMX307_TEST_PATTERN_ENABLE;
		imx307_write_reg(imx307->client,
				 0x300a,
				 IMX307_REG_VALUE_08BIT,
				 0x00);
		imx307_write_reg(imx307->client,
				 0x300e,
				 IMX307_REG_VALUE_08BIT,
				 0x00);
	} else {
		val &= ~IMX307_TEST_PATTERN_ENABLE;
		imx307_write_reg(imx307->client,
				 0x300a,
				 IMX307_REG_VALUE_08BIT,
				 0x3c);
		imx307_write_reg(imx307->client,
				 0x300e,
				 IMX307_REG_VALUE_08BIT,
				 0x01);
	}
	return imx307_write_reg(imx307->client,
				IMX307_REG_TEST_PATTERN,
				IMX307_REG_VALUE_08BIT,
				val);
}
#endif

static int imx307_g_frame_interval(struct v4l2_subdev *sd,
				   struct v4l2_subdev_frame_interval *fi)
{
	struct imx307 *imx307 = to_imx307(sd);
	const struct imx307_mode *mode = imx307->cur_mode;

	mutex_lock(&imx307->mutex);
	fi->interval = mode->max_fps;
	mutex_unlock(&imx307->mutex);

	return 0;
}

static int imx307_g_mbus_config(struct v4l2_subdev *sd,
				struct v4l2_mbus_config *config)
{
	struct imx307 *imx307 = to_imx307(sd);
	u32 val = 0;

	val = 1 << (imx307->cur_mode->lanes - 1) |
			V4L2_MBUS_CSI2_CHANNEL_0 |
			V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
	if (imx307->bus_cfg.bus_type == 3)
		config->type = V4L2_MBUS_CCP2;
	else
		config->type = V4L2_MBUS_CSI2;
	config->flags = val;

	return 0;
}

static int imx307_set_hdrae(struct imx307 *imx307,
			    struct preisp_hdrae_exp_s *ae)
{
	u32 l_exp_time, m_exp_time, s_exp_time;
	u32 l_gain, m_gain, s_gain;
	u32 shs1, shs2, rhs1;
	u32 gain_switch = 0;
	int ret = 0;
	u8 cg_mode = 0;
	u32 fsc = imx307->cur_vts;//The HDR mode vts is double by default to workaround T-line

	if (!imx307->has_init_exp && !imx307->streaming) {
		imx307->init_hdrae_exp = *ae;
		imx307->has_init_exp = true;
		dev_dbg(&imx307->client->dev, "imx307 don't stream, record exp for hdr!\n");
		return ret;
	}

	l_exp_time = ae->long_exp_reg;
	m_exp_time = ae->middle_exp_reg;
	s_exp_time = ae->short_exp_reg;
	l_gain = ae->long_gain_reg;
	m_gain = ae->middle_gain_reg;
	s_gain = ae->short_gain_reg;

	if (imx307->cur_mode->hdr_mode == HDR_X2) {
		//2 stagger
		l_gain = m_gain;
		l_exp_time = m_exp_time;
		cg_mode = ae->middle_cg_mode;
	}
	dev_dbg(&imx307->client->dev,
		"rev exp req: L_time=%d, gain=%d, S_time=%d, gain=%d\n",
		l_exp_time, l_gain,
		s_exp_time, s_gain);
	ret = imx307_read_reg(imx307->client, IMX307_GAIN_SWITCH_REG,
				IMX307_REG_VALUE_08BIT, &gain_switch);
	if (!g_isHCG && cg_mode == GAIN_MODE_HCG) {
		gain_switch |= 0x0110;
		g_isHCG = true;
	} else if (g_isHCG && cg_mode == GAIN_MODE_LCG) {
		gain_switch &= 0xef;
		gain_switch |= 0x100;
		g_isHCG = false;
	}

	//long exposure and short exposure
	if (imx307->cur_mode->lanes == 2 && imx307->bus_cfg.bus_type == 3)
		rhs1 = RHS1;
	else
		rhs1 = 0xe1;
	shs1 = rhs1 - s_exp_time - 1;
	shs2 = fsc - l_exp_time - 1;
	if (shs1 < 2)
		shs1 = 2;
	if (shs2 < (rhs1 + 2))
		shs2 = rhs1 + 2;
	else if (shs2 > (fsc - 2))
		shs2 = fsc - 2;

	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS1_L,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_LOW_BYTE_EXP(shs1));
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS1_M,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_MID_BYTE_EXP(shs1));
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS1_H,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_HIGH_BYTE_EXP(shs1));
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS2_L,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_LOW_BYTE_EXP(shs2));
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS2_M,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_MID_BYTE_EXP(shs2));
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SHS2_H,
		IMX307_REG_VALUE_08BIT,
		IMX307_FETCH_HIGH_BYTE_EXP(shs2));

	ret |= imx307_write_reg(imx307->client, IMX307_REG_LF_GAIN,
		IMX307_REG_VALUE_08BIT,
		l_gain);
	ret |= imx307_write_reg(imx307->client, IMX307_REG_SF_GAIN,
		IMX307_REG_VALUE_08BIT,
		s_gain);
	if (gain_switch & 0x100) {
		ret |= imx307_write_reg(imx307->client,
				IMX307_GROUP_HOLD_REG,
				IMX307_REG_VALUE_08BIT,
				IMX307_GROUP_HOLD_START);
		ret |= imx307_write_reg(imx307->client, IMX307_GAIN_SWITCH_REG,
				IMX307_REG_VALUE_08BIT, gain_switch);
		ret |= imx307_write_reg(imx307->client,
				IMX307_GROUP_HOLD_REG,
				IMX307_REG_VALUE_08BIT,
				IMX307_GROUP_HOLD_END);
	}
	dev_dbg(&imx307->client->dev,
		"set l_gain:0x%x s_gain:0x%x shs2:0x%x shs1:0x%x\n",
		l_gain, s_gain, shs2, shs1);
	return ret;
}

static void imx307_get_module_inf(struct imx307 *imx307,
				  struct rkmodule_inf *inf)
{
	memset(inf, 0, sizeof(*inf));
	strlcpy(inf->base.sensor, IMX307_NAME, sizeof(inf->base.sensor));
	strlcpy(inf->base.module, imx307->module_name,
		sizeof(inf->base.module));
	strlcpy(inf->base.lens, imx307->len_name, sizeof(inf->base.lens));
}

static int imx307_set_conversion_gain(struct imx307 *imx307, u32 *cg)
{
	int ret = 0;
	struct i2c_client *client = imx307->client;
	int cur_cg = *cg;
	u32 gain_switch = 0;

	ret = imx307_read_reg(client,
		IMX307_GAIN_SWITCH_REG,
		IMX307_REG_VALUE_08BIT,
		&gain_switch);
	if (g_isHCG && cur_cg == GAIN_MODE_LCG) {
		gain_switch &= 0xef;
		gain_switch |= 0x0100;
		g_isHCG = false;
	} else if (!g_isHCG && cur_cg == GAIN_MODE_HCG) {
		gain_switch |= 0x0110;
		g_isHCG = true;
	}

	if (gain_switch & 0x100) {
		ret |= imx307_write_reg(client,
			IMX307_GROUP_HOLD_REG,
			IMX307_REG_VALUE_08BIT,
			IMX307_GROUP_HOLD_START);
		ret |= imx307_write_reg(client,
			IMX307_GAIN_SWITCH_REG,
			IMX307_REG_VALUE_08BIT,
			gain_switch & 0xff);
		ret |= imx307_write_reg(client,
			IMX307_GROUP_HOLD_REG,
			IMX307_REG_VALUE_08BIT,
			IMX307_GROUP_HOLD_END);
	}

	return ret;
}

#define USED_SYS_DEBUG
#ifdef USED_SYS_DEBUG
//ag: echo 0 >  /sys/devices/platform/ff510000.i2c/i2c-1/1-0037/cam_s_cg
static ssize_t set_conversion_gain_status(struct device *dev,
	struct device_attribute *attr,
	const char *buf,
	size_t count)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct imx307 *imx307 = to_imx307(sd);
	int status = 0;
	int ret = 0;

	ret = kstrtoint(buf, 0, &status);
	if (!ret && status >= 0 && status < 2)
		imx307_set_conversion_gain(imx307, &status);
	else
		dev_err(dev, "input 0 for LCG, 1 for HCG, cur %d\n", status);
	return count;
}

static struct device_attribute attributes[] = {
	__ATTR(cam_s_cg, S_IWUSR, NULL, set_conversion_gain_status),
};

static int add_sysfs_interfaces(struct device *dev)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(attributes); i++)
		if (device_create_file(dev, attributes + i))
			goto undo;
	return 0;
undo:
	for (i--; i >= 0 ; i--)
		device_remove_file(dev, attributes + i);
	dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
	return -ENODEV;
}
#endif

static long imx307_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	struct imx307 *imx307 = to_imx307(sd);
	struct rkmodule_hdr_cfg *hdr;
	struct rkmodule_lvds_cfg *lvds_cfg;
	const struct imx307_mode *mode;
	u32 i, h, w;
	long ret = 0;
	s64 dst_pixel_rate = 0;
	s32 dst_link_freq = 0;
	u32 stream = 0;

	switch (cmd) {
	case RKMODULE_GET_MODULE_INFO:
		imx307_get_module_inf(imx307, (struct rkmodule_inf *)arg);
		break;
	case PREISP_CMD_SET_HDRAE_EXP:
		ret = imx307_set_hdrae(imx307, arg);
		break;
	case RKMODULE_GET_HDR_CFG:
		hdr = (struct rkmodule_hdr_cfg *)arg;
		if (imx307->cur_mode->hdr_mode == NO_HDR)
			hdr->esp.mode = HDR_NORMAL_VC;
		else
			hdr->esp.mode = HDR_ID_CODE;
		hdr->hdr_mode = imx307->cur_mode->hdr_mode;
		break;
	case RKMODULE_SET_HDR_CFG:
		hdr = (struct rkmodule_hdr_cfg *)arg;
		for (i = 0; i < imx307->support_modes_num; i++) {
			if (imx307->support_modes[i].hdr_mode == hdr->hdr_mode) {
				imx307->cur_mode = &imx307->support_modes[i];
				break;
			}
		}
		if (i == imx307->support_modes_num) {
			dev_err(&imx307->client->dev,
				"not find hdr mode:%d config\n",
				hdr->hdr_mode);
			ret = -EINVAL;
		} else {
			mode = imx307->cur_mode;
			w = mode->hts_def - mode->width;
			h = mode->vts_def - mode->height;
			__v4l2_ctrl_modify_range(imx307->hblank, w, w, 1, w);
			__v4l2_ctrl_modify_range(imx307->vblank, h,
				IMX307_VTS_MAX - mode->height,
				1, h);
			dst_link_freq = mode->freq_idx;
			dst_pixel_rate = (u32)link_freq_menu_items[mode->freq_idx] / mode->bpp * 2 * mode->lanes;
			__v4l2_ctrl_s_ctrl_int64(imx307->pixel_rate,
						 dst_pixel_rate);
			__v4l2_ctrl_s_ctrl(imx307->link_freq,
					   dst_link_freq);
			imx307->cur_vts = mode->vts_def;
		}
		break;
	case RKMODULE_SET_CONVERSION_GAIN:
		ret = imx307_set_conversion_gain(imx307, (u32 *)arg);
		break;
	case RKMODULE_GET_LVDS_CFG:
		lvds_cfg = (struct rkmodule_lvds_cfg *)arg;
		if (imx307->bus_cfg.bus_type == 3)
			memcpy(lvds_cfg, &imx307->cur_mode->lvds_cfg,
				sizeof(struct rkmodule_lvds_cfg));
		else
			ret = -ENOIOCTLCMD;
		break;
	case RKMODULE_SET_QUICK_STREAM:

		stream = *((u32 *)arg);

		if (stream)
			ret = imx307_write_reg(imx307->client,
					       IMX307_REG_CTRL_MODE,
					       IMX307_REG_VALUE_08BIT,
					       0);
		else
			ret = imx307_write_reg(imx307->client,
					       IMX307_REG_CTRL_MODE,
					       IMX307_REG_VALUE_08BIT,
					       1);
		break;
	default:
		ret = -ENOIOCTLCMD;
		break;
	}
	return ret;
}

#ifdef CONFIG_COMPAT
static long imx307_compat_ioctl32(struct v4l2_subdev *sd,
				  unsigned int cmd, unsigned long arg)
{
	void __user *up = compat_ptr(arg);
	struct rkmodule_inf *inf;
	struct rkmodule_awb_cfg *cfg;
	struct rkmodule_hdr_cfg *hdr;
	struct preisp_hdrae_exp_s *hdrae;
	long ret;
	u32 cg = 0;
	u32 stream = 0;

	switch (cmd) {
	case RKMODULE_GET_MODULE_INFO:
		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
		if (!inf) {
			ret = -ENOMEM;
			return ret;
		}

		ret = imx307_ioctl(sd, cmd, inf);
		if (!ret)
			ret = copy_to_user(up, inf, sizeof(*inf));
		kfree(inf);
		break;
	case RKMODULE_AWB_CFG:
		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
		if (!cfg) {
			ret = -ENOMEM;
			return ret;
		}
		ret = copy_from_user(cfg, up, sizeof(*cfg));
		if (!ret)
			ret = imx307_ioctl(sd, cmd, cfg);
		kfree(cfg);
		break;
	case RKMODULE_GET_HDR_CFG:
		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
		if (!hdr) {
			ret = -ENOMEM;
			return ret;
		}

		ret = imx307_ioctl(sd, cmd, hdr);
		if (!ret)
			ret = copy_to_user(up, hdr, sizeof(*hdr));
		kfree(hdr);
		break;
	case RKMODULE_SET_HDR_CFG:
		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
		if (!hdr) {
			ret = -ENOMEM;
			return ret;
		}

		ret = copy_from_user(hdr, up, sizeof(*hdr));
		if (!ret)
			ret = imx307_ioctl(sd, cmd, hdr);
		kfree(hdr);
		break;
	case PREISP_CMD_SET_HDRAE_EXP:
		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
		if (!hdrae) {
			ret = -ENOMEM;
			return ret;
		}

		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
		if (!ret)
			ret = imx307_ioctl(sd, cmd, hdrae);
		kfree(hdrae);
		break;
	case RKMODULE_SET_CONVERSION_GAIN:
		ret = copy_from_user(&cg, up, sizeof(cg));
		if (!ret)
			ret = imx307_ioctl(sd, cmd, &cg);
		break;
	case RKMODULE_SET_QUICK_STREAM:
		ret = copy_from_user(&stream, up, sizeof(u32));
		if (!ret)
			ret = imx307_ioctl(sd, cmd, &stream);
		break;
	default:
		ret = -ENOIOCTLCMD;
		break;
	}

	return ret;
}
#endif

static int imx307_init_conversion_gain(struct imx307 *imx307)
{
	int ret = 0;
	struct i2c_client *client = imx307->client;
	u32 val = 0;

	ret = imx307_read_reg(client,
		IMX307_GAIN_SWITCH_REG,
		IMX307_REG_VALUE_08BIT,
		&val);
	val &= 0xef;
	ret |= imx307_write_reg(client,
		IMX307_GAIN_SWITCH_REG,
		IMX307_REG_VALUE_08BIT,
		val);
	if (!ret)
		g_isHCG = false;
	return ret;
}

static int __imx307_start_stream(struct imx307 *imx307)
{
	int ret;

	ret = imx307_write_array(imx307->client, imx307->cur_mode->reg_list);
	if (ret)
		return ret;
	ret = imx307_init_conversion_gain(imx307);
	if (ret)
		return ret;
	/* In case these controls are set before streaming */
	ret = __v4l2_ctrl_handler_setup(&imx307->ctrl_handler);
	if (ret)
		return ret;
	if (imx307->has_init_exp && imx307->cur_mode->hdr_mode != NO_HDR) {
		ret = imx307_ioctl(&imx307->subdev, PREISP_CMD_SET_HDRAE_EXP,
			&imx307->init_hdrae_exp);
		if (ret) {
			dev_err(&imx307->client->dev,
				"init exp fail in hdr mode\n");
			return ret;
		}
	}

	ret = imx307_write_reg(imx307->client,
		IMX307_REG_CTRL_MODE,
		IMX307_REG_VALUE_08BIT,
		0);
	return ret;
}

static int __imx307_stop_stream(struct imx307 *imx307)
{
	return imx307_write_reg(imx307->client,
		IMX307_REG_CTRL_MODE,
		IMX307_REG_VALUE_08BIT,
		1);
}

static int imx307_s_stream(struct v4l2_subdev *sd, int on)
{
	struct imx307 *imx307 = to_imx307(sd);
	struct i2c_client *client = imx307->client;
	int ret = 0;

	mutex_lock(&imx307->mutex);
	on = !!on;
	if (on == imx307->streaming)
		goto unlock_and_return;

	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}

		ret = __imx307_start_stream(imx307);
		if (ret) {
			v4l2_err(sd, "start stream failed while write regs\n");
			pm_runtime_put(&client->dev);
			goto unlock_and_return;
		}
	} else {
		__imx307_stop_stream(imx307);
		pm_runtime_put(&client->dev);
	}

	imx307->streaming = on;

unlock_and_return:
	mutex_unlock(&imx307->mutex);

	return ret;
}

static int imx307_s_power(struct v4l2_subdev *sd, int on)
{
	struct imx307 *imx307 = to_imx307(sd);
	struct i2c_client *client = imx307->client;
	int ret = 0;

	mutex_lock(&imx307->mutex);

	/* If the power state is not modified - no work to do. */
	if (imx307->power_on == !!on)
		goto unlock_and_return;

	if (on) {
		ret = pm_runtime_get_sync(&client->dev);
		if (ret < 0) {
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}

		ret = imx307_write_array(imx307->client, imx307_global_regs);
		if (ret) {
			v4l2_err(sd, "could not set init registers\n");
			pm_runtime_put_noidle(&client->dev);
			goto unlock_and_return;
		}

		imx307->power_on = true;
	} else {
		pm_runtime_put(&client->dev);
		imx307->power_on = false;
	}

unlock_and_return:
	mutex_unlock(&imx307->mutex);

	return ret;
}

/* Calculate the delay in us by clock rate and clock cycles */
static inline u32 imx307_cal_delay(u32 cycles)
{
	return DIV_ROUND_UP(cycles, IMX307_XVCLK_FREQ / 1000 / 1000);
}

static int __imx307_power_on(struct imx307 *imx307)
{
	int ret;
	u32 delay_us;
	struct device *dev = &imx307->client->dev;

	if (!IS_ERR_OR_NULL(imx307->pins_default)) {
		ret = pinctrl_select_state(imx307->pinctrl,
					   imx307->pins_default);
		if (ret < 0)
			dev_err(dev, "could not set pins\n");
	}

	ret = clk_set_rate(imx307->xvclk, IMX307_XVCLK_FREQ);
	if (ret < 0)
		dev_warn(dev, "Failed to set xvclk rate (37.125M Hz)\n");

	if (clk_get_rate(imx307->xvclk) != IMX307_XVCLK_FREQ)
		dev_warn(dev, "xvclk mismatched,based on 24M Hz\n");

	ret = clk_prepare_enable(imx307->xvclk);
	if (ret < 0) {
		dev_err(dev, "Failed to enable xvclk\n");
		return ret;
	}


	ret = regulator_bulk_enable(IMX307_NUM_SUPPLIES, imx307->supplies);
	if (ret < 0) {
		dev_err(dev, "Failed to enable regulators\n");
		goto disable_clk;
	}

	if (!IS_ERR(imx307->reset_gpio))
		gpiod_set_value_cansleep(imx307->reset_gpio, 1);
	usleep_range(500, 1000);
	if (!IS_ERR(imx307->reset_gpio))
		gpiod_set_value_cansleep(imx307->reset_gpio, 0);

	if (!IS_ERR(imx307->pwdn_gpio))
		gpiod_set_value_cansleep(imx307->pwdn_gpio, 1);

	/* 8192 cycles prior to first SCCB transaction */
	delay_us = imx307_cal_delay(8192);
	usleep_range(delay_us, delay_us * 2);
	usleep_range(5000, 10000);
	return 0;

disable_clk:
	clk_disable_unprepare(imx307->xvclk);

	return ret;
}

static void __imx307_power_off(struct imx307 *imx307)
{
	int ret;
	struct device *dev = &imx307->client->dev;

	if (!IS_ERR(imx307->pwdn_gpio))
		gpiod_set_value_cansleep(imx307->pwdn_gpio, 0);
	clk_disable_unprepare(imx307->xvclk);
	if (!IS_ERR(imx307->reset_gpio))
		gpiod_set_value_cansleep(imx307->reset_gpio, 1);
	if (!IS_ERR_OR_NULL(imx307->pins_sleep)) {
		ret = pinctrl_select_state(imx307->pinctrl,
					   imx307->pins_sleep);
		if (ret < 0)
			dev_dbg(dev, "could not set pins\n");
	}
	regulator_bulk_disable(IMX307_NUM_SUPPLIES, imx307->supplies);
}

static int imx307_runtime_resume(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct imx307 *imx307 = to_imx307(sd);

	return __imx307_power_on(imx307);
}

static int imx307_runtime_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct imx307 *imx307 = to_imx307(sd);

	__imx307_power_off(imx307);

	return 0;
}

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static int imx307_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
	struct imx307 *imx307 = to_imx307(sd);
	struct v4l2_mbus_framefmt *try_fmt =
				v4l2_subdev_get_try_format(sd, fh->pad, 0);
	const struct imx307_mode *def_mode = &imx307->support_modes[0];

	mutex_lock(&imx307->mutex);
	/* Initialize try_fmt */
	try_fmt->width = def_mode->width;
	try_fmt->height = def_mode->height;
	try_fmt->code = def_mode->bus_fmt;
	try_fmt->field = V4L2_FIELD_NONE;

	mutex_unlock(&imx307->mutex);
	/* No crop or compose */

	return 0;
}
#endif

static int imx307_enum_frame_interval(struct v4l2_subdev *sd,
				       struct v4l2_subdev_pad_config *cfg,
				       struct v4l2_subdev_frame_interval_enum *fie)
{
	struct imx307 *imx307 = to_imx307(sd);

	if (fie->index >= imx307->support_modes_num)
		return -EINVAL;

	fie->code = imx307->support_modes[fie->index].bus_fmt;
	fie->width = imx307->support_modes[fie->index].width;
	fie->height = imx307->support_modes[fie->index].height;
	fie->interval = imx307->support_modes[fie->index].max_fps;
	fie->reserved[0] = imx307->support_modes[fie->index].hdr_mode;
	return 0;
}

#define CROP_START(SRC, DST) (((SRC) - (DST)) / 2 / 4 * 4)
#define DST_WIDTH 1920
#define DST_HEIGHT 1080

/*
 * The resolution of the driver configuration needs to be exactly
 * the same as the current output resolution of the sensor,
 * the input width of the isp needs to be 16 aligned,
 * the input height of the isp needs to be 8 aligned.
 * Can be cropped to standard resolution by this function,
 * otherwise it will crop out strange resolution according
 * to the alignment rules.
 */

static int imx307_get_selection(struct v4l2_subdev *sd,
				struct v4l2_subdev_pad_config *cfg,
				struct v4l2_subdev_selection *sel)
{
	struct imx307 *imx307 = to_imx307(sd);

	if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
		sel->r.left = CROP_START(imx307->cur_mode->width, DST_WIDTH);
		sel->r.width = DST_WIDTH;
		if (imx307->bus_cfg.bus_type == 3) {
			if (imx307->cur_mode->hdr_mode == NO_HDR)
				sel->r.top = 21;
			else
				sel->r.top = 13;
		} else {
			sel->r.top = CROP_START(imx307->cur_mode->height, DST_HEIGHT);
		}
		sel->r.height = DST_HEIGHT;
		return 0;
	}
	return -EINVAL;
}

static const struct dev_pm_ops imx307_pm_ops = {
	SET_RUNTIME_PM_OPS(imx307_runtime_suspend,
			   imx307_runtime_resume, NULL)
};

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static const struct v4l2_subdev_internal_ops imx307_internal_ops = {
	.open = imx307_open,
};
#endif

static const struct v4l2_subdev_core_ops imx307_core_ops = {
	.s_power = imx307_s_power,
	.ioctl = imx307_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = imx307_compat_ioctl32,
#endif
};

static const struct v4l2_subdev_video_ops imx307_video_ops = {
	.s_stream = imx307_s_stream,
	.g_frame_interval = imx307_g_frame_interval,
	.g_mbus_config = imx307_g_mbus_config,
};

static const struct v4l2_subdev_pad_ops imx307_pad_ops = {
	.enum_mbus_code = imx307_enum_mbus_code,
	.enum_frame_size = imx307_enum_frame_sizes,
	.enum_frame_interval = imx307_enum_frame_interval,
	.get_fmt = imx307_get_fmt,
	.set_fmt = imx307_set_fmt,
	.get_selection = imx307_get_selection,
};

static const struct v4l2_subdev_ops imx307_subdev_ops = {
	.core	= &imx307_core_ops,
	.video	= &imx307_video_ops,
	.pad	= &imx307_pad_ops,
};

static int imx307_set_ctrl(struct v4l2_ctrl *ctrl)
{

	struct imx307 *imx307 = container_of(ctrl->handler,
					     struct imx307, ctrl_handler);
	struct i2c_client *client = imx307->client;
	s64 max;
	int ret = 0;
	u32 shs1 = 0;
	u32 vts = 0;
	u32 val = 0;

	imx307_write_reg(imx307->client,
			IMX307_FLIP_REG,
			IMX307_REG_VALUE_08BIT,
			0x01);

	


	/* Propagate change of current control to all related controls */
	switch (ctrl->id) {
	case V4L2_CID_VBLANK:
		/* Update max exposure while meeting expected vblanking */
		max = imx307->cur_mode->height + ctrl->val - 2;
		__v4l2_ctrl_modify_range(imx307->exposure,
					 imx307->exposure->minimum, max,
					 imx307->exposure->step,
					 imx307->exposure->default_value);
		break;
	}

	if (!pm_runtime_get_if_in_use(&client->dev))
		return 0;

	switch (ctrl->id) {
	case V4L2_CID_EXPOSURE:
		shs1 = imx307->cur_vts - (ctrl->val + 1);
		ret = imx307_write_reg(imx307->client,
			IMX307_REG_SHS1_H,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_HIGH_BYTE_EXP(shs1));
		ret |= imx307_write_reg(imx307->client,
			IMX307_REG_SHS1_M,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_MID_BYTE_EXP(shs1));
		ret |= imx307_write_reg(imx307->client,
			IMX307_REG_SHS1_L,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_LOW_BYTE_EXP(shs1));
		dev_dbg(&client->dev, "set exposure 0x%x, cur_vts 0x%x,shs1 0x%x\n",
			ctrl->val, imx307->cur_vts, shs1);
		break;
	case V4L2_CID_ANALOGUE_GAIN:
		ret = imx307_write_reg(imx307->client,
			IMX307_REG_LF_GAIN,
			IMX307_REG_VALUE_08BIT,
			ctrl->val);
		dev_dbg(&client->dev, "set analog gain 0x%x\n",
			ctrl->val);
		break;
	case V4L2_CID_VBLANK:
		vts = ctrl->val + imx307->cur_mode->height;
		imx307->cur_vts = vts;
		if (imx307->cur_mode->hdr_mode == HDR_X2)
			vts /= 2;
		ret = imx307_write_reg(imx307->client,
			IMX307_REG_VTS_H,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_HIGH_BYTE_VTS(vts));
		ret |= imx307_write_reg(imx307->client,
			IMX307_REG_VTS_M,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_MID_BYTE_VTS(vts));
		ret |= imx307_write_reg(imx307->client,
			IMX307_REG_VTS_L,
			IMX307_REG_VALUE_08BIT,
			IMX307_FETCH_LOW_BYTE_VTS(vts));
		dev_dbg(&client->dev, "set vts 0x%x\n",
			vts);
		break;
	case V4L2_CID_TEST_PATTERN:
#ifdef USED_TEST_PATTERN
		ret = imx307_enable_test_pattern(imx307, ctrl->val);
#endif
		break;
	case V4L2_CID_HFLIP:
		ret = imx307_read_reg(client,
				      IMX307_FLIP_REG,
				      IMX307_REG_VALUE_08BIT,
				      &val);
		if (ctrl->val)
			val |= MIRROR_BIT_MASK;
		else
			val &= ~MIRROR_BIT_MASK;
		ret |= imx307_write_reg(client,
					IMX307_FLIP_REG,
					IMX307_REG_VALUE_08BIT,
					val);
		if (ret == 0)
			imx307->flip = val;
		break;
	case V4L2_CID_VFLIP:
		ret = imx307_read_reg(client,
				      IMX307_FLIP_REG,
				      IMX307_REG_VALUE_08BIT,
				      &val);
		if (ctrl->val)
			val |= FLIP_BIT_MASK;
		else
			val &= ~FLIP_BIT_MASK;
		ret |= imx307_write_reg(client,
					IMX307_FLIP_REG,
					IMX307_REG_VALUE_08BIT,
					val);
		if (ret == 0)
			imx307->flip = val;
		break;
	default:
		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
			 __func__, ctrl->id, ctrl->val);
		break;
	}

	pm_runtime_put(&client->dev);

	return ret;
}

static const struct v4l2_ctrl_ops imx307_ctrl_ops = {
	.s_ctrl = imx307_set_ctrl,
};

static int imx307_initialize_controls(struct imx307 *imx307)
{
	const struct imx307_mode *mode;
	struct v4l2_ctrl_handler *handler;
	s64 exposure_max, vblank_def;
	u32 h_blank;
	int ret;
	s32 dst_link_freq = 0;
	s64 dst_pixel_rate = 0;

	handler = &imx307->ctrl_handler;
	mode = imx307->cur_mode;
	ret = v4l2_ctrl_handler_init(handler, 9);
	if (ret)
		return ret;
	handler->lock = &imx307->mutex;

	imx307->link_freq = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
				      1, 0, link_freq_menu_items);

	dst_link_freq = mode->freq_idx;
	dst_pixel_rate = (u32)link_freq_menu_items[mode->freq_idx] / mode->bpp * 2 * mode->lanes;
	__v4l2_ctrl_s_ctrl(imx307->link_freq,
			   dst_link_freq);
	imx307->pixel_rate = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
			  0, IMX307_PIXEL_RATE_HDR, 1, dst_pixel_rate);

	h_blank = mode->hts_def - mode->width;

	imx307->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
				h_blank, h_blank, 1, h_blank);
	if (imx307->hblank)
		imx307->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;

	vblank_def = mode->vts_def - mode->height;
	imx307->cur_vts = mode->vts_def;
	imx307->vblank = v4l2_ctrl_new_std(handler, &imx307_ctrl_ops,
				V4L2_CID_VBLANK, vblank_def,
				IMX307_VTS_MAX - mode->height,
				1, vblank_def);

	exposure_max = mode->vts_def - 4;

	imx307->exposure = v4l2_ctrl_new_std(handler, &imx307_ctrl_ops,
				V4L2_CID_EXPOSURE, IMX307_EXPOSURE_MIN,
				exposure_max, IMX307_EXPOSURE_STEP,
				mode->exp_def);

	imx307->anal_gain = v4l2_ctrl_new_std(handler, &imx307_ctrl_ops,
				V4L2_CID_ANALOGUE_GAIN, IMX307_GAIN_MIN,
				IMX307_GAIN_MAX, IMX307_GAIN_STEP,
				IMX307_GAIN_DEFAULT);

#ifdef USED_TEST_PATTERN
	imx307->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
				&imx307_ctrl_ops, V4L2_CID_TEST_PATTERN,
				ARRAY_SIZE(imx307_test_pattern_menu) - 1,
				0, 0, imx307_test_pattern_menu);
#endif
	imx307->h_flip = v4l2_ctrl_new_std(handler, &imx307_ctrl_ops,
				V4L2_CID_HFLIP, 0, 1, 1, 0);

	imx307->v_flip = v4l2_ctrl_new_std(handler, &imx307_ctrl_ops,
				V4L2_CID_VFLIP, 0, 1, 1, 0);
	imx307->flip = 0;
	if (handler->error) {
		ret = handler->error;
		dev_err(&imx307->client->dev,
			"Failed to init controls(%d)\n", ret);
		goto err_free_handler;
	}

	imx307->subdev.ctrl_handler = handler;
	imx307->has_init_exp = false;

	return 0;

err_free_handler:
	v4l2_ctrl_handler_free(handler);

	return ret;
}

static int imx307_check_sensor_id(struct imx307 *imx307,
				  struct i2c_client *client)
{
	struct device *dev = &imx307->client->dev;
	u32 id = 0;
	int ret;

	ret = imx307_read_reg(client, IMX307_REG_CHIP_ID,
			      IMX307_REG_VALUE_08BIT, &id);

	if (id != CHIP_ID) {
		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
		return -EINVAL;
	}
	return ret;
}

static int imx307_configure_regulators(struct imx307 *imx307)
{
	unsigned int i;

	for (i = 0; i < IMX307_NUM_SUPPLIES; i++)
		imx307->supplies[i].supply = imx307_supply_names[i];

	return devm_regulator_bulk_get(&imx307->client->dev,
				       IMX307_NUM_SUPPLIES,
				       imx307->supplies);
}

static int imx307_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct device *dev = &client->dev;
	struct device_node *node = dev->of_node;
	struct imx307 *imx307;
	struct v4l2_subdev *sd;
	char facing[2];
	int ret;
	struct device_node *endpoint;

	dev_info(dev, "driver version: %02x.%02x.%02x",
		DRIVER_VERSION >> 16,
		(DRIVER_VERSION & 0xff00) >> 8,
		DRIVER_VERSION & 0x00ff);

	imx307 = devm_kzalloc(dev, sizeof(*imx307), GFP_KERNEL);
	if (!imx307)
		return -ENOMEM;

	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
				   &imx307->module_index);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
				       &imx307->module_facing);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
				       &imx307->module_name);
	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
				       &imx307->len_name);
	if (ret) {
		dev_err(dev, "could not get module information!\n");
		return -EINVAL;
	}
	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
	if (!endpoint) {
		dev_err(dev, "Failed to get endpoint\n");
		return -EINVAL;
	}

	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
		&imx307->bus_cfg);
	if (ret)
		dev_warn(dev, "could not get bus config!\n");
	if (imx307->bus_cfg.bus_type == 3) {
		if (imx307->bus_cfg.bus.mipi_csi1.data_lane == 2) {
			imx307->support_modes = lvds_2lane_supported_modes;
			imx307->support_modes_num = ARRAY_SIZE(lvds_2lane_supported_modes);
		} else if (imx307->bus_cfg.bus.mipi_csi1.data_lane  == 4) {
			imx307->support_modes = lvds_supported_modes;
			imx307->support_modes_num = ARRAY_SIZE(lvds_supported_modes);
		} else {
			dev_err(dev, "lvds lanes err!\n");
		}
	} else {
		imx307->support_modes = mipi_supported_modes;
		imx307->support_modes_num = ARRAY_SIZE(mipi_supported_modes);
	}
	imx307->client = client;
	imx307->cur_mode = &imx307->support_modes[0];

	imx307->xvclk = devm_clk_get(dev, "xvclk");
	if (IS_ERR(imx307->xvclk)) {
		dev_err(dev, "Failed to get xvclk\n");
		return -EINVAL;
	}

	imx307->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
	if (IS_ERR(imx307->reset_gpio))
		dev_warn(dev, "Failed to get reset-gpios\n");

	imx307->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
	if (IS_ERR(imx307->pwdn_gpio))
		dev_warn(dev, "Failed to get pwdn-gpios\n");

	ret = imx307_configure_regulators(imx307);
	if (ret) {
		dev_err(dev, "Failed to get power regulators\n");
		return ret;
	}

	imx307->pinctrl = devm_pinctrl_get(dev);
	if (!IS_ERR(imx307->pinctrl)) {
		imx307->pins_default =
			pinctrl_lookup_state(imx307->pinctrl,
					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
		if (IS_ERR(imx307->pins_default))
			dev_err(dev, "could not get default pinstate\n");

		imx307->pins_sleep =
			pinctrl_lookup_state(imx307->pinctrl,
					     OF_CAMERA_PINCTRL_STATE_SLEEP);
		if (IS_ERR(imx307->pins_sleep))
			dev_err(dev, "could not get sleep pinstate\n");
	}

	mutex_init(&imx307->mutex);

	sd = &imx307->subdev;
	v4l2_i2c_subdev_init(sd, client, &imx307_subdev_ops);
	ret = imx307_initialize_controls(imx307);
	if (ret)
		goto err_destroy_mutex;

	ret = __imx307_power_on(imx307);
	if (ret)
		goto err_free_handler;

	ret = imx307_check_sensor_id(imx307, client);
	if (ret)
		goto err_power_off;

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
	dev_err(dev, "set the video v4l2 subdev api\n");
	sd->internal_ops = &imx307_internal_ops;
	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
		     V4L2_SUBDEV_FL_HAS_EVENTS;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
	dev_err(dev, "set the media controller\n");
	imx307->pad.flags = MEDIA_PAD_FL_SOURCE;
	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
	ret = media_entity_pads_init(&sd->entity, 1, &imx307->pad);
	if (ret < 0)
		goto err_power_off;
#endif

	memset(facing, 0, sizeof(facing));
	if (strcmp(imx307->module_facing, "back") == 0)
		facing[0] = 'b';
	else
		facing[0] = 'f';

	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
		 imx307->module_index, facing,
		 IMX307_NAME, dev_name(sd->dev));
	ret = v4l2_async_register_subdev_sensor_common(sd);
	if (ret) {
		dev_err(dev, "v4l2 async register subdev failed\n");
		goto err_clean_entity;
	}

	pm_runtime_set_active(dev);
	pm_runtime_enable(dev);
	pm_runtime_idle(dev);
	g_isHCG = false;
#ifdef USED_SYS_DEBUG
	add_sysfs_interfaces(dev);
#endif
	dev_err(dev, "v4l2 async register subdev success\n");
	return 0;

err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
	media_entity_cleanup(&sd->entity);
#endif
err_power_off:
	__imx307_power_off(imx307);
err_free_handler:
	v4l2_ctrl_handler_free(&imx307->ctrl_handler);
err_destroy_mutex:
	mutex_destroy(&imx307->mutex);

	return ret;
}

static int imx307_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd = i2c_get_clientdata(client);
	struct imx307 *imx307 = to_imx307(sd);

	v4l2_async_unregister_subdev(sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
	media_entity_cleanup(&sd->entity);
#endif
	v4l2_ctrl_handler_free(&imx307->ctrl_handler);
	mutex_destroy(&imx307->mutex);

	pm_runtime_disable(&client->dev);
	if (!pm_runtime_status_suspended(&client->dev))
		__imx307_power_off(imx307);
	pm_runtime_set_suspended(&client->dev);

	return 0;
}

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id imx307_of_match[] = {
	{ .compatible = "sony,imx307" },
	{},
};
MODULE_DEVICE_TABLE(of, imx307_of_match);
#endif

static const struct i2c_device_id imx307_match_id[] = {
	{ "sony,imx307", 0 },
	{ },
};

static struct i2c_driver imx307_i2c_driver = {
	.driver = {
		.name = IMX307_NAME,
		.pm = &imx307_pm_ops,
		.of_match_table = of_match_ptr(imx307_of_match),
	},
	.probe		= &imx307_probe,
	.remove		= &imx307_remove,
	.id_table	= imx307_match_id,
};

static int __init sensor_mod_init(void)
{
	return i2c_add_driver(&imx307_i2c_driver);
}

static void __exit sensor_mod_exit(void)
{
	i2c_del_driver(&imx307_i2c_driver);
}

device_initcall_sync(sensor_mod_init);
module_exit(sensor_mod_exit);

MODULE_DESCRIPTION("Sony imx307 sensor driver");
MODULE_LICENSE("GPL v2");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1708880.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch 认证模拟题 - 1

1、题目 定义一个数据流&#xff0c;满足 data-stream_*_*&#xff0c;数据首先分布在 data_hot&#xff0c;5分钟后移动到 data_warm&#xff0c;3分钟后到 data_cold&#xff0c;再过 8 分钟删除。 1.1 考点 生命周期索引模板数据流 1.2 答案 # 修改生命周期策略修改时间…

SpringBoot整合RabbitMQ的快速使用教程

目录 一、引入依赖 二、配置rabbitmq的连接信息等 1、生产者配置 2、消费者配置 三、设置消息转换器 四、生产者代码示例 1、配置交换机和队列信息 2、生产消息代码 五、消费者代码示例 1、消费层代码 2、业务层代码 在分布式系统中&#xff0c;消息队列是一种重要…

RTOS(6)任务

重点&#xff1a; 一、FreeRtos任务的API调用 1.创建任务&#xff08;静态、动态创建&#xff09; 动态创建: ①先写任务函数 ②定义函数的handle指针 ③调用动态创建任务的API&#xff1a;xTaskCreate&#xff08;任务函数&#xff0c;任务名称&#xff0c;栈深度&#x…

【css3】01-css3新特性样式篇

目录 1 背景 1.1 设置背景图片的定位 1.2 背景裁切-规定背景的绘制区域 1.3 设置背景图片尺寸 2 边框 2.1 盒子阴影box-shadow 2.2 边框图片border-image 3 文本 -文字阴影text-shadow 1 背景 1.1 设置背景图片的定位 background-origin&#xff1a;规定背景图片的定位…

遇到了导师放养,该怎么坚持?

最近收到学生读者的留言&#xff0c;抱怨科研的困难。导师忙碌且学生众多&#xff0c;自己只是众多学生之一&#xff0c;常常处于放养状态。除了每周的组会外&#xff0c;几乎无法接触到导师。在这种状态下&#xff0c;缺乏方向和动力&#xff0c;非常担心无法顺利毕业&#xf…

Llama模型家族之使用 Supervised Fine-Tuning(SFT)微调预训练Llama 3 语言模型(三)通过web页面方式微调

LlaMA 3 系列博客 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;一&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;二&#xff09; 基于 LlaMA 3 LangGraph 在windows本地部署大模型 &#xff08;三&#xff09; 基于 LlaMA…

【Linux】自己实现一个bash进程

bash就是命令行解释器&#xff0c;就是Linux操作系统让我们看到的&#xff0c;与用户进行交互的一种外壳&#xff08;shell&#xff09;&#xff0c;当然了bash也是一个进程&#xff0c;它有时候就是通过创建子进程来执行我们输入的命令的。这无疑就离不开我们上篇博客所说的进…

如何解决链游中可能出现的延迟或网络拥堵问题?

随着区块链技术的不断发展和普及&#xff0c;链游&#xff08;基于区块链的游戏&#xff09;作为新兴的娱乐形式&#xff0c;正逐渐走进大众的视野。然而&#xff0c;与传统游戏相比&#xff0c;链游在运行过程中可能会遇到一些特有的问题&#xff0c;其中最为突出的就是延迟和…

Windows hook介绍与代码演示

Windows Hook 是一种机制&#xff0c;允许应用程序监视系统或处理特定事件。它可以拦截和更改消息&#xff0c;甚至可以插入到其他应用程序的消息处理机制中。Windows 提供了多种挂钩类型&#xff0c;例如键盘挂钩、鼠标挂钩、消息挂钩等。 hook代码实现 下面是一个使用 Wind…

微服务架构下的‘黑带’安全大师:Spring Cloud Security全攻略!

深入探讨了微服务间的安全通信、安全策略设计以及面对经典安全问题的应对策略。无论你是微服务的新手还是资深开发者&#xff0c;都能在本文中找到提升安全功力的秘籍。让我们一起成为微服务架构下的‘黑带’安全大师&#xff01; 文章目录 1. 引言微服务安全挑战与重要性Sprin…

【软件工程】【23.04】p1

关键字&#xff1a; 软件模型、提炼、加工表达工具、通信内聚、访问依赖、边界类交互分析、RUP核心工作流、首先测试数据流、软件验证过程、CMMI过程域分类工程类&#xff1b; 软件工程目的、功能需求是需求的主体、结构化方法、耦合、详细设计工具、类、类图、RUP采用用例技…

rk3568_mutex

文章目录 前言1、什么是mutex?1.1mutex互斥体API函数二、实验2.1实验目的2.2源码2.3结果图前言 本文记录的是rk3568开发板基础上做的mutex实验 1、什么是mutex? mutex是互斥体,它是比信号量semaphore更加专业的机制。 在我们编写Linux驱动的时候遇到需要互斥的地方建议使用…

Nginx企业级负载均衡:技术详解系列(12)—— 深入解析root、alias及location

你好&#xff0c;我是赵兴晨&#xff0c;97年文科程序员。 在生产服务器的Nginx配置中&#xff0c;我们总会遇到形形色色的配置方案。你是否曾注意到root和alias指令的巧妙应用&#xff1f;是否对那些五花八门的location匹配规则感到好奇&#xff1f; 今天&#xff0c;咱们来聊…

微服务架构-分支微服务设计模式

微服务架构-分支微服务设计模式 这种模式是聚合器模式的扩展&#xff0c;允许同时调用两个微服务链 分支微服务设计模式是一种用于构建大型系统的微服务架构模式&#xff0c;其核心思想是 将复杂的业务逻辑拆解为多个小的、相互独立的子系统&#xff0c;每个子系统由一个或多…

家政项目day2 需求分析(模拟入职后熟悉业务流程)

目录 1 项目主体介绍1.1 项目背景1.2 运营模式1.3 项目业务流程 2 运营端需求2.1 服务类型管理2.2 服务项目&#xff08;服务&#xff09;管理2.3 区域管理2.4 区域服务管理2.5 相关数据库表的管理2.6 设计工程结构2.7 测试接口&#xff08;接口断点查看业务代码&#xff09; 1…

SQL学习小记(三)

SQL学习小记&#xff08;三&#xff09; 功能实现思路代码部分名词解释 代码打包为可执行文件 功能说明&#xff1a;使用python代码&#xff0c;将数据库database1中的表格table1同步到数据库database2中 功能实现 思路 #mermaid-svg-R1pWrEWA799M299a {font-family:"tre…

Redis 中 List 数据结构详解

目录 List 用法 1. 增 2. 删 3. 查 内部编码 应用场景 前言 Redis 中的 List 和 Set 数据结构各有特点&#xff0c;适用于不同的应用场景。List 提供了有序的列表结构&#xff0c;适合用于消息队列和任务列表等场景&#xff1b;Set 提供了无序且不重复的集合结构&#…

【全开源】旅游系统源码(Uniapp+FastAdmin+ThinkPHP)

一款基于UniappFastAdminThinkPHP开发的旅游系统&#xff0c;包含消费者端&#xff08;手机端&#xff09;、机构工作人员&#xff08;手机端&#xff09;、机构端&#xff08;PC&#xff09;、平台管理端&#xff08;PC&#xff09;。机构可以发布旅游线路、景点项目&#xff…

Wpf 使用 Prism 实战开发Day27

首页汇总和数据动态显示 一.创建首页数据汇总数据接口 汇总&#xff1a;待办事项的总数已完成&#xff1a;待办事项里面有多少条完成的待办完成比例&#xff1a;已完成和汇总之间的比例备忘录&#xff1a;显示备忘录的总数待办事项&#xff1a;显示待办事项未完成的集合备忘录&…

Java实现对PDF、纵向、横向页面添加自定义水印功能

Java实现对PDF、纵向、横向页面添加自定义水印 效果图 -- 纵向 页面PDF使用到JAR Maven依赖版本效果图 -- 横向页面PDF 效果图 – 纵向 页面PDF 代码如下&#xff1a; 使用到JAR Maven依赖版本 <dependency><groupId>org.apache.pdfbox</groupId><artifa…