diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
index e030627159c6bc012b44f80180033f0a5a516929..c81c9488828d9bf91273430f883a5b0ac97296d2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-rock-pi-4.dts
@@ -414,9 +414,65 @@
 };
 
 &i2c4 {
-	i2c-scl-rising-time-ns = <600>;
-	i2c-scl-falling-time-ns = <20>;
+///	i2c-scl-rising-time-ns = <600>;
+///	i2c-scl-falling-time-ns = <20>;
+	clock-frequency = <400000>;
+
 	status = "okay";
+
+/*
+	rpicamv2: camera@10 {
+		compatible = "sony,imx219";
+		reg = <0x10>;
+		clocks = <&rpicamclk2>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&enable>;
+		status = "disabled";
+
+		rpicamclk2: camera-clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <25000000>;
+		};
+
+		port {
+			csi1_out_2: endpoint {
+				remote-endpoint = <&csi1_ep1>;
+				clock-lanes = <0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+*/
+
+	rpicamv1: camera@36 {
+		compatible = "ovti,ov5647";
+		reg = <0x36>;
+		clocks = <&rpicamclk1>;
+		pinctrl-names = "default";
+		status = "okay";
+
+		/* CAM_GPIO is an enable GPIO and
+		 * CAM_MCLK is actually controlling the red LED
+		 */
+		pinctrl-0 = <&enable>, <&camled>;
+		enable-gpios = <&gpio1 RK_PB5 GPIO_ACTIVE_HIGH>;
+
+		/* The camera module has an internal clock */
+		rpicamclk1: camera-clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <25000000>;
+		};
+
+		port {
+			csi1_out_1: endpoint {
+				remote-endpoint = <&csi1_ep1>;
+				clock-lanes = <0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
 };
 
 &i2s0 {
@@ -482,6 +538,17 @@
 			rockchip,pins = <4 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>;
 		};
 	};
+
+	cam_pins {
+		enable: enable-pins {
+			rockchip,pins =
+				<1 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+		camled: cam_led_pins {
+			rockchip,pins =
+				<2 RK_PB3 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
 };
 
 &pwm2 {
@@ -610,3 +677,28 @@
 &vopl_mmu {
 	status = "okay";
 };
+
+&i2c6 {
+	status = "okay";
+};
+
+&i2c4 {
+};
+
+&mipi_dphy_rx0 {
+	status = "okay";
+};
+
+&isp0 {
+	status = "okay";
+
+	ports {
+		port@0 {
+			csi1_ep1: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&csi1_out_1>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+};
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 79ce9ec6fc1be1e612af10ec96cbbb166502033c..1ad17c6755cb50b8dbba0d392e5c3d8131788aa9 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -575,6 +575,18 @@ config VIDEO_IMX214
 	  To compile this driver as a module, choose M here: the
 	  module will be called imx214.
 
+config VIDEO_IMX219
+	tristate "Sony IMX219 sensor support"
+	depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	depends on V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the Sony
+	  IMX214 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called imx219.
+
 config VIDEO_IMX258
 	tristate "Sony IMX258 sensor support"
 	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index fd4ea86dedd5df448ced54e00cfc061cc77a26fe..f1dece3de91f9376d2a805203344ba4f845f04d7 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_VIDEO_ML86V7667)	+= ml86v7667.o
 obj-$(CONFIG_VIDEO_OV2659)	+= ov2659.o
 obj-$(CONFIG_VIDEO_TC358743)	+= tc358743.o
 obj-$(CONFIG_VIDEO_IMX214)	+= imx214.o
+obj-$(CONFIG_VIDEO_IMX219)	+= imx219.o
 obj-$(CONFIG_VIDEO_IMX258)	+= imx258.o
 obj-$(CONFIG_VIDEO_IMX274)	+= imx274.o
 obj-$(CONFIG_VIDEO_IMX319)	+= imx319.o
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index 4589631798c968a41116169ccdd9e6cd9b26b800..13e19e973aa2a0f060d3e84572fc33295db206aa 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -32,26 +32,33 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-image-sizes.h>
 #include <media/v4l2-mediabus.h>
+#include <media/v4l2-ctrls.h>
 
 #define SENSOR_NAME "ov5647"
 
+#define REG_NULL 0xffff
+
 #define MIPI_CTRL00_CLOCK_LANE_GATE		BIT(5)
 #define MIPI_CTRL00_BUS_IDLE			BIT(2)
 #define MIPI_CTRL00_CLOCK_LANE_DISABLE		BIT(0)
 
 #define OV5647_SW_STANDBY		0x0100
 #define OV5647_SW_RESET			0x0103
-#define OV5647_REG_CHIPID_H		0x300A
-#define OV5647_REG_CHIPID_L		0x300B
-#define OV5640_REG_PAD_OUT		0x300D
+#define OV5647_REG_CHIPID_H		0x300a
+#define OV5647_REG_CHIPID_L		0x300b
+#define OV5647_REG_PAD_OUT		0x300d
+
+#define OV5647_REG_TIMING_DVPHO		0x3808
+#define OV5647_REG_TIMING_DVPVO		0x380a
+#define OV5647_REG_TIMING_HTS		0x380c
+#define OV5647_REG_TIMING_VTS		0x380e
+#define OV5647_REG_TIMING_TC_REG20	0x3820
+#define OV5647_REG_TIMING_TC_REG21	0x3821
+
 #define OV5647_REG_FRAME_OFF_NUMBER	0x4202
 #define OV5647_REG_MIPI_CTRL00		0x4800
 #define OV5647_REG_MIPI_CTRL14		0x4814
 
-#define REG_TERM 0xfffe
-#define VAL_TERM 0xfe
-#define REG_DLY  0xffff
-
 #define OV5647_ROW_START		0x01
 #define OV5647_ROW_START_MIN		0
 #define OV5647_ROW_START_MAX		2004
@@ -77,6 +84,15 @@ struct regval_list {
 	u8 data;
 };
 
+struct ov5647_mode {
+	unsigned int width;
+	unsigned int height;
+	unsigned int max_fps;
+	unsigned int htot;
+	unsigned int vtot;
+	const struct regval_list *reg_list;
+};
+
 struct ov5647 {
 	struct v4l2_subdev		sd;
 	struct media_pad		pad;
@@ -86,6 +102,10 @@ struct ov5647 {
 	unsigned int			height;
 	int				power_count;
 	struct clk			*xclk;
+	struct v4l2_ctrl		*pixel_rate;
+	const struct ov5647_mode	*cur_mode;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct gpio_desc *enable_gpio;
 };
 
 static inline struct ov5647 *to_state(struct v4l2_subdev *sd)
@@ -97,71 +117,34 @@ static struct regval_list sensor_oe_disable_regs[] = {
 	{0x3000, 0x00},
 	{0x3001, 0x00},
 	{0x3002, 0x00},
+	{REG_NULL, 0x00}
 };
 
 static struct regval_list sensor_oe_enable_regs[] = {
 	{0x3000, 0x0f},
 	{0x3001, 0xff},
 	{0x3002, 0xe4},
+	{REG_NULL, 0x00}
 };
 
-static struct regval_list ov5647_640x480[] = {
-	{0x0100, 0x00},
-	{0x0103, 0x01},
-	{0x3034, 0x08},
-	{0x3035, 0x21},
-	{0x3036, 0x46},
-	{0x303c, 0x11},
-	{0x3106, 0xf5},
-	{0x3821, 0x07},
-	{0x3820, 0x41},
-	{0x3827, 0xec},
+static struct regval_list ov5647_init_regs[] = {
+	/* upstream */
 	{0x370c, 0x0f},
-	{0x3612, 0x59},
-	{0x3618, 0x00},
 	{0x5000, 0x06},
-	{0x5001, 0x01},
-	{0x5002, 0x41},
 	{0x5003, 0x08},
 	{0x5a00, 0x08},
 	{0x3000, 0x00},
 	{0x3001, 0x00},
 	{0x3002, 0x00},
-	{0x3016, 0x08},
-	{0x3017, 0xe0},
-	{0x3018, 0x44},
-	{0x301c, 0xf8},
 	{0x301d, 0xf0},
 	{0x3a18, 0x00},
 	{0x3a19, 0xf8},
 	{0x3c01, 0x80},
 	{0x3b07, 0x0c},
-	{0x380c, 0x07},
-	{0x380d, 0x68},
-	{0x380e, 0x03},
-	{0x380f, 0xd8},
-	{0x3814, 0x31},
-	{0x3815, 0x31},
-	{0x3708, 0x64},
-	{0x3709, 0x52},
-	{0x3808, 0x02},
-	{0x3809, 0x80},
-	{0x380a, 0x01},
-	{0x380b, 0xE0},
-	{0x3801, 0x00},
-	{0x3802, 0x00},
-	{0x3803, 0x00},
-	{0x3804, 0x0a},
-	{0x3805, 0x3f},
-	{0x3806, 0x07},
-	{0x3807, 0xa1},
-	{0x3811, 0x08},
-	{0x3813, 0x02},
 	{0x3630, 0x2e},
 	{0x3632, 0xe2},
 	{0x3633, 0x23},
 	{0x3634, 0x44},
-	{0x3636, 0x06},
 	{0x3620, 0x64},
 	{0x3621, 0xe0},
 	{0x3600, 0x37},
@@ -175,12 +158,6 @@ static struct regval_list ov5647_640x480[] = {
 	{0x3f05, 0x02},
 	{0x3f06, 0x10},
 	{0x3f01, 0x0a},
-	{0x3a08, 0x01},
-	{0x3a09, 0x27},
-	{0x3a0a, 0x00},
-	{0x3a0b, 0xf6},
-	{0x3a0d, 0x04},
-	{0x3a0e, 0x03},
 	{0x3a0f, 0x58},
 	{0x3a10, 0x50},
 	{0x3a1b, 0x58},
@@ -188,55 +165,271 @@ static struct regval_list ov5647_640x480[] = {
 	{0x3a11, 0x60},
 	{0x3a1f, 0x28},
 	{0x4001, 0x02},
-	{0x4004, 0x02},
 	{0x4000, 0x09},
-	{0x4837, 0x24},
+	{0x5001, 0x01},		/* auto white balance */
+	{0x5002, 0x41},
+	{0x3011, 0x62},
+	/* mipi */
+	{0x3016, 0x08},
+	{0x3017, 0xe0},
+	{0x3018, 0x44},
+	{0x3034, 0x08},
+	{0x3106, 0xf5},
+	{REG_NULL, 0x00}
+};
+
+static struct regval_list ov5647_640x480[] = {
+	{0x3035, 0x21},
+	{0x3036, 0x60},
+	{0x303c, 0x11},
+	{0x3821, 0x07},
+	{0x3820, 0x41},
+	{0x3827, 0xec}, /* ? */
+	{0x370c, 0x0f}, /* ? */
+	{0x3612, 0x59},
+	{0x3618, 0x00},
+	{0x301c, 0xf8}, /* ? */
+	{0x380c, 0x07}, /* 0768 */
+	{0x380d, 0x68},
+	{0x380e, 0x03}, /* 03d8 */
+	{0x380f, 0xd8},
+	{0x3814, 0x31},
+	{0x3815, 0x31},
+	{0x3708, 0x64},
+	{0x3709, 0x52},
+	{0x3808, 0x02}, /* width = 640 */
+	{0x3809, 0x80},
+	{0x380a, 0x01}, /* heigth = 480 */
+	{0x380b, 0xE0},
+	{0x3800, 0x00},	/* X Start */
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xa1},
+	{0x3811, 0x08},
+	{0x3813, 0x02},
+	{0x3636, 0x06},
+	{0x3a08, 0x01},
+	{0x3a09, 0x27},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xf6},
+	{0x3a0d, 0x04},
+	{0x3a0e, 0x03},
+	{0x4004, 0x02},
+	{0x4837, 0x0b},		/* MIPI pclk period */
 	{0x4050, 0x6e},
 	{0x4051, 0x8f},
-	{0x0100, 0x01},
+	{REG_NULL, 0x00}
+};
+
+static struct regval_list ov5647_1280x960[] = {
+	{0x3035, 0x21},		/* PLL */
+	{0x3036, 0x60},		/* PLL */
+	{0x303c, 0x11},		/* PLL */
+	{0x3821, 0x07},		/* ISP mirror on, Sensor mirror on, H bin on */
+	{0x3820, 0x41},		/* ISP flip off, Sensor flip off, V bin on */
+	{0x3612, 0x59},		/* analog control */
+	{0x3618, 0x00},		/* analog control */
+	{0x3814, 0x31},		/* X INC */
+	{0x3815, 0x31},		/* Y INC */
+	{0x3708, 0x64},		/* analog control */
+	{0x3709, 0x52},		/* analog control */
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x06},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0x9d},
+	/* banding filter */
+	{0x3a08, 0x01},		/* B50 */
+	{0x3a09, 0x27},		/* B50 */
+	{0x3a0a, 0x00},		/* B60 */
+	{0x3a0b, 0xf6},		/* B60 */
+	{0x3a0d, 0x04},		/* B60 max */
+	{0x3a0e, 0x03},		/* B50 max */
+	{0x4004, 0x02},		/* black line number */
+	{0x4837, 0x0b},		/* MIPI pclk period */
+	{REG_NULL, 0x00}
+};
+
+static struct regval_list ov5647_2592x1944[] = {
+	{0x3035, 0x21},
+	{0x3036, 0x70},
+	{0x303c, 0x11},
+	{0x3612, 0x5b},
+	{0x3618, 0x04},
+	{0x380c, 0x0a},
+	{0x380d, 0x8c},
+	{0x380e, 0x07},
+	{0x380f, 0xb6},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3708, 0x64},
+	{0x3709, 0x12},
+	{0x3808, 0x0a},
+	{0x3809, 0x20},
+	{0x380a, 0x07},
+	{0x380b, 0x98},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x04},
+	{0x3804, 0x0a},
+	{0x3805, 0x33},
+	{0x3806, 0x07},
+	{0x3807, 0xa3},
+	{0x3a08, 0x01},
+	{0x3a09, 0x28},
+	{0x3a0a, 0x00},
+	{0x3a0b, 0xf6},
+	{0x3a0d, 0x08},
+	{0x3a0e, 0x06},
+	{0x4004, 0x04},
+	{0x4837, 0x0b},	/* MIPI pclk period */
+	{REG_NULL, 0x00}
+};
+
+static const struct ov5647_mode supported_modes[] = {
+	{
+		.width = 1280,
+		.height = 960,
+		.max_fps = 45,
+		.htot = 1896,
+		.vtot = 984,
+		.reg_list = ov5647_1280x960,
+	},
+	{
+		.width = 2592,
+		.height = 1944,
+		.max_fps = 15,
+		.htot = 2592,
+		.vtot = 1944,
+		.reg_list = ov5647_2592x1944,
+	},
 };
 
 static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
 {
-	int ret;
-	unsigned char data[3] = { reg >> 8, reg & 0xff, val};
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg;
+	u8 buf[3];
+	int ret;
 
-	ret = i2c_master_send(client, data, 3);
-	if (ret < 0)
-		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
-				__func__, reg);
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+	buf[2] = val;
 
-	return ret;
+	msg.addr = client->addr;
+	msg.flags = client->flags;
+	msg.buf = buf;
+	msg.len = sizeof(buf);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: error: reg=%x, val=%x\n",
+			__func__, reg, val);
+		return ret;
+	}
+
+	return 0;
 }
 
 static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val)
 {
+	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[2];
+	u8 buf[2];
 	int ret;
-	unsigned char data_w[2] = { reg >> 8, reg & 0xff };
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags;
+	msg[0].buf = buf;
+	msg[0].len = sizeof(buf);
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags | I2C_M_RD;
+	msg[1].buf = buf;
+	msg[1].len = 1;
+
+	ret = i2c_transfer(client->adapter, msg, 2);
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: error: reg=%x\n",
+			__func__, reg);
+		return ret;
+	}
+
+	*val = buf[0];
+	return 0;
+}
+
+static int ov5647_read16(struct v4l2_subdev *sd, u16 reg, u16 *val)
+{
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct i2c_msg msg[4];
+	u8 buflo[2], bufhi[2];
+	int ret;
+
+	buflo[0] = reg >> 8;
+	buflo[1] = reg & 0xff;
+	bufhi[0] = (reg + 1) >> 8;
+	bufhi[1] = (reg + 1) & 0xff;
+
+	msg[0].addr = client->addr;
+	msg[0].flags = client->flags;
+	msg[0].buf = buflo;
+	msg[0].len = sizeof(buflo);
+
+	msg[1].addr = client->addr;
+	msg[1].flags = client->flags | I2C_M_RD;
+	msg[1].buf = buflo;
+	msg[1].len = 1;
 
-	ret = i2c_master_send(client, data_w, 2);
+	msg[2].addr = client->addr;
+	msg[2].flags = client->flags;
+	msg[2].buf = bufhi;
+	msg[2].len = sizeof(bufhi);
+
+	msg[3].addr = client->addr;
+	msg[3].flags = client->flags | I2C_M_RD;
+	msg[3].buf = bufhi;
+	msg[3].len = 1;
+
+	ret = i2c_transfer(client->adapter, msg, 4);
 	if (ret < 0) {
-		dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",
+		dev_err(&client->dev, "%s: error: reg=%x\n",
 			__func__, reg);
 		return ret;
 	}
 
-	ret = i2c_master_recv(client, val, 1);
-	if (ret < 0)
-		dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n",
-				__func__, reg);
+	*val = ((u16)bufhi[0] << 8) | (u16)buflo;
+	return 0;
+}
 
-	return ret;
+static int ov5647_write16(struct v4l2_subdev *sd, u16 reg, u16 val)
+{
+	int ret;
+
+	ret = ov5647_write(sd, reg, val >> 8);
+	if (ret)
+		return ret;
+
+	return ov5647_write(sd, reg + 1, val & 0xff);
 }
 
 static int ov5647_write_array(struct v4l2_subdev *sd,
-				struct regval_list *regs, int array_size)
+			      const struct regval_list *regs)
 {
 	int i, ret;
 
-	for (i = 0; i < array_size; i++) {
+	for (i = 0; regs[i].addr != REG_NULL; i++) {
 		ret = ov5647_write(sd, regs[i].addr, regs[i].data);
 		if (ret < 0)
 			return ret;
@@ -245,6 +438,51 @@ static int ov5647_write_array(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int ov5647_mod_reg(struct v4l2_subdev *sd, u16 reg,
+			  u8 mask, u8 val)
+{
+	u8 readval;
+	int ret;
+
+	ret = ov5647_read(sd, reg, &readval);
+	if (ret)
+		return ret;
+
+	readval &= ~mask;
+	val &= mask;
+	val |= readval;
+
+	return ov5647_write(sd, reg, val);
+}
+
+static int ov5647_sw_reset(struct v4l2_subdev *sd)
+{
+	int ret;
+
+	ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
+	if (ret < 0)
+		return ret;
+	msleep(5);
+	return 0;
+}
+
+static int ov5647_set_sw_standby(struct v4l2_subdev *sd, bool standby)
+{
+	int ret;
+	u8 rdval;
+
+	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+	if (ret < 0)
+		return ret;
+
+	if (standby)
+		rdval &= ~0x01;
+	else
+		rdval |= 0x01;
+
+	return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
+}
+
 static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
 {
 	u8 channel_id;
@@ -270,13 +508,21 @@ static int ov5647_stream_on(struct v4l2_subdev *sd)
 	if (ret < 0)
 		return ret;
 
-	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x00);
+	ret = ov5647_write(sd, OV5647_REG_PAD_OUT, 0x00);
+	if (ret < 0)
+		return ret;
+
+	return ov5647_set_sw_standby(sd, false);
 }
 
 static int ov5647_stream_off(struct v4l2_subdev *sd)
 {
 	int ret;
 
+	ret = ov5647_set_sw_standby(sd, true);
+	if (ret < 0)
+		return ret;
+
 	ret = ov5647_write(sd, OV5647_REG_MIPI_CTRL00, MIPI_CTRL00_CLOCK_LANE_GATE
 			   | MIPI_CTRL00_BUS_IDLE | MIPI_CTRL00_CLOCK_LANE_DISABLE);
 	if (ret < 0)
@@ -286,57 +532,62 @@ static int ov5647_stream_off(struct v4l2_subdev *sd)
 	if (ret < 0)
 		return ret;
 
-	return ov5647_write(sd, OV5640_REG_PAD_OUT, 0x01);
+	return ov5647_write(sd, OV5647_REG_PAD_OUT, 0x01);
 }
 
-static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
+static int ov5647_set_timings(struct v4l2_subdev *sd,
+			      const struct ov5647_mode *mode)
 {
 	int ret;
-	u8 rdval;
 
-	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+	ret = ov5647_write16(sd, OV5647_REG_TIMING_DVPHO, mode->width);
 	if (ret < 0)
 		return ret;
 
-	if (standby)
-		rdval &= ~0x01;
-	else
-		rdval |= 0x01;
+	ret = ov5647_write16(sd, OV5647_REG_TIMING_DVPVO, mode->height);
+	if (ret < 0)
+		return ret;
 
-	return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
+	ret = ov5647_write16(sd, OV5647_REG_TIMING_HTS, mode->htot);
+	if (ret < 0)
+		return ret;
+
+	return ov5647_write16(sd, OV5647_REG_TIMING_VTS, mode->vtot);
 }
 
 static int __sensor_init(struct v4l2_subdev *sd)
 {
-	int ret;
-	u8 resetval, rdval;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	struct ov5647 *sensor = to_state(sd);
+	int ret;
 
-	ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
+	ret = ov5647_set_sw_standby(sd, true);
 	if (ret < 0)
 		return ret;
 
-	ret = ov5647_write_array(sd, ov5647_640x480,
-					ARRAY_SIZE(ov5647_640x480));
+	ret = ov5647_sw_reset(sd);
+	if (ret < 0)
+		return ret;
+
+	ret = ov5647_write_array(sd, ov5647_init_regs);
 	if (ret < 0) {
 		dev_err(&client->dev, "write sensor default regs error\n");
 		return ret;
 	}
 
-	ret = ov5647_set_virtual_channel(sd, 0);
-	if (ret < 0)
+	ret = ov5647_write_array(sd, sensor->cur_mode->reg_list);
+	if (ret < 0) {
+		dev_err(&client->dev, "write sensor mode regs error\n");
 		return ret;
+	}
 
-	ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
+	ret = ov5647_set_timings(sd, sensor->cur_mode);
 	if (ret < 0)
 		return ret;
 
-	if (!(resetval & 0x01)) {
-		dev_err(&client->dev, "Device was in SW standby");
-		ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
-		if (ret < 0)
-			return ret;
-	}
+	ret = ov5647_set_virtual_channel(sd, 0);
+	if (ret < 0)
+		return ret;
 
 	/*
 	 * stream off to make the clock lane into LP-11 state.
@@ -361,8 +612,10 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
 			goto out;
 		}
 
-		ret = ov5647_write_array(sd, sensor_oe_enable_regs,
-				ARRAY_SIZE(sensor_oe_enable_regs));
+		gpiod_set_value_cansleep(ov5647->enable_gpio, 1);
+		msleep(20);
+
+		ret = ov5647_write_array(sd, sensor_oe_enable_regs);
 		if (ret < 0) {
 			clk_disable_unprepare(ov5647->xclk);
 			dev_err(&client->dev,
@@ -380,18 +633,12 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
 	} else if (!on && ov5647->power_count == 1) {
 		dev_dbg(&client->dev, "OV5647 power off\n");
 
-		ret = ov5647_write_array(sd, sensor_oe_disable_regs,
-				ARRAY_SIZE(sensor_oe_disable_regs));
-
+		ret = ov5647_write_array(sd, sensor_oe_disable_regs);
 		if (ret < 0)
 			dev_dbg(&client->dev, "disable oe failed\n");
 
-		ret = set_sw_standby(sd, true);
-
-		if (ret < 0)
-			dev_dbg(&client->dev, "soft stby failed\n");
-
 		clk_disable_unprepare(ov5647->xclk);
+		gpiod_set_value_cansleep(ov5647->enable_gpio, 0);
 	}
 
 	/* Update the power count. */
@@ -451,6 +698,91 @@ static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
 	.s_stream =		ov5647_s_stream,
 };
 
+static int ov5647_get_reso_dist(const struct ov5647_mode *mode,
+				struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct ov5647_mode *
+ov5647_find_best_fit(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;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = ov5647_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int ov5647_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov5647 *ov5647 = to_state(sd);
+	const struct ov5647_mode *mode;
+	s64 pixel_rate;
+	int ret = 0;
+
+	mutex_lock(&ov5647->lock);
+
+	mode = ov5647_find_best_fit(fmt);
+	fmt->format.code = MEDIA_BUS_FMT_SBGGR8_1X8;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+	} else {
+		ov5647->cur_mode = mode;
+		pixel_rate = mode->vtot * mode->htot * mode->max_fps;
+		ret = v4l2_ctrl_s_ctrl_int64(ov5647->pixel_rate,
+					     pixel_rate);
+		if (ret < 0)
+			goto err_unlock;
+		pr_info("new mode is %dx%d\n", mode->width, mode->height);
+		pr_info("pixel rate is now %lld\n", pixel_rate);
+	}
+
+err_unlock:
+	mutex_unlock(&ov5647->lock);
+
+	return ret;
+}
+
+static int ov5647_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov5647 *ov5647 = to_state(sd);
+	const struct ov5647_mode *mode = ov5647->cur_mode;
+
+	mutex_lock(&ov5647->lock);
+
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = MEDIA_BUS_FMT_SBGGR8_1X8;
+		fmt->format.field = V4L2_FIELD_NONE;
+	}
+
+	mutex_unlock(&ov5647->lock);
+
+	return 0;
+}
+
 static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
 				struct v4l2_subdev_pad_config *cfg,
 				struct v4l2_subdev_mbus_code_enum *code)
@@ -463,8 +795,29 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
 	return 0;
 }
 
+static int ov5647_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != MEDIA_BUS_FMT_SBGGR8_1X8)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
 static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
 	.enum_mbus_code = ov5647_enum_mbus_code,
+	.enum_frame_size = ov5647_enum_frame_sizes,
+	.get_fmt = ov5647_get_fmt,
+	.set_fmt = ov5647_set_fmt,
 };
 
 static const struct v4l2_subdev_ops ov5647_subdev_ops = {
@@ -475,33 +828,23 @@ static const struct v4l2_subdev_ops ov5647_subdev_ops = {
 
 static int ov5647_detect(struct v4l2_subdev *sd)
 {
-	u8 read;
-	int ret;
 	struct i2c_client *client = v4l2_get_subdevdata(sd);
+	u16 chip_id;
+	int ret;
 
-	ret = ov5647_write(sd, OV5647_SW_RESET, 0x01);
-	if (ret < 0)
-		return ret;
-
-	ret = ov5647_read(sd, OV5647_REG_CHIPID_H, &read);
-	if (ret < 0)
-		return ret;
-
-	if (read != 0x56) {
-		dev_err(&client->dev, "ID High expected 0x56 got %x", read);
+	ret = ov5647_read16(sd, OV5647_REG_CHIPID_H, &chip_id);
+	if (ret) {
+		dev_err(&client->dev, "%s: failed to read chip identifier\n",
+			__func__);
 		return -ENODEV;
 	}
 
-	ret = ov5647_read(sd, OV5647_REG_CHIPID_L, &read);
-	if (ret < 0)
-		return ret;
-
-	if (read != 0x47) {
-		dev_err(&client->dev, "ID Low expected 0x47 got %x", read);
-		return -ENODEV;
+	if (chip_id != 0x5647) {
+		dev_err(&client->dev, "%s: wrong chip identifier, expected 0x5647, got 0x%x\n",
+			__func__, chip_id);
+		ret = -ENXIO;
 	}
-
-	return ov5647_write(sd, OV5647_SW_RESET, 0x00);
+	return 0;
 }
 
 static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
@@ -532,7 +875,7 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
 
 static int ov5647_parse_dt(struct device_node *np)
 {
-	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
+	struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_CSI2_DPHY };
 	struct device_node *ep;
 
 	int ret;
@@ -544,6 +887,7 @@ static int ov5647_parse_dt(struct device_node *np)
 	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);
 
 	of_node_put(ep);
+
 	return ret;
 }
 
@@ -552,14 +896,16 @@ static int ov5647_probe(struct i2c_client *client,
 {
 	struct device *dev = &client->dev;
 	struct ov5647 *sensor;
-	int ret;
 	struct v4l2_subdev *sd;
+	struct v4l2_ctrl_handler *handler;
 	struct device_node *np = client->dev.of_node;
 	u32 xclk_freq;
+	int ret;
 
 	sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
 	if (!sensor)
 		return -ENOMEM;
+	sensor->cur_mode = &supported_modes[0];
 
 	if (IS_ENABLED(CONFIG_OF) && np) {
 		ret = ov5647_parse_dt(np);
@@ -582,28 +928,54 @@ static int ov5647_probe(struct i2c_client *client,
 		return -EINVAL;
 	}
 
+	sensor->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_HIGH);
+	if (IS_ERR(sensor->enable_gpio)) {
+		dev_err(dev, "cannot get enable gpio\n");
+		return PTR_ERR(sensor->enable_gpio);
+	}
+	msleep(20);
+
 	mutex_init(&sensor->lock);
 
 	sd = &sensor->sd;
 	v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
+
+	handler = &sensor->ctrl_handler;
+	ret = v4l2_ctrl_handler_init(handler, 1);
+	if (ret)
+		return ret;
+	sensor->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
+					V4L2_CID_PIXEL_RATE,
+					1, INT_MAX, 1, 1);
+	sensor->sd.ctrl_handler = handler;
+
 	sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
 	sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
 	sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
 	ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);
-	if (ret < 0)
+	if (ret < 0) {
+		dev_err(dev, "cannot register media entity\n");
 		goto mutex_remove;
+	}
 
-	ret = ov5647_detect(sd);
+	ret = ov5647_sw_reset(sd);
 	if (ret < 0)
+		return ret;
+
+	ret = ov5647_detect(sd);
+	if (ret < 0) {
+		dev_err(&client->dev, "not detected!");
 		goto error;
+	}
+
+	/* TODO: power off here */
 
 	ret = v4l2_async_register_subdev(sd);
 	if (ret < 0)
 		goto error;
 
-	dev_dbg(dev, "OmniVision OV5647 camera driver probed\n");
+	dev_info(dev, "OV5647 detected at address 0x%02x\n", client->addr);
 	return 0;
 error:
 	media_entity_cleanup(&sd->entity);