devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor
@ 2017-12-29  8:08 Shunqian Zheng
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2018-01-03 14:30 ` Sakari Ailus
  0 siblings, 2 replies; 15+ messages in thread
From: Shunqian Zheng @ 2017-12-29  8:08 UTC (permalink / raw)
  To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng

This patch adds driver for Omnivision's ov5695 sensor,
the driver supports following features:
 - supported resolutions
   + 2592x1944 at 30fps
   + 1920x1080 at 30fps
   + 1296x972 at 60fps
   + 1280x720 at 30fps
   + 640x480 at 120fps
 - test patterns
 - manual exposure/gain(analog and digital) control
 - vblank and hblank
 - media controller
 - runtime pm

Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/media/i2c/Kconfig  |   11 +
 drivers/media/i2c/Makefile |    1 +
 drivers/media/i2c/ov5695.c | 1396 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1408 insertions(+)
 create mode 100644 drivers/media/i2c/ov5695.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 3c6d642..55b37c8 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -645,6 +645,17 @@ config VIDEO_OV5670
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov5670.
 
+config VIDEO_OV5695
+	tristate "OmniVision OV5695 sensor support"
+	depends on I2C && VIDEO_V4L2
+	depends on MEDIA_CAMERA_SUPPORT
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV5695 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov5695.
+
 config VIDEO_OV7640
 	tristate "OmniVision OV7640 sensor support"
 	depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 548a9ef..a063030 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
 obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
 obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
 obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
 obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
 obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
 obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
new file mode 100644
index 0000000..4745da4
--- /dev/null
+++ b/drivers/media/i2c/ov5695.c
@@ -0,0 +1,1396 @@
+/*
+ * ov5695 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#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 <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+/* 45Mhz * 4 Binning */
+#define OV5695_PIXEL_RATE		(45 * 1000 * 1000 * 4)
+#define CHIP_ID				0x005695
+#define OV5695_REG_CHIP_ID		0x300a
+
+#define OV5695_REG_CTRL_MODE		0x0100
+#define OV5695_MODE_SW_STANDBY		0x0
+#define OV5695_MODE_STREAMING		BIT(0)
+
+#define OV5695_REG_EXPOSURE		0x3500
+#define	OV5695_EXPOSURE_MIN		4
+#define	OV5695_EXPOSURE_STEP		1
+#define OV5695_VTS_MAX			0x7fff
+
+#define OV5695_REG_ANALOG_GAIN		0x3509
+#define	ANALOG_GAIN_MIN			0x10
+#define	ANALOG_GAIN_MAX			0xf8
+#define	ANALOG_GAIN_STEP		1
+#define	ANALOG_GAIN_DEFAULT		0xf8
+
+#define OV5695_REG_DIGI_GAIN_H		0x350a
+#define OV5695_REG_DIGI_GAIN_L		0x350b
+#define OV5695_DIGI_GAIN_L_MASK		0x3f
+#define OV5695_DIGI_GAIN_H_SHIFT	6
+#define OV5695_DIGI_GAIN_MIN		0
+#define OV5695_DIGI_GAIN_MAX		(0x4000 - 1)
+#define OV5695_DIGI_GAIN_STEP		1
+#define OV5695_DIGI_GAIN_DEFAULT	1024
+
+#define OV5695_REG_TEST_PATTERN		0x4503
+#define	OV5695_TEST_PATTERN_ENABLE	0x80
+#define	OV5695_TEST_PATTERN_DISABLE	0x0
+
+#define OV5695_REG_VTS			0x380e
+
+#define REG_NULL			0xFFFF
+
+#define OV5695_REG_VALUE_08BIT		1
+#define OV5695_REG_VALUE_16BIT		2
+#define OV5695_REG_VALUE_24BIT		3
+
+#define OV5695_LANES			2
+#define OV5695_BITS_PER_SAMPLE		10
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct ov5695_mode {
+	u32 width;
+	u32 height;
+	u32 max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+};
+
+struct ov5695 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct regulator        *avdd_regulator;
+	struct regulator        *dovdd_regulator;
+	struct regulator        *dvdd_regulator;
+	struct gpio_desc	*reset_gpio;
+
+	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	*test_pattern;
+	struct mutex		mutex;
+	bool			streaming;
+	const struct ov5695_mode *cur_mode;
+};
+#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_global_regs[] = {
+	{0x0103, 0x01},
+	{0x0100, 0x00},
+	{0x0300, 0x04},
+	{0x0301, 0x00},
+	{0x0302, 0x69},
+	{0x0303, 0x00},
+	{0x0304, 0x00},
+	{0x0305, 0x01},
+	{0x0307, 0x00},
+	{0x030b, 0x00},
+	{0x030c, 0x00},
+	{0x030d, 0x1e},
+	{0x030e, 0x04},
+	{0x030f, 0x03},
+	{0x0312, 0x01},
+	{0x3000, 0x00},
+	{0x3002, 0xa1},
+	{0x0308, 0x00},
+	{0x0310, 0x00},
+	{0x3022, 0x51},
+	{0x3106, 0x15},
+	{0x3107, 0x01},
+	{0x3108, 0x05},
+	{0x3500, 0x00},
+	{0x3501, 0x45},
+	{0x3502, 0x00},
+	{0x3503, 0x08},
+	{0x3504, 0x03},
+	{0x3505, 0x8c},
+	{0x3507, 0x03},
+	{0x3508, 0x00},
+	{0x3509, 0x10},
+	{0x350c, 0x00},
+	{0x350d, 0x80},
+	{0x3510, 0x00},
+	{0x3511, 0x02},
+	{0x3512, 0x00},
+	{0x3601, 0x55},
+	{0x3602, 0x58},
+	{0x3614, 0x30},
+	{0x3615, 0x77},
+	{0x3621, 0x08},
+	{0x3624, 0x40},
+	{0x3633, 0x0c},
+	{0x3634, 0x0c},
+	{0x3635, 0x0c},
+	{0x3636, 0x0c},
+	{0x3638, 0x00},
+	{0x3639, 0x00},
+	{0x363a, 0x00},
+	{0x363b, 0x00},
+	{0x363c, 0xff},
+	{0x363d, 0xfa},
+	{0x3650, 0x44},
+	{0x3651, 0x44},
+	{0x3652, 0x44},
+	{0x3653, 0x44},
+	{0x3654, 0x44},
+	{0x3655, 0x44},
+	{0x3656, 0x44},
+	{0x3657, 0x44},
+	{0x3660, 0x00},
+	{0x3661, 0x00},
+	{0x3662, 0x00},
+	{0x366a, 0x00},
+	{0x366e, 0x0c},
+	{0x3673, 0x04},
+	{0x3700, 0x14},
+	{0x3703, 0x0c},
+	{0x3715, 0x01},
+	{0x3733, 0x10},
+	{0x3734, 0x40},
+	{0x373f, 0xa0},
+	{0x3765, 0x20},
+	{0x37a1, 0x1d},
+	{0x37a8, 0x26},
+	{0x37ab, 0x14},
+	{0x37c2, 0x04},
+	{0x37cb, 0x09},
+	{0x37cc, 0x13},
+	{0x37cd, 0x1f},
+	{0x37ce, 0x1f},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xaf},
+	{0x3808, 0x05},
+	{0x3809, 0x10},
+	{0x380a, 0x03},
+	{0x380b, 0xcc},
+	{0x380c, 0x02},
+	{0x380d, 0xa0},
+	{0x380e, 0x08},
+	{0x380f, 0xb8},
+	{0x3810, 0x00},
+	{0x3811, 0x06},
+	{0x3812, 0x00},
+	{0x3813, 0x06},
+	{0x3814, 0x03},
+	{0x3815, 0x01},
+	{0x3816, 0x03},
+	{0x3817, 0x01},
+	{0x3818, 0x00},
+	{0x3819, 0x00},
+	{0x381a, 0x00},
+	{0x381b, 0x01},
+	{0x3820, 0x8b},
+	{0x3821, 0x01},
+	{0x3c80, 0x08},
+	{0x3c82, 0x00},
+	{0x3c83, 0x00},
+	{0x3c88, 0x00},
+	{0x3d85, 0x14},
+	{0x3f02, 0x08},
+	{0x3f03, 0x10},
+	{0x4008, 0x02},
+	{0x4009, 0x09},
+	{0x404e, 0x20},
+	{0x4501, 0x00},
+	{0x4502, 0x10},
+	{0x4800, 0x00},
+	{0x481f, 0x2a},
+	{0x4837, 0x13},
+	{0x5000, 0x17},
+	{0x5780, 0x3e},
+	{0x5781, 0x0f},
+	{0x5782, 0x44},
+	{0x5783, 0x02},
+	{0x5784, 0x01},
+	{0x5785, 0x01},
+	{0x5786, 0x00},
+	{0x5787, 0x04},
+	{0x5788, 0x02},
+	{0x5789, 0x0f},
+	{0x578a, 0xfd},
+	{0x578b, 0xf5},
+	{0x578c, 0xf5},
+	{0x578d, 0x03},
+	{0x578e, 0x08},
+	{0x578f, 0x0c},
+	{0x5790, 0x08},
+	{0x5791, 0x06},
+	{0x5792, 0x00},
+	{0x5793, 0x52},
+	{0x5794, 0xa3},
+	{0x5b00, 0x00},
+	{0x5b01, 0x1c},
+	{0x5b02, 0x00},
+	{0x5b03, 0x7f},
+	{0x5b05, 0x6c},
+	{0x5e10, 0xfc},
+	{0x4010, 0xf1},
+	{0x3503, 0x08},
+	{0x3505, 0x8c},
+	{0x3507, 0x03},
+	{0x3508, 0x00},
+	{0x3509, 0xf8},
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x2e4)
+ * framelength 2024(0x7e8)
+ * grabwindow_width 2592
+ * grabwindow_height 1944
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_2592x1944_regs[] = {
+	{0x3501, 0x7e},
+	{0x366e, 0x18},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x04},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xab},
+	{0x3808, 0x0a},
+	{0x3809, 0x20},
+	{0x380a, 0x07},
+	{0x380b, 0x98},
+	{0x380c, 0x02},
+	{0x380d, 0xe4},
+	{0x380e, 0x07},
+	{0x380f, 0xe8},
+	{0x3811, 0x06},
+	{0x3813, 0x08},
+	{0x3814, 0x01},
+	{0x3816, 0x01},
+	{0x3817, 0x01},
+	{0x3820, 0x88},
+	{0x3821, 0x00},
+	{0x4501, 0x00},
+	{0x4008, 0x04},
+	{0x4009, 0x13},
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1920
+ * grabwindow_height 1080
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1920x1080_regs[] = {
+	{0x3501, 0x45},
+	{0x366e, 0x18},
+	{0x3800, 0x01},
+	{0x3801, 0x50},
+	{0x3802, 0x01},
+	{0x3803, 0xb8},
+	{0x3804, 0x08},
+	{0x3805, 0xef},
+	{0x3806, 0x05},
+	{0x3807, 0xf7},
+	{0x3808, 0x07},
+	{0x3809, 0x80},
+	{0x380a, 0x04},
+	{0x380b, 0x38},
+	{0x380c, 0x02},
+	{0x380d, 0xa0},
+	{0x380e, 0x08},
+	{0x380f, 0xb8},
+	{0x3811, 0x06},
+	{0x3813, 0x04},
+	{0x3814, 0x01},
+	{0x3816, 0x01},
+	{0x3817, 0x01},
+	{0x3820, 0x88},
+	{0x3821, 0x00},
+	{0x4501, 0x00},
+	{0x4008, 0x04},
+	{0x4009, 0x13},
+	{REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 740(0x02e4)
+ * framelength 1012(0x03f4)
+ * grabwindow_width 1296
+ * grabwindow_height 972
+ * max_framerate 60fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1296x972_regs[] = {
+	{0x0103, 0x01},
+	{0x0100, 0x00},
+	{0x0300, 0x04},
+	{0x0301, 0x00},
+	{0x0302, 0x69},
+	{0x0303, 0x00},
+	{0x0304, 0x00},
+	{0x0305, 0x01},
+	{0x0307, 0x00},
+	{0x030b, 0x00},
+	{0x030c, 0x00},
+	{0x030d, 0x1e},
+	{0x030e, 0x04},
+	{0x030f, 0x03},
+	{0x0312, 0x01},
+	{0x3000, 0x00},
+	{0x3002, 0x21},
+	{0x3016, 0x32},
+	{0x3022, 0x51},
+	{0x3106, 0x15},
+	{0x3107, 0x01},
+	{0x3108, 0x05},
+	{0x3500, 0x00},
+	{0x3501, 0x3e},
+	{0x3502, 0x00},
+	{0x3503, 0x08},
+	{0x3504, 0x03},
+	{0x3505, 0x8c},
+	{0x3507, 0x03},
+	{0x3508, 0x00},
+	{0x3509, 0x10},
+	{0x350c, 0x00},
+	{0x350d, 0x80},
+	{0x3510, 0x00},
+	{0x3511, 0x02},
+	{0x3512, 0x00},
+	{0x3601, 0x55},
+	{0x3602, 0x58},
+	{0x3611, 0x58},
+	{0x3614, 0x30},
+	{0x3615, 0x77},
+	{0x3621, 0x08},
+	{0x3624, 0x40},
+	{0x3633, 0x0c},
+	{0x3634, 0x0c},
+	{0x3635, 0x0c},
+	{0x3636, 0x0c},
+	{0x3638, 0x00},
+	{0x3639, 0x00},
+	{0x363a, 0x00},
+	{0x363b, 0x00},
+	{0x363c, 0xff},
+	{0x363d, 0xfa},
+	{0x3650, 0x44},
+	{0x3651, 0x44},
+	{0x3652, 0x44},
+	{0x3653, 0x44},
+	{0x3654, 0x44},
+	{0x3655, 0x44},
+	{0x3656, 0x44},
+	{0x3657, 0x44},
+	{0x3660, 0x00},
+	{0x3661, 0x00},
+	{0x3662, 0x00},
+	{0x366a, 0x00},
+	{0x366e, 0x0c},
+	{0x3673, 0x04},
+	{0x3700, 0x14},
+	{0x3703, 0x0c},
+	{0x3706, 0x24},
+	{0x3714, 0x27},
+	{0x3715, 0x01},
+	{0x3716, 0x00},
+	{0x3717, 0x02},
+	{0x3733, 0x10},
+	{0x3734, 0x40},
+	{0x373f, 0xa0},
+	{0x3765, 0x20},
+	{0x37a1, 0x1d},
+	{0x37a8, 0x26},
+	{0x37ab, 0x14},
+	{0x37c2, 0x04},
+	{0x37c3, 0xf0},
+	{0x37cb, 0x09},
+	{0x37cc, 0x13},
+	{0x37cd, 0x1f},
+	{0x37ce, 0x1f},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xaf},
+	{0x3808, 0x05},
+	{0x3809, 0x10},
+	{0x380a, 0x03},
+	{0x380b, 0xcc},
+	{0x380c, 0x02},
+	{0x380d, 0xe4},
+	{0x380e, 0x03},
+	{0x380f, 0xf4},
+	{0x3810, 0x00},
+	{0x3811, 0x00},
+	{0x3812, 0x00},
+	{0x3813, 0x06},
+	{0x3814, 0x03},
+	{0x3815, 0x01},
+	{0x3816, 0x03},
+	{0x3817, 0x01},
+	{0x3818, 0x00},
+	{0x3819, 0x00},
+	{0x381a, 0x00},
+	{0x381b, 0x01},
+	{0x3820, 0x8b},
+	{0x3821, 0x01},
+	{0x3c80, 0x08},
+	{0x3c82, 0x00},
+	{0x3c83, 0x00},
+	{0x3c88, 0x00},
+	{0x3d85, 0x14},
+	{0x3f02, 0x08},
+	{0x3f03, 0x10},
+	{0x4008, 0x02},
+	{0x4009, 0x09},
+	{0x404e, 0x20},
+	{0x4501, 0x00},
+	{0x4502, 0x10},
+	{0x4800, 0x00},
+	{0x481f, 0x2a},
+	{0x4837, 0x13},
+	{0x5000, 0x13},
+	{0x5780, 0x3e},
+	{0x5781, 0x0f},
+	{0x5782, 0x44},
+	{0x5783, 0x02},
+	{0x5784, 0x01},
+	{0x5785, 0x01},
+	{0x5786, 0x00},
+	{0x5787, 0x04},
+	{0x5788, 0x02},
+	{0x5789, 0x0f},
+	{0x578a, 0xfd},
+	{0x578b, 0xf5},
+	{0x578c, 0xf5},
+	{0x578d, 0x03},
+	{0x578e, 0x08},
+	{0x578f, 0x0c},
+	{0x5790, 0x08},
+	{0x5791, 0x06},
+	{0x5792, 0x00},
+	{0x5793, 0x52},
+	{0x5794, 0xa3},
+	{0x5b00, 0x00},
+	{0x5b01, 0x1c},
+	{0x5b02, 0x00},
+	{0x5b03, 0x7f},
+	{0x5b05, 0x6c},
+	{0x5e10, 0xfc},
+	{0x4010, 0xf1},
+	{0x3503, 0x08},
+	{0x3505, 0x8c},
+	{0x3507, 0x03},
+	{0x3508, 0x00},
+	{0x3509, 0xf8},
+	{0x0100, 0x01},
+	{REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 2232(0x8b8)
+ * grabwindow_width 1280
+ * grabwindow_height 720
+ * max_framerate 30fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_1280x720_regs[] = {
+	{0x3501, 0x45},
+	{0x366e, 0x0c},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x01},
+	{0x3803, 0x00},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x06},
+	{0x3807, 0xaf},
+	{0x3808, 0x05},
+	{0x3809, 0x00},
+	{0x380a, 0x02},
+	{0x380b, 0xd0},
+	{0x380c, 0x02},
+	{0x380d, 0xa0},
+	{0x380e, 0x08},
+	{0x380f, 0xb8},
+	{0x3811, 0x06},
+	{0x3813, 0x02},
+	{0x3814, 0x03},
+	{0x3816, 0x03},
+	{0x3817, 0x01},
+	{0x3820, 0x8b},
+	{0x3821, 0x01},
+	{0x4501, 0x00},
+	{0x4008, 0x02},
+	{0x4009, 0x09},
+	{REG_NULL, 0x00}
+};
+
+/*
+ * Xclk 24Mhz
+ * Pclk 45Mhz
+ * linelength 672(0x2a0)
+ * framelength 558(0x22e)
+ * grabwindow_width 640
+ * grabwindow_height 480
+ * max_framerate 120fps
+ * mipi_datarate per lane 840Mbps
+ */
+static const struct regval ov5695_640x480_regs[] = {
+	{0x3501, 0x22},
+	{0x366e, 0x0c},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x08},
+	{0x3804, 0x0a},
+	{0x3805, 0x3f},
+	{0x3806, 0x07},
+	{0x3807, 0xa7},
+	{0x3808, 0x02},
+	{0x3809, 0x80},
+	{0x380a, 0x01},
+	{0x380b, 0xe0},
+	{0x380c, 0x02},
+	{0x380d, 0xa0},
+	{0x380e, 0x02},
+	{0x380f, 0x2e},
+	{0x3811, 0x06},
+	{0x3813, 0x04},
+	{0x3814, 0x07},
+	{0x3816, 0x05},
+	{0x3817, 0x03},
+	{0x3820, 0x8d},
+	{0x3821, 0x01},
+	{0x4501, 0x00},
+	{0x4008, 0x02},
+	{0x4009, 0x09},
+	{REG_NULL, 0x00}
+};
+
+static const struct ov5695_mode supported_modes[] = {
+	{
+		.width = 2592,
+		.height = 1944,
+		.max_fps = 30,
+		.exp_def = 0x0450,
+		.hts_def = 0x02e4 * 4,
+		.vts_def = 0x07e8,
+		.reg_list = ov5695_2592x1944_regs,
+	},
+	{
+		.width = 1920,
+		.height = 1080,
+		.max_fps = 30,
+		.exp_def = 0x0450,
+		.hts_def = 0x02a0 * 4,
+		.vts_def = 0x08b8,
+		.reg_list = ov5695_1920x1080_regs,
+	},
+	{
+		.width = 1296,
+		.height = 972,
+		.max_fps = 60,
+		.exp_def = 0x03e0,
+		.hts_def = 0x02e4 * 4,
+		.vts_def = 0x03f4,
+		.reg_list = ov5695_1296x972_regs,
+	},
+	{
+		.width = 1280,
+		.height = 720,
+		.max_fps = 30,
+		.exp_def = 0x0450,
+		.hts_def = 0x02a0 * 4,
+		.vts_def = 0x08b8,
+		.reg_list = ov5695_1280x720_regs,
+	},
+	{
+		.width = 640,
+		.height = 480,
+		.max_fps = 120,
+		.exp_def = 0x0450,
+		.hts_def = 0x02a0 * 4,
+		.vts_def = 0x022e,
+		.reg_list = ov5695_640x480_regs,
+	},
+};
+
+#define OV5695_LINK_FREQ_420MHZ		420000000
+static const s64 link_freq_menu_items[] = {
+	OV5695_LINK_FREQ_420MHZ
+};
+
+static const char * const ov5695_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int ov5695_write_reg(struct i2c_client *client, u16 reg,
+			    unsigned int len, u32 val)
+{
+	int buf_i;
+	int 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 ov5695_write_array(struct i2c_client *client,
+			      const struct regval *regs)
+{
+	int i, ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = ov5695_write_reg(client, regs[i].addr,
+				       OV5695_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov5695_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)
+		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 ov5695_get_reso_dist(const struct ov5695_mode *mode,
+				struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct ov5695_mode *ov5695_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 = ov5695_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 ov5695_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov5695 *ov5695 = to_ov5695(sd);
+	const struct ov5695_mode *mode;
+	s64 h_blank, vblank_def;
+
+	mutex_lock(&ov5695->mutex);
+
+	mode = ov5695_find_best_fit(fmt);
+	fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	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(&ov5695->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		ov5695->cur_mode = mode;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(ov5695->vblank, vblank_def,
+					 OV5695_VTS_MAX - mode->height,
+					 1, vblank_def);
+	}
+
+	mutex_unlock(&ov5695->mutex);
+	return 0;
+}
+
+static int ov5695_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov5695 *ov5695 = to_ov5695(sd);
+	const struct ov5695_mode *mode = ov5695->cur_mode;
+
+	mutex_lock(&ov5695->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(&ov5695->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+		fmt->format.field = V4L2_FIELD_NONE;
+	}
+	mutex_unlock(&ov5695->mutex);
+	return 0;
+}
+
+static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+	return 0;
+}
+
+static int ov5695_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_SBGGR10_1X10)
+		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 int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
+{
+	u32 val;
+
+	if (pattern)
+		val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
+	else
+		val = OV5695_TEST_PATTERN_DISABLE;
+
+	return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
+				OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val)
+{
+	/* 4 least significant bits of expsoure are fractional part */
+	ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
+			 OV5695_REG_VALUE_24BIT, val << 4);
+}
+
+static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val)
+{
+	ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
+			 OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val)
+{
+	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
+			 OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK);
+	val >>= OV5695_DIGI_GAIN_H_SHIFT;
+	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
+			 OV5695_REG_VALUE_08BIT, val);
+}
+
+static void ov5695_set_vts(struct ov5695 *ov5695, s32 val)
+{
+	val += ov5695->cur_mode->height;
+
+	ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
+			 OV5695_REG_VALUE_16BIT, val);
+}
+
+static int __ov5695_start_stream(struct ov5695 *ov5695)
+{
+	int ret;
+
+	ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
+	if (ret)
+		return ret;
+	ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
+	if (ret)
+		return ret;
+
+	/* In case these controls are set before streaming */
+	ov5695_set_exposure(ov5695, ov5695->exposure->val);
+	ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val);
+	ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val);
+	ov5695_set_vts(ov5695, ov5695->vblank->val);
+	ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val);
+
+	ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+			       OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
+	return ret;
+}
+
+static int __ov5695_stop_stream(struct ov5695 *ov5695)
+{
+	return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
+				OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
+}
+
+static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct ov5695 *ov5695 = to_ov5695(sd);
+	struct i2c_client *client = ov5695->client;
+	int ret = 0;
+
+	mutex_lock(&ov5695->mutex);
+	on = !!on;
+	if (on == ov5695->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret)
+			goto unlock_and_return;
+
+		ret = __ov5695_start_stream(ov5695);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+		}
+	} else {
+		ret = __ov5695_stop_stream(ov5695);
+		if (ret)
+			goto unlock_and_return;
+		pm_runtime_put(&client->dev);
+	}
+
+	ov5695->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&ov5695->mutex);
+	return ret;
+}
+
+static int __ov5695_power_on(struct ov5695 *ov5695)
+{
+	int ret;
+	struct device *dev = &ov5695->client->dev;
+
+	ret = clk_prepare_enable(ov5695->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+
+	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+
+	/* AVDD and DOVDD may rise in any order */
+	ret = regulator_enable(ov5695->avdd_regulator);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulator\n");
+		goto disable_clk;
+	}
+	ret = regulator_enable(ov5695->dovdd_regulator);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable DOVDD regulator\n");
+		goto disable_avdd;
+	}
+	/* DVDD must rise after AVDD and DOVDD */
+	ret = regulator_enable(ov5695->dvdd_regulator);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable DVDD regulator\n");
+		goto disable_dovdd;
+	}
+
+	gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
+	/* 8192 cycles prior to first SCCB transaction */
+	usleep_range(1000, 1500);
+
+	return 0;
+
+disable_dovdd:
+	regulator_disable(ov5695->dovdd_regulator);
+disable_avdd:
+	regulator_disable(ov5695->avdd_regulator);
+disable_clk:
+	clk_disable_unprepare(ov5695->xvclk);
+
+	return ret;
+}
+
+static void __ov5695_power_off(struct ov5695 *ov5695)
+{
+	clk_disable_unprepare(ov5695->xvclk);
+	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
+	regulator_disable(ov5695->dvdd_regulator);
+	regulator_disable(ov5695->dovdd_regulator);
+	regulator_disable(ov5695->avdd_regulator);
+}
+
+static int ov5695_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5695 *ov5695 = to_ov5695(sd);
+	int ret;
+
+	ret = __ov5695_power_on(ov5695);
+	if (ret)
+		return ret;
+
+	if (ov5695->streaming) {
+		ret = __ov5695_start_stream(ov5695);
+		if (ret)
+			__ov5695_power_off(ov5695);
+	}
+
+	return ret;
+}
+
+static int ov5695_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov5695 *ov5695 = to_ov5695(sd);
+
+	__ov5695_power_off(ov5695);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov5695 *ov5695 = to_ov5695(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+
+	mutex_lock(&ov5695->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = ov5695->cur_mode->width;
+	try_fmt->height = ov5695->cur_mode->height;
+	try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&ov5695->mutex);
+	/* No crop or compose */
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops ov5695_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
+			   ov5695_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
+	.open = ov5695_open,
+};
+#endif
+
+static struct v4l2_subdev_video_ops ov5695_video_ops = {
+	.s_stream = ov5695_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov5695_pad_ops = {
+	.enum_mbus_code = ov5695_enum_mbus_code,
+	.enum_frame_size = ov5695_enum_frame_sizes,
+	.get_fmt = ov5695_get_fmt,
+	.set_fmt = ov5695_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov5695_subdev_ops = {
+	.video	= &ov5695_video_ops,
+	.pad	= &ov5695_pad_ops,
+};
+
+static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov5695 *ov5695 = container_of(ctrl->handler,
+					     struct ov5695, ctrl_handler);
+	struct i2c_client *client = ov5695->client;
+	s64 max;
+	int ret = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = ov5695->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(ov5695->exposure,
+					 ov5695->exposure->minimum, max,
+					 ov5695->exposure->step,
+					 ov5695->exposure->default_value);
+		break;
+	}
+
+	pm_runtime_get_sync(&client->dev);
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		ov5695_set_exposure(ov5695, ctrl->val);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ov5695_set_analog_gain(ov5695, ctrl->val);
+		break;
+	case V4L2_CID_DIGITAL_GAIN:
+		ov5695_set_digi_gain(ov5695, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		ov5695_set_vts(ov5695, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = ov5695_enable_test_pattern(ov5695, ctrl->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 ov5695_ctrl_ops = {
+	.s_ctrl = ov5695_set_ctrl,
+};
+
+static int ov5695_initialize_controls(struct ov5695 *ov5695)
+{
+	const struct ov5695_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+
+	handler = &ov5695->ctrl_handler;
+	mode = ov5695->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 1);
+	if (ret)
+		return ret;
+	handler->lock = &ov5695->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+			  0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
+
+	h_blank = mode->hts_def - mode->width;
+	ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+				h_blank, h_blank, 1, h_blank);
+	if (ov5695->hblank)
+		ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	vblank_def = mode->vts_def - mode->height;
+	ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+				V4L2_CID_VBLANK, vblank_def,
+				OV5695_VTS_MAX - mode->height,
+				1, vblank_def);
+
+	exposure_max = mode->vts_def - 4;
+	ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+				V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
+				exposure_max, OV5695_EXPOSURE_STEP,
+				mode->exp_def);
+
+	ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+				V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
+				ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
+				ANALOG_GAIN_DEFAULT);
+
+	/* Digital gain */
+	ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
+				V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
+				OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
+				OV5695_DIGI_GAIN_DEFAULT);
+
+	ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+				&ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
+				ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
+				0, 0, ov5695_test_pattern_menu);
+
+	if (handler->error) {
+		v4l2_ctrl_handler_free(handler);
+		return handler->error;
+	}
+
+	ov5695->subdev.ctrl_handler = handler;
+
+	return 0;
+}
+
+static int ov5695_check_sensor_id(struct ov5695 *ov5695,
+				  struct i2c_client *client)
+{
+	struct device *dev = &ov5695->client->dev;
+	u32 id;
+	int ret;
+
+	ret = __ov5695_power_on(ov5695);
+	if (ret)
+		return ret;
+	ov5695_read_reg(client, OV5695_REG_CHIP_ID,
+			OV5695_REG_VALUE_24BIT, &id);
+	__ov5695_power_off(ov5695);
+
+	if (id != CHIP_ID) {
+		dev_err(dev, "Wrong camera sensor id(%06x)\n", id);
+		return -EINVAL;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int ov5695_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ov5695 *ov5695;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
+	if (!ov5695)
+		return -ENOMEM;
+
+	ov5695->client = client;
+	ov5695->cur_mode = &supported_modes[0];
+
+	ov5695->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(ov5695->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+	ret = clk_set_rate(ov5695->xvclk, 24000000);
+	if (ret < 0) {
+		dev_err(dev, "Failed to set xvclk rate (24M)\n");
+		return ret;
+	}
+
+	ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ov5695->reset_gpio)) {
+		dev_err(dev, "Failed to get reset-gpios\n");
+		return -EINVAL;
+	}
+
+	ov5695->avdd_regulator = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(ov5695->avdd_regulator)) {
+		dev_err(dev, "Failed to get avdd-supply\n");
+		return -EINVAL;
+	}
+	ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+	if (IS_ERR(ov5695->dovdd_regulator)) {
+		dev_err(dev, "Failed to get dovdd-supply\n");
+		return -EINVAL;
+	}
+	ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd");
+	if (IS_ERR(ov5695->dvdd_regulator)) {
+		dev_err(dev, "Failed to get dvdd-supply\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&ov5695->mutex);
+
+	sd = &ov5695->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &ov5695_internal_ops;
+#endif
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+	ret = ov5695_initialize_controls(ov5695);
+	if (ret)
+		return ret;
+	ret = ov5695_check_sensor_id(ov5695, client);
+	if (ret)
+		return ret;
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
+	if (ret < 0)
+		return ret;
+#endif
+
+	ret = v4l2_async_register_subdev(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto clean_entity;
+	}
+
+	pm_runtime_enable(dev);
+	dev_info(dev, "Probe successfully\n");
+
+	return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+
+	return ret;
+}
+
+static int ov5695_remove(struct i2c_client *client)
+{
+	struct ov5695 *ov5695 = i2c_get_clientdata(client);
+
+	v4l2_async_unregister_subdev(&ov5695->subdev);
+
+	pm_runtime_disable(&client->dev);
+	return 0;
+}
+
+static const struct of_device_id ov5695_of_match[] = {
+	{ .compatible = "ovti,ov5695" },
+	{},
+};
+
+static struct i2c_driver ov5695_i2c_driver = {
+	.driver = {
+		.name = "ov5695",
+		.owner = THIS_MODULE,
+		.pm = &ov5695_pm_ops,
+		.of_match_table = ov5695_of_match
+	},
+	.probe		= &ov5695_probe,
+	.remove		= &ov5695_remove,
+};
+
+module_i2c_driver(ov5695_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
@ 2017-12-29  8:08   ` Shunqian Zheng
       [not found]     ` <1514534905-21393-2-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2017-12-29  8:08   ` [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor Shunqian Zheng
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 15+ messages in thread
From: Shunqian Zheng @ 2017-12-29  8:08 UTC (permalink / raw)
  To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng

Add device tree binding documentation for the OV5695 sensor.

Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/media/i2c/ov5695.txt       | 38 ++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5695.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
new file mode 100644
index 0000000..8d87dbc
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
@@ -0,0 +1,38 @@
+* Omnivision OV5695 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov5695"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- dovdd-supply: Digital I/O voltage supply, 1.8 volts
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+&i2c1: camera-sensor@36 {
+	compatible = "ovti,ov5695";
+	reg = <0x36>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&clk_24m_cam>;
+
+	clocks = <&cru SCLK_TESTCLKOUT1>;
+	clock-names = "xvclk";
+
+	avdd-supply = <&pp2800_cam>;
+	dvdd-supply = <&pp1250_cam>;
+	dovdd-supply = <&pp1800>;
+
+	reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
+
+	port {
+		wcam_out: endpoint {
+			remote-endpoint = <&mipi_in_wcam>;
+			data-lanes = <1 2>;
+		};
+	};
+};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2017-12-29  8:08   ` [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695 Shunqian Zheng
@ 2017-12-29  8:08   ` Shunqian Zheng
  2018-01-03 11:43     ` Sakari Ailus
  2017-12-29  8:08   ` [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685 Shunqian Zheng
  2018-01-02 10:09   ` [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor jacopo mondi
  3 siblings, 1 reply; 15+ messages in thread
From: Shunqian Zheng @ 2017-12-29  8:08 UTC (permalink / raw)
  To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng

This patch adds driver for Omnivision's ov2685 sensor.
Though the ov2685 can output yuv data, this driver only
supports the raw bayer format, including the following features:
  - output 1600x1200 at 30fps
  - test patterns
  - manual exposure/gain control
  - vblank and hblank
  - media controller
  - runtime pm

Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 drivers/media/i2c/Kconfig  |  12 +
 drivers/media/i2c/Makefile |   1 +
 drivers/media/i2c/ov2685.c | 841 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 854 insertions(+)
 create mode 100644 drivers/media/i2c/ov2685.c

diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 55b37c8..63a175d 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -586,6 +586,18 @@ config VIDEO_OV2659
 	  To compile this driver as a module, choose M here: the
 	  module will be called ov2659.
 
+config VIDEO_OV2685
+	tristate "OmniVision OV2685 sensor support"
+	depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	---help---
+	  This is a Video4Linux2 sensor-level driver for the OmniVision
+	  OV2685 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ov2685.
+
 config VIDEO_OV5640
 	tristate "OmniVision OV5640 sensor support"
 	depends on OF
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index a063030..3054c69 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
+obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
 obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
 obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
 obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
new file mode 100644
index 0000000..e037d20
--- /dev/null
+++ b/drivers/media/i2c/ov2685.c
@@ -0,0 +1,841 @@
+/*
+ * ov2685 driver
+ *
+ * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#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 <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#define CHIP_ID				0x2685
+#define OV2685_REG_CHIP_ID		0x300a
+
+#define REG_SC_CTRL_MODE		0x0100
+#define     SC_CTRL_MODE_STANDBY	0x0
+#define     SC_CTRL_MODE_STREAMING	BIT(0)
+
+#define OV2685_REG_EXPOSURE		0x3500
+#define	OV2685_EXPOSURE_MIN		4
+#define	OV2685_EXPOSURE_STEP		1
+
+#define OV2685_REG_VTS			0x380e
+#define OV2685_VTS_MAX			0x7fff
+
+#define OV2685_REG_GAIN			0x350a
+#define OV2685_GAIN_MIN			0
+#define OV2685_GAIN_MAX			0x07ff
+#define OV2685_GAIN_STEP		0x1
+#define OV2685_GAIN_DEFAULT		0x0036
+
+#define OV2685_REG_TEST_PATTERN		0x5080
+#define OV2685_TEST_PATTERN_DISABLED		0x00
+#define OV2685_TEST_PATTERN_COLOR_BAR		0x80
+#define OV2685_TEST_PATTERN_RND			0x81
+#define OV2685_TEST_PATTERN_COLOR_BAR_FADE	0x88
+#define OV2685_TEST_PATTERN_BW_SQUARE		0x92
+#define OV2685_TEST_PATTERN_COLOR_SQUARE	0x82
+
+#define REG_NULL			0xFFFF
+
+#define OV2685_REG_VALUE_08BIT		1
+#define OV2685_REG_VALUE_16BIT		2
+#define OV2685_REG_VALUE_24BIT		3
+
+#define OV2685_LANES			1
+#define OV2685_BITS_PER_SAMPLE		10
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct ov2685_mode {
+	u32 width;
+	u32 height;
+	u32 exp_def;
+	u32 hts_def;
+	u32 vts_def;
+	const struct regval *reg_list;
+};
+
+struct ov2685 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct regulator	*avdd_regulator;	/* Analog power */
+	struct regulator	*dovdd_regulator;	/* Digital I/O power */
+				/* use internal DVDD power */
+	struct gpio_desc	*reset_gpio;
+
+	bool			streaming;
+	struct mutex		mutex;
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*test_pattern;
+	struct v4l2_ctrl_handler ctrl_handler;
+
+	const struct ov2685_mode *cur_mode;
+};
+#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
+
+/* PLL settings bases on 24M xvclk */
+static struct regval ov2685_1600x1200_regs[] = {
+	{0x0103, 0x01},
+	{0x0100, 0x00},
+	{0x3002, 0x00},
+	{0x3016, 0x1c},
+	{0x3018, 0x44},
+	{0x301d, 0xf0},
+	{0x3020, 0x00},
+	{0x3082, 0x37},
+	{0x3083, 0x03},
+	{0x3084, 0x09},
+	{0x3085, 0x04},
+	{0x3086, 0x00},
+	{0x3087, 0x00},
+	{0x3501, 0x4e},
+	{0x3502, 0xe0},
+	{0x3503, 0x07},
+	{0x350b, 0x36},
+	{0x3600, 0xb4},
+	{0x3603, 0x35},
+	{0x3604, 0x24},
+	{0x3605, 0x00},
+	{0x3620, 0x24},
+	{0x3621, 0x34},
+	{0x3622, 0x03},
+	{0x3628, 0x10},
+	{0x3705, 0x3c},
+	{0x370a, 0x21},
+	{0x370c, 0x50},
+	{0x370d, 0xc0},
+	{0x3717, 0x58},
+	{0x3718, 0x80},
+	{0x3720, 0x00},
+	{0x3721, 0x09},
+	{0x3722, 0x06},
+	{0x3723, 0x59},
+	{0x3738, 0x99},
+	{0x3781, 0x80},
+	{0x3784, 0x0c},
+	{0x3789, 0x60},
+	{0x3800, 0x00},
+	{0x3801, 0x00},
+	{0x3802, 0x00},
+	{0x3803, 0x00},
+	{0x3804, 0x06},
+	{0x3805, 0x4f},
+	{0x3806, 0x04},
+	{0x3807, 0xbf},
+	{0x3808, 0x06},
+	{0x3809, 0x40},
+	{0x380a, 0x04},
+	{0x380b, 0xb0},
+	{0x380c, 0x06},
+	{0x380d, 0xa4},
+	{0x380e, 0x05},
+	{0x380f, 0x0e},
+	{0x3810, 0x00},
+	{0x3811, 0x08},
+	{0x3812, 0x00},
+	{0x3813, 0x08},
+	{0x3814, 0x11},
+	{0x3815, 0x11},
+	{0x3819, 0x04},
+	{0x3820, 0xc0},
+	{0x3821, 0x00},
+	{0x3a06, 0x01},
+	{0x3a07, 0x84},
+	{0x3a08, 0x01},
+	{0x3a09, 0x43},
+	{0x3a0a, 0x24},
+	{0x3a0b, 0x60},
+	{0x3a0c, 0x28},
+	{0x3a0d, 0x60},
+	{0x3a0e, 0x04},
+	{0x3a0f, 0x8c},
+	{0x3a10, 0x05},
+	{0x3a11, 0x0c},
+	{0x4000, 0x81},
+	{0x4001, 0x40},
+	{0x4008, 0x02},
+	{0x4009, 0x09},
+	{0x4300, 0x00},
+	{0x430e, 0x00},
+	{0x4602, 0x02},
+	{0x481b, 0x40},
+	{0x481f, 0x40},
+	{0x4837, 0x18},
+	{0x5000, 0x1f},
+	{0x5001, 0x05},
+	{0x5002, 0x30},
+	{0x5003, 0x04},
+	{0x5004, 0x00},
+	{0x5005, 0x0c},
+	{0x5280, 0x15},
+	{0x5281, 0x06},
+	{0x5282, 0x06},
+	{0x5283, 0x08},
+	{0x5284, 0x1c},
+	{0x5285, 0x1c},
+	{0x5286, 0x20},
+	{0x5287, 0x10},
+	{REG_NULL, 0x00}
+};
+
+#define OV2685_LINK_FREQ_330MHZ		330000000
+static const s64 link_freq_menu_items[] = {
+	OV2685_LINK_FREQ_330MHZ
+};
+
+static const char * const ov2685_test_pattern_menu[] = {
+	"Disabled",
+	"Color Bar",
+	"RND PATTERN",
+	"Color Bar FADE",
+	"BW SQUARE",
+	"COLOR SQUARE"
+};
+
+static const int ov2685_test_pattern_val[] = {
+	OV2685_TEST_PATTERN_DISABLED,
+	OV2685_TEST_PATTERN_COLOR_BAR,
+	OV2685_TEST_PATTERN_RND,
+	OV2685_TEST_PATTERN_COLOR_BAR_FADE,
+	OV2685_TEST_PATTERN_BW_SQUARE,
+	OV2685_TEST_PATTERN_COLOR_SQUARE,
+};
+
+static const struct ov2685_mode supported_modes[] = {
+	{
+		.width = 1600,
+		.height = 1200,
+		.exp_def = 0x04ee,
+		.hts_def = 0x06a4,
+		.vts_def = 0x050e,
+		.reg_list = ov2685_1600x1200_regs,
+	},
+};
+
+/* Write registers up to 4 at a time */
+static int ov2685_write_reg(struct i2c_client *client, u16 reg,
+			    unsigned int len, u32 val)
+{
+	int buf_i;
+	int 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 ov2685_write_array(struct i2c_client *client,
+			      const struct regval *regs)
+{
+	int i, ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = ov2685_write_reg(client, regs[i].addr,
+				       OV2685_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ov2685_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)
+		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 void ov2685_fill_fmt(struct ov2685 *ov2685,
+			    struct v4l2_mbus_framefmt *fmt)
+{
+	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	fmt->width = ov2685->cur_mode->width;
+	fmt->height = ov2685->cur_mode->height;
+	fmt->field = V4L2_FIELD_NONE;
+}
+
+static int ov2685_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+	ov2685_fill_fmt(ov2685, mbus_fmt);
+
+	return 0;
+}
+
+static int ov2685_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
+
+	ov2685_fill_fmt(ov2685, mbus_fmt);
+
+	return 0;
+}
+
+static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	if (code->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+	return 0;
+}
+
+static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	int index = fse->index;
+
+	if (index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+
+	fse->min_width  = supported_modes[index].width;
+	fse->max_width  = supported_modes[index].width;
+	fse->max_height = supported_modes[index].height;
+	fse->min_height = supported_modes[index].height;
+
+	return 0;
+}
+
+static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
+{
+	ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
+			 OV2685_REG_VALUE_24BIT, val << 4);
+}
+
+static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
+{
+	ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
+			 OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
+}
+
+static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
+{
+	val += ov2685->cur_mode->height;
+	ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
+			 OV2685_REG_VALUE_16BIT, val);
+}
+
+static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
+{
+	ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
+			 OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
+}
+
+static int __ov2685_power_on(struct ov2685 *ov2685)
+{
+	int ret;
+	struct device *dev = &ov2685->client->dev;
+
+	ret = clk_prepare_enable(ov2685->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+	clk_set_rate(ov2685->xvclk, 24000000);
+
+	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+	/* AVDD and DOVDD may rise in any order */
+	ret = regulator_enable(ov2685->avdd_regulator);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable AVDD regulator\n");
+		goto disable_xvclk;
+	}
+	ret = regulator_enable(ov2685->dovdd_regulator);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable DOVDD regulator\n");
+		goto disable_avdd;
+	}
+	/* The minimum delay between AVDD and reset rising can be 0 */
+	gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
+	/* 8192 xvclk cycles prior to the first SCCB transaction.
+	 * NOTE: An additional 1ms must be added to wait for
+	 *       SCCB to become stable when using internal DVDD.
+	 */
+	usleep_range(1350, 1500);
+
+	/* HACK: ov2685 would output messy data after reset(R0103),
+	 * writing register before .s_stream() as a workaround
+	 */
+	ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
+
+	return ret;
+disable_avdd:
+	regulator_disable(ov2685->avdd_regulator);
+disable_xvclk:
+	clk_disable_unprepare(ov2685->xvclk);
+
+	return ret;
+}
+
+static void __ov2685_power_off(struct ov2685 *ov2685)
+{
+	/* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
+	usleep_range(30, 50);
+	clk_disable_unprepare(ov2685->xvclk);
+	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
+	regulator_disable(ov2685->dovdd_regulator);
+	regulator_disable(ov2685->avdd_regulator);
+}
+
+static int ov2685_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	int ret = 0;
+
+	mutex_lock(&ov2685->mutex);
+
+	if (on)
+		ret = pm_runtime_get_sync(&ov2685->client->dev);
+	else
+		ret = pm_runtime_put(&ov2685->client->dev);
+
+	mutex_unlock(&ov2685->mutex);
+
+	return ret;
+}
+
+static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	struct i2c_client *client = ov2685->client;
+	int ret = 0;
+
+	mutex_lock(&ov2685->mutex);
+
+	on = !!on;
+	if (on == ov2685->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		/* In case these controls are set before streaming */
+		ov2685_set_exposure(ov2685, ov2685->exposure->val);
+		ov2685_set_gain(ov2685, ov2685->anal_gain->val);
+		ov2685_set_vts(ov2685, ov2685->vblank->val);
+		ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
+
+		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
+		if (ret)
+			goto unlock_and_return;
+	} else {
+		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
+				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
+		if (ret)
+			goto unlock_and_return;
+	}
+
+	ov2685->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&ov2685->mutex);
+	return ret;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	struct v4l2_mbus_framefmt *try_fmt;
+
+	mutex_lock(&ov2685->mutex);
+
+	try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	/* Initialize try_fmt */
+	ov2685_fill_fmt(ov2685, try_fmt);
+
+	mutex_unlock(&ov2685->mutex);
+
+	return 0;
+}
+#endif
+
+static int ov2685_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2685 *ov2685 = to_ov2685(sd);
+	int ret;
+
+	ret = __ov2685_power_on(ov2685);
+	if (ret)
+		return ret;
+
+	if (ov2685->streaming) {
+		ret = ov2685_s_stream(sd, 1);
+		if (ret)
+			__ov2685_power_off(ov2685);
+	}
+
+	return ret;
+}
+
+static int ov2685_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ov2685 *ov2685 = to_ov2685(sd);
+
+	__ov2685_power_off(ov2685);
+
+	return 0;
+}
+
+static const struct dev_pm_ops ov2685_pm_ops = {
+	SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
+			   ov2685_runtime_resume, NULL)
+};
+
+static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ov2685 *ov2685 = container_of(ctrl->handler,
+					     struct ov2685, ctrl_handler);
+	struct i2c_client *client = ov2685->client;
+	s64 max;
+	int ret = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = ov2685->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(ov2685->exposure,
+					 ov2685->exposure->minimum, max,
+					 ov2685->exposure->step,
+					 ov2685->exposure->default_value);
+		break;
+	}
+
+	pm_runtime_get_sync(&client->dev);
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		ov2685_set_exposure(ov2685, ctrl->val);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		ov2685_set_gain(ov2685, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		ov2685_set_vts(ov2685, ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ov2685_enable_test_pattern(ov2685, ctrl->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 struct v4l2_subdev_core_ops ov2685_core_ops = {
+	.s_power = ov2685_s_power,
+};
+
+static struct v4l2_subdev_video_ops ov2685_video_ops = {
+	.s_stream = ov2685_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
+	.enum_mbus_code = ov2685_enum_mbus_code,
+	.enum_frame_size = ov2685_enum_frame_sizes,
+	.get_fmt = ov2685_get_fmt,
+	.set_fmt = ov2685_set_fmt,
+};
+
+static struct v4l2_subdev_ops ov2685_subdev_ops = {
+	.core	= &ov2685_core_ops,
+	.video	= &ov2685_video_ops,
+	.pad	= &ov2685_pad_ops,
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
+	.open = ov2685_open,
+};
+#endif
+
+static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
+	.s_ctrl = ov2685_set_ctrl,
+};
+
+static int ov2685_initialize_controls(struct ov2685 *ov2685)
+{
+	const struct ov2685_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	u64 exposure_max;
+	u32 pixel_rate, h_blank;
+	int ret;
+
+	handler = &ov2685->ctrl_handler;
+	mode = ov2685->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 1);
+	if (ret)
+		return ret;
+	handler->lock = &ov2685->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
+		     OV2685_BITS_PER_SAMPLE;
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+			  0, pixel_rate, 1, pixel_rate);
+
+	h_blank = mode->hts_def - mode->width;
+	ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+				h_blank, h_blank, 1, h_blank);
+	if (ov2685->hblank)
+		ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+				V4L2_CID_VBLANK, mode->vts_def - mode->height,
+				OV2685_VTS_MAX - mode->height, 1,
+				mode->vts_def - mode->height);
+
+	exposure_max = mode->vts_def - 4;
+	ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+				V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
+				exposure_max, OV2685_EXPOSURE_STEP,
+				mode->exp_def);
+
+	ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
+				V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
+				OV2685_GAIN_MAX, OV2685_GAIN_STEP,
+				OV2685_GAIN_DEFAULT);
+
+	ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+				&ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
+				ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
+				0, 0, ov2685_test_pattern_menu);
+
+	if (handler->error) {
+		v4l2_ctrl_handler_free(handler);
+		return handler->error;
+	}
+
+	ov2685->subdev.ctrl_handler = handler;
+
+	return 0;
+}
+
+static int ov2685_check_sensor_id(struct ov2685 *ov2685,
+				  struct i2c_client *client)
+{
+	struct device *dev = &ov2685->client->dev;
+	int id, ret;
+
+	ret = __ov2685_power_on(ov2685);
+	if (ret)
+		return ret;
+	ov2685_read_reg(client, OV2685_REG_CHIP_ID,
+			OV2685_REG_VALUE_16BIT, &id);
+	__ov2685_power_off(ov2685);
+
+	if (id != CHIP_ID) {
+		dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
+		return -EINVAL;
+	}
+
+	dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int ov2685_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ov2685 *ov2685;
+	int ret;
+
+	ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
+	if (!ov2685)
+		return -ENOMEM;
+
+	ov2685->client = client;
+	ov2685->cur_mode = &supported_modes[0];
+
+	ov2685->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(ov2685->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ov2685->reset_gpio)) {
+		dev_err(dev, "Failed to get reset-gpios\n");
+		return -EINVAL;
+	}
+
+	ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
+	if (IS_ERR(ov2685->avdd_regulator)) {
+		dev_err(dev, "Failed to get avdd-supply\n");
+		return -EINVAL;
+	}
+	ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
+	if (IS_ERR(ov2685->dovdd_regulator)) {
+		dev_err(dev, "Failed to get dovdd-supply\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&ov2685->mutex);
+	v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
+	ret = ov2685_initialize_controls(ov2685);
+	if (ret)
+		goto destroy_mutex;
+
+	ret = ov2685_check_sensor_id(ov2685, client);
+	if (ret)
+		return ret;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	ov2685->subdev.internal_ops = &ov2685_internal_ops;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
+	ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+	ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
+	if (ret < 0)
+		goto free_ctrl_handler;
+#endif
+
+	ret = v4l2_async_register_subdev(&ov2685->subdev);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto clean_entity;
+	}
+
+	pm_runtime_enable(dev);
+	return 0;
+
+clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&ov2685->subdev.entity);
+#endif
+free_ctrl_handler:
+	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+destroy_mutex:
+	mutex_destroy(&ov2685->mutex);
+
+	return ret;
+}
+
+static int ov2685_remove(struct i2c_client *client)
+{
+	struct ov2685 *ov2685 = i2c_get_clientdata(client);
+
+	__ov2685_power_off(ov2685);
+	v4l2_async_unregister_subdev(&ov2685->subdev);
+	media_entity_cleanup(&ov2685->subdev.entity);
+	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
+	mutex_destroy(&ov2685->mutex);
+
+	return 0;
+}
+
+static const struct of_device_id ov2685_of_match[] = {
+	{ .compatible = "ovti,ov2685" },
+	{},
+};
+
+static struct i2c_driver ov2685_i2c_driver = {
+	.driver = {
+		.name = "ov2685",
+		.owner = THIS_MODULE,
+		.pm = &ov2685_pm_ops,
+		.of_match_table = ov2685_of_match
+	},
+	.probe		= &ov2685_probe,
+	.remove		= &ov2685_remove,
+};
+
+module_i2c_driver(ov2685_i2c_driver);
+
+MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2017-12-29  8:08   ` [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695 Shunqian Zheng
  2017-12-29  8:08   ` [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor Shunqian Zheng
@ 2017-12-29  8:08   ` Shunqian Zheng
       [not found]     ` <1514534905-21393-4-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2018-01-02 10:09   ` [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor jacopo mondi
  3 siblings, 1 reply; 15+ messages in thread
From: Shunqian Zheng @ 2017-12-29  8:08 UTC (permalink / raw)
  To: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw, Shunqian Zheng

Add device tree binding documentation for the OV2685 sensor.

Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
---
 .../devicetree/bindings/media/i2c/ov2685.txt       | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2685.txt

diff --git a/Documentation/devicetree/bindings/media/i2c/ov2685.txt b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
new file mode 100644
index 0000000..85aec03
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
@@ -0,0 +1,35 @@
+* Omnivision OV2685 MIPI CSI-2 sensor
+
+Required Properties:
+- compatible: should be "ovti,ov2685"
+- clocks: reference to the 24M xvclk input clock.
+- clock-names: should be "xvclk".
+- avdd-supply: Analog voltage supply, 2.8 volts
+- dvdd-supply: Digital core voltage supply, 1.2 volts
+- reset-gpios: Low active reset gpio
+
+The device node must contain one 'port' child node for its digital output
+video port, in accordance with the video interface bindings defined in
+Documentation/devicetree/bindings/media/video-interfaces.txt.
+
+Example:
+	ucam: camera-sensor@3c {
+		compatible = "ovti,ov2685";
+		reg = <0x3c>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&clk_24m_cam>;
+
+		clocks = <&cru SCLK_TESTCLKOUT1>;
+		clock-names = "xvclk";
+
+		avdd-supply = <&pp2800_cam>;
+		dovdd-supply = <&pp1800>;
+		reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
+
+		port {
+			ucam_out: endpoint {
+				remote-endpoint = <&mipi_in_ucam>;
+				data-lanes = <1>;
+			};
+		};
+	};
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695
       [not found]     ` <1514534905-21393-2-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
@ 2018-01-02  9:19       ` jacopo mondi
  2018-01-02 14:25       ` Sakari Ailus
  1 sibling, 0 replies; 15+ messages in thread
From: jacopo mondi @ 2018-01-02  9:19 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw

Hello Shunqian,

On Fri, Dec 29, 2017 at 04:08:23PM +0800, Shunqian Zheng wrote:
> Add device tree binding documentation for the OV5695 sensor.
>
> Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
>  .../devicetree/bindings/media/i2c/ov5695.txt       | 38 ++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5695.txt
>
> diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
> new file mode 100644
> index 0000000..8d87dbc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
> @@ -0,0 +1,38 @@
> +* Omnivision OV5695 MIPI CSI-2 sensor
> +
> +Required Properties:
> +- compatible: should be "ovti,ov5695"
> +- clocks: reference to the 24M xvclk input clock.
> +- clock-names: should be "xvclk".
> +- dovdd-supply: Digital I/O voltage supply, 1.8 volts
> +- avdd-supply: Analog voltage supply, 2.8 volts
> +- dvdd-supply: Digital core voltage supply, 1.2 volts
> +- reset-gpios: Low active reset gpio
> +
> +The device node must contain one 'port' child node for its digital output
> +video port, in accordance with the video interface bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.
> +
> +Example:
> +&i2c1: camera-sensor@36 {

I think what Rob asked for in his v1 review was to have a
"camera-sensor" node which is a child of the "i2c1" interface node.

I would expect something like:

&i2c1 {
        pinctrl-names ...
        pinctrl-0...

        camera-sensor@36 {

                compatible = "ovti,ov5695";
                ...
        };
};

Thanks
   j
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
                     ` (2 preceding siblings ...)
  2017-12-29  8:08   ` [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685 Shunqian Zheng
@ 2018-01-02 10:09   ` jacopo mondi
  2018-01-08 13:40     ` Shunqian Zheng
  3 siblings, 1 reply; 15+ messages in thread
From: jacopo mondi @ 2018-01-02 10:09 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw

Hi Shunqian,
   thank you for the patch

On Fri, Dec 29, 2017 at 04:08:22PM +0800, Shunqian Zheng wrote:
> This patch adds driver for Omnivision's ov5695 sensor,
> the driver supports following features:
>  - supported resolutions
>    + 2592x1944 at 30fps
>    + 1920x1080 at 30fps
>    + 1296x972 at 60fps
>    + 1280x720 at 30fps
>    + 640x480 at 120fps
>  - test patterns
>  - manual exposure/gain(analog and digital) control
>  - vblank and hblank
>  - media controller
>  - runtime pm
>
> Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
>  drivers/media/i2c/Kconfig  |   11 +
>  drivers/media/i2c/Makefile |    1 +
>  drivers/media/i2c/ov5695.c | 1396 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1408 insertions(+)
>  create mode 100644 drivers/media/i2c/ov5695.c
>
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 3c6d642..55b37c8 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -645,6 +645,17 @@ config VIDEO_OV5670
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ov5670.
>
> +config VIDEO_OV5695
> +	tristate "OmniVision OV5695 sensor support"
> +	depends on I2C && VIDEO_V4L2
> +	depends on MEDIA_CAMERA_SUPPORT
> +	---help---
> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> +	  OV5695 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ov5695.
> +
>  config VIDEO_OV7640
>  	tristate "OmniVision OV7640 sensor support"
>  	depends on I2C && VIDEO_V4L2
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index 548a9ef..a063030 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>  obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
> +obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
>  obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
>  obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
>  obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
> diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
> new file mode 100644
> index 0000000..4745da4
> --- /dev/null
> +++ b/drivers/media/i2c/ov5695.c
> @@ -0,0 +1,1396 @@
> +/*
> + * ov5695 driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */

Please use SPDX identifiers and drop the license text

> +
> +#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 <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +
> +#ifndef V4L2_CID_DIGITAL_GAIN
> +#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
> +#endif
> +
> +/* 45Mhz * 4 Binning */
> +#define OV5695_PIXEL_RATE		(45 * 1000 * 1000 * 4)
> +#define CHIP_ID				0x005695
> +#define OV5695_REG_CHIP_ID		0x300a
> +
> +#define OV5695_REG_CTRL_MODE		0x0100
> +#define OV5695_MODE_SW_STANDBY		0x0
> +#define OV5695_MODE_STREAMING		BIT(0)
> +
> +#define OV5695_REG_EXPOSURE		0x3500
> +#define	OV5695_EXPOSURE_MIN		4
> +#define	OV5695_EXPOSURE_STEP		1
> +#define OV5695_VTS_MAX			0x7fff
> +
> +#define OV5695_REG_ANALOG_GAIN		0x3509
> +#define	ANALOG_GAIN_MIN			0x10
> +#define	ANALOG_GAIN_MAX			0xf8
> +#define	ANALOG_GAIN_STEP		1
> +#define	ANALOG_GAIN_DEFAULT		0xf8
> +
> +#define OV5695_REG_DIGI_GAIN_H		0x350a
> +#define OV5695_REG_DIGI_GAIN_L		0x350b
> +#define OV5695_DIGI_GAIN_L_MASK		0x3f
> +#define OV5695_DIGI_GAIN_H_SHIFT	6
> +#define OV5695_DIGI_GAIN_MIN		0
> +#define OV5695_DIGI_GAIN_MAX		(0x4000 - 1)
> +#define OV5695_DIGI_GAIN_STEP		1
> +#define OV5695_DIGI_GAIN_DEFAULT	1024
> +
> +#define OV5695_REG_TEST_PATTERN		0x4503
> +#define	OV5695_TEST_PATTERN_ENABLE	0x80
> +#define	OV5695_TEST_PATTERN_DISABLE	0x0
> +
> +#define OV5695_REG_VTS			0x380e
> +
> +#define REG_NULL			0xFFFF
> +
> +#define OV5695_REG_VALUE_08BIT		1
> +#define OV5695_REG_VALUE_16BIT		2
> +#define OV5695_REG_VALUE_24BIT		3
> +
> +#define OV5695_LANES			2
> +#define OV5695_BITS_PER_SAMPLE		10
> +
> +struct regval {
> +	u16 addr;
> +	u8 val;
> +};
> +
> +struct ov5695_mode {
> +	u32 width;
> +	u32 height;
> +	u32 max_fps;
> +	u32 hts_def;
> +	u32 vts_def;
> +	u32 exp_def;
> +	const struct regval *reg_list;
> +};
> +
> +struct ov5695 {
> +	struct i2c_client	*client;
> +	struct clk		*xvclk;
> +	struct regulator        *avdd_regulator;
> +	struct regulator        *dovdd_regulator;
> +	struct regulator        *dvdd_regulator;
> +	struct gpio_desc	*reset_gpio;
> +
> +	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	*test_pattern;
> +	struct mutex		mutex;
> +	bool			streaming;
> +	const struct ov5695_mode *cur_mode;
> +};
> +#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 672(0x2a0)
> + * framelength 2232(0x8b8)
> + * grabwindow_width 1296
> + * grabwindow_height 972
> + * max_framerate 30fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_global_regs[] = {
> +	{0x0103, 0x01},
> +	{0x0100, 0x00},
> +	{0x0300, 0x04},
> +	{0x0301, 0x00},
> +	{0x0302, 0x69},
> +	{0x0303, 0x00},
> +	{0x0304, 0x00},
> +	{0x0305, 0x01},
> +	{0x0307, 0x00},
> +	{0x030b, 0x00},
> +	{0x030c, 0x00},
> +	{0x030d, 0x1e},
> +	{0x030e, 0x04},
> +	{0x030f, 0x03},
> +	{0x0312, 0x01},
> +	{0x3000, 0x00},
> +	{0x3002, 0xa1},
> +	{0x0308, 0x00},
> +	{0x0310, 0x00},
> +	{0x3022, 0x51},
> +	{0x3106, 0x15},
> +	{0x3107, 0x01},
> +	{0x3108, 0x05},
> +	{0x3500, 0x00},
> +	{0x3501, 0x45},
> +	{0x3502, 0x00},
> +	{0x3503, 0x08},
> +	{0x3504, 0x03},
> +	{0x3505, 0x8c},
> +	{0x3507, 0x03},
> +	{0x3508, 0x00},
> +	{0x3509, 0x10},
> +	{0x350c, 0x00},
> +	{0x350d, 0x80},
> +	{0x3510, 0x00},
> +	{0x3511, 0x02},
> +	{0x3512, 0x00},
> +	{0x3601, 0x55},
> +	{0x3602, 0x58},
> +	{0x3614, 0x30},
> +	{0x3615, 0x77},
> +	{0x3621, 0x08},
> +	{0x3624, 0x40},
> +	{0x3633, 0x0c},
> +	{0x3634, 0x0c},
> +	{0x3635, 0x0c},
> +	{0x3636, 0x0c},
> +	{0x3638, 0x00},
> +	{0x3639, 0x00},
> +	{0x363a, 0x00},
> +	{0x363b, 0x00},
> +	{0x363c, 0xff},
> +	{0x363d, 0xfa},
> +	{0x3650, 0x44},
> +	{0x3651, 0x44},
> +	{0x3652, 0x44},
> +	{0x3653, 0x44},
> +	{0x3654, 0x44},
> +	{0x3655, 0x44},
> +	{0x3656, 0x44},
> +	{0x3657, 0x44},
> +	{0x3660, 0x00},
> +	{0x3661, 0x00},
> +	{0x3662, 0x00},
> +	{0x366a, 0x00},
> +	{0x366e, 0x0c},
> +	{0x3673, 0x04},
> +	{0x3700, 0x14},
> +	{0x3703, 0x0c},
> +	{0x3715, 0x01},
> +	{0x3733, 0x10},
> +	{0x3734, 0x40},
> +	{0x373f, 0xa0},
> +	{0x3765, 0x20},
> +	{0x37a1, 0x1d},
> +	{0x37a8, 0x26},
> +	{0x37ab, 0x14},
> +	{0x37c2, 0x04},
> +	{0x37cb, 0x09},
> +	{0x37cc, 0x13},
> +	{0x37cd, 0x1f},
> +	{0x37ce, 0x1f},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x00},
> +	{0x3803, 0x00},
> +	{0x3804, 0x0a},
> +	{0x3805, 0x3f},
> +	{0x3806, 0x07},
> +	{0x3807, 0xaf},
> +	{0x3808, 0x05},
> +	{0x3809, 0x10},
> +	{0x380a, 0x03},
> +	{0x380b, 0xcc},
> +	{0x380c, 0x02},
> +	{0x380d, 0xa0},
> +	{0x380e, 0x08},
> +	{0x380f, 0xb8},
> +	{0x3810, 0x00},
> +	{0x3811, 0x06},
> +	{0x3812, 0x00},
> +	{0x3813, 0x06},
> +	{0x3814, 0x03},
> +	{0x3815, 0x01},
> +	{0x3816, 0x03},
> +	{0x3817, 0x01},
> +	{0x3818, 0x00},
> +	{0x3819, 0x00},
> +	{0x381a, 0x00},
> +	{0x381b, 0x01},
> +	{0x3820, 0x8b},
> +	{0x3821, 0x01},
> +	{0x3c80, 0x08},
> +	{0x3c82, 0x00},
> +	{0x3c83, 0x00},
> +	{0x3c88, 0x00},
> +	{0x3d85, 0x14},
> +	{0x3f02, 0x08},
> +	{0x3f03, 0x10},
> +	{0x4008, 0x02},
> +	{0x4009, 0x09},
> +	{0x404e, 0x20},
> +	{0x4501, 0x00},
> +	{0x4502, 0x10},
> +	{0x4800, 0x00},
> +	{0x481f, 0x2a},
> +	{0x4837, 0x13},
> +	{0x5000, 0x17},
> +	{0x5780, 0x3e},
> +	{0x5781, 0x0f},
> +	{0x5782, 0x44},
> +	{0x5783, 0x02},
> +	{0x5784, 0x01},
> +	{0x5785, 0x01},
> +	{0x5786, 0x00},
> +	{0x5787, 0x04},
> +	{0x5788, 0x02},
> +	{0x5789, 0x0f},
> +	{0x578a, 0xfd},
> +	{0x578b, 0xf5},
> +	{0x578c, 0xf5},
> +	{0x578d, 0x03},
> +	{0x578e, 0x08},
> +	{0x578f, 0x0c},
> +	{0x5790, 0x08},
> +	{0x5791, 0x06},
> +	{0x5792, 0x00},
> +	{0x5793, 0x52},
> +	{0x5794, 0xa3},
> +	{0x5b00, 0x00},
> +	{0x5b01, 0x1c},
> +	{0x5b02, 0x00},
> +	{0x5b03, 0x7f},
> +	{0x5b05, 0x6c},
> +	{0x5e10, 0xfc},
> +	{0x4010, 0xf1},
> +	{0x3503, 0x08},
> +	{0x3505, 0x8c},
> +	{0x3507, 0x03},
> +	{0x3508, 0x00},
> +	{0x3509, 0xf8},
> +	{REG_NULL, 0x00},
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 740(0x2e4)
> + * framelength 2024(0x7e8)
> + * grabwindow_width 2592
> + * grabwindow_height 1944
> + * max_framerate 30fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_2592x1944_regs[] = {
> +	{0x3501, 0x7e},
> +	{0x366e, 0x18},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x00},
> +	{0x3803, 0x04},
> +	{0x3804, 0x0a},
> +	{0x3805, 0x3f},
> +	{0x3806, 0x07},
> +	{0x3807, 0xab},
> +	{0x3808, 0x0a},
> +	{0x3809, 0x20},
> +	{0x380a, 0x07},
> +	{0x380b, 0x98},
> +	{0x380c, 0x02},
> +	{0x380d, 0xe4},
> +	{0x380e, 0x07},
> +	{0x380f, 0xe8},
> +	{0x3811, 0x06},
> +	{0x3813, 0x08},
> +	{0x3814, 0x01},
> +	{0x3816, 0x01},
> +	{0x3817, 0x01},
> +	{0x3820, 0x88},
> +	{0x3821, 0x00},
> +	{0x4501, 0x00},
> +	{0x4008, 0x04},
> +	{0x4009, 0x13},
> +	{REG_NULL, 0x00},
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 672(0x2a0)
> + * framelength 2232(0x8b8)
> + * grabwindow_width 1920
> + * grabwindow_height 1080
> + * max_framerate 30fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_1920x1080_regs[] = {
> +	{0x3501, 0x45},
> +	{0x366e, 0x18},
> +	{0x3800, 0x01},
> +	{0x3801, 0x50},
> +	{0x3802, 0x01},
> +	{0x3803, 0xb8},
> +	{0x3804, 0x08},
> +	{0x3805, 0xef},
> +	{0x3806, 0x05},
> +	{0x3807, 0xf7},
> +	{0x3808, 0x07},
> +	{0x3809, 0x80},
> +	{0x380a, 0x04},
> +	{0x380b, 0x38},
> +	{0x380c, 0x02},
> +	{0x380d, 0xa0},
> +	{0x380e, 0x08},
> +	{0x380f, 0xb8},
> +	{0x3811, 0x06},
> +	{0x3813, 0x04},
> +	{0x3814, 0x01},
> +	{0x3816, 0x01},
> +	{0x3817, 0x01},
> +	{0x3820, 0x88},
> +	{0x3821, 0x00},
> +	{0x4501, 0x00},
> +	{0x4008, 0x04},
> +	{0x4009, 0x13},
> +	{REG_NULL, 0x00}
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 740(0x02e4)
> + * framelength 1012(0x03f4)
> + * grabwindow_width 1296
> + * grabwindow_height 972
> + * max_framerate 60fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_1296x972_regs[] = {
> +	{0x0103, 0x01},
> +	{0x0100, 0x00},
> +	{0x0300, 0x04},
> +	{0x0301, 0x00},
> +	{0x0302, 0x69},
> +	{0x0303, 0x00},
> +	{0x0304, 0x00},
> +	{0x0305, 0x01},
> +	{0x0307, 0x00},
> +	{0x030b, 0x00},
> +	{0x030c, 0x00},
> +	{0x030d, 0x1e},
> +	{0x030e, 0x04},
> +	{0x030f, 0x03},
> +	{0x0312, 0x01},
> +	{0x3000, 0x00},
> +	{0x3002, 0x21},
> +	{0x3016, 0x32},
> +	{0x3022, 0x51},
> +	{0x3106, 0x15},
> +	{0x3107, 0x01},
> +	{0x3108, 0x05},
> +	{0x3500, 0x00},
> +	{0x3501, 0x3e},
> +	{0x3502, 0x00},
> +	{0x3503, 0x08},
> +	{0x3504, 0x03},
> +	{0x3505, 0x8c},
> +	{0x3507, 0x03},
> +	{0x3508, 0x00},
> +	{0x3509, 0x10},
> +	{0x350c, 0x00},
> +	{0x350d, 0x80},
> +	{0x3510, 0x00},
> +	{0x3511, 0x02},
> +	{0x3512, 0x00},
> +	{0x3601, 0x55},
> +	{0x3602, 0x58},
> +	{0x3611, 0x58},
> +	{0x3614, 0x30},
> +	{0x3615, 0x77},
> +	{0x3621, 0x08},
> +	{0x3624, 0x40},
> +	{0x3633, 0x0c},
> +	{0x3634, 0x0c},
> +	{0x3635, 0x0c},
> +	{0x3636, 0x0c},
> +	{0x3638, 0x00},
> +	{0x3639, 0x00},
> +	{0x363a, 0x00},
> +	{0x363b, 0x00},
> +	{0x363c, 0xff},
> +	{0x363d, 0xfa},
> +	{0x3650, 0x44},
> +	{0x3651, 0x44},
> +	{0x3652, 0x44},
> +	{0x3653, 0x44},
> +	{0x3654, 0x44},
> +	{0x3655, 0x44},
> +	{0x3656, 0x44},
> +	{0x3657, 0x44},
> +	{0x3660, 0x00},
> +	{0x3661, 0x00},
> +	{0x3662, 0x00},
> +	{0x366a, 0x00},
> +	{0x366e, 0x0c},
> +	{0x3673, 0x04},
> +	{0x3700, 0x14},
> +	{0x3703, 0x0c},
> +	{0x3706, 0x24},
> +	{0x3714, 0x27},
> +	{0x3715, 0x01},
> +	{0x3716, 0x00},
> +	{0x3717, 0x02},
> +	{0x3733, 0x10},
> +	{0x3734, 0x40},
> +	{0x373f, 0xa0},
> +	{0x3765, 0x20},
> +	{0x37a1, 0x1d},
> +	{0x37a8, 0x26},
> +	{0x37ab, 0x14},
> +	{0x37c2, 0x04},
> +	{0x37c3, 0xf0},
> +	{0x37cb, 0x09},
> +	{0x37cc, 0x13},
> +	{0x37cd, 0x1f},
> +	{0x37ce, 0x1f},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x00},
> +	{0x3803, 0x00},
> +	{0x3804, 0x0a},
> +	{0x3805, 0x3f},
> +	{0x3806, 0x07},
> +	{0x3807, 0xaf},
> +	{0x3808, 0x05},
> +	{0x3809, 0x10},
> +	{0x380a, 0x03},
> +	{0x380b, 0xcc},
> +	{0x380c, 0x02},
> +	{0x380d, 0xe4},
> +	{0x380e, 0x03},
> +	{0x380f, 0xf4},
> +	{0x3810, 0x00},
> +	{0x3811, 0x00},
> +	{0x3812, 0x00},
> +	{0x3813, 0x06},
> +	{0x3814, 0x03},
> +	{0x3815, 0x01},
> +	{0x3816, 0x03},
> +	{0x3817, 0x01},
> +	{0x3818, 0x00},
> +	{0x3819, 0x00},
> +	{0x381a, 0x00},
> +	{0x381b, 0x01},
> +	{0x3820, 0x8b},
> +	{0x3821, 0x01},
> +	{0x3c80, 0x08},
> +	{0x3c82, 0x00},
> +	{0x3c83, 0x00},
> +	{0x3c88, 0x00},
> +	{0x3d85, 0x14},
> +	{0x3f02, 0x08},
> +	{0x3f03, 0x10},
> +	{0x4008, 0x02},
> +	{0x4009, 0x09},
> +	{0x404e, 0x20},
> +	{0x4501, 0x00},
> +	{0x4502, 0x10},
> +	{0x4800, 0x00},
> +	{0x481f, 0x2a},
> +	{0x4837, 0x13},
> +	{0x5000, 0x13},
> +	{0x5780, 0x3e},
> +	{0x5781, 0x0f},
> +	{0x5782, 0x44},
> +	{0x5783, 0x02},
> +	{0x5784, 0x01},
> +	{0x5785, 0x01},
> +	{0x5786, 0x00},
> +	{0x5787, 0x04},
> +	{0x5788, 0x02},
> +	{0x5789, 0x0f},
> +	{0x578a, 0xfd},
> +	{0x578b, 0xf5},
> +	{0x578c, 0xf5},
> +	{0x578d, 0x03},
> +	{0x578e, 0x08},
> +	{0x578f, 0x0c},
> +	{0x5790, 0x08},
> +	{0x5791, 0x06},
> +	{0x5792, 0x00},
> +	{0x5793, 0x52},
> +	{0x5794, 0xa3},
> +	{0x5b00, 0x00},
> +	{0x5b01, 0x1c},
> +	{0x5b02, 0x00},
> +	{0x5b03, 0x7f},
> +	{0x5b05, 0x6c},
> +	{0x5e10, 0xfc},
> +	{0x4010, 0xf1},
> +	{0x3503, 0x08},
> +	{0x3505, 0x8c},
> +	{0x3507, 0x03},
> +	{0x3508, 0x00},
> +	{0x3509, 0xf8},
> +	{0x0100, 0x01},
> +	{REG_NULL, 0x00}
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 672(0x2a0)
> + * framelength 2232(0x8b8)
> + * grabwindow_width 1280
> + * grabwindow_height 720
> + * max_framerate 30fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_1280x720_regs[] = {
> +	{0x3501, 0x45},
> +	{0x366e, 0x0c},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x01},
> +	{0x3803, 0x00},
> +	{0x3804, 0x0a},
> +	{0x3805, 0x3f},
> +	{0x3806, 0x06},
> +	{0x3807, 0xaf},
> +	{0x3808, 0x05},
> +	{0x3809, 0x00},
> +	{0x380a, 0x02},
> +	{0x380b, 0xd0},
> +	{0x380c, 0x02},
> +	{0x380d, 0xa0},
> +	{0x380e, 0x08},
> +	{0x380f, 0xb8},
> +	{0x3811, 0x06},
> +	{0x3813, 0x02},
> +	{0x3814, 0x03},
> +	{0x3816, 0x03},
> +	{0x3817, 0x01},
> +	{0x3820, 0x8b},
> +	{0x3821, 0x01},
> +	{0x4501, 0x00},
> +	{0x4008, 0x02},
> +	{0x4009, 0x09},
> +	{REG_NULL, 0x00}
> +};
> +
> +/*
> + * Xclk 24Mhz
> + * Pclk 45Mhz
> + * linelength 672(0x2a0)
> + * framelength 558(0x22e)
> + * grabwindow_width 640
> + * grabwindow_height 480
> + * max_framerate 120fps
> + * mipi_datarate per lane 840Mbps
> + */
> +static const struct regval ov5695_640x480_regs[] = {
> +	{0x3501, 0x22},
> +	{0x366e, 0x0c},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x00},
> +	{0x3803, 0x08},
> +	{0x3804, 0x0a},
> +	{0x3805, 0x3f},
> +	{0x3806, 0x07},
> +	{0x3807, 0xa7},
> +	{0x3808, 0x02},
> +	{0x3809, 0x80},
> +	{0x380a, 0x01},
> +	{0x380b, 0xe0},
> +	{0x380c, 0x02},
> +	{0x380d, 0xa0},
> +	{0x380e, 0x02},
> +	{0x380f, 0x2e},
> +	{0x3811, 0x06},
> +	{0x3813, 0x04},
> +	{0x3814, 0x07},
> +	{0x3816, 0x05},
> +	{0x3817, 0x03},
> +	{0x3820, 0x8d},
> +	{0x3821, 0x01},
> +	{0x4501, 0x00},
> +	{0x4008, 0x02},
> +	{0x4009, 0x09},
> +	{REG_NULL, 0x00}
> +};
> +
> +static const struct ov5695_mode supported_modes[] = {
> +	{
> +		.width = 2592,
> +		.height = 1944,
> +		.max_fps = 30,
> +		.exp_def = 0x0450,
> +		.hts_def = 0x02e4 * 4,
> +		.vts_def = 0x07e8,
> +		.reg_list = ov5695_2592x1944_regs,
> +	},
> +	{
> +		.width = 1920,
> +		.height = 1080,
> +		.max_fps = 30,
> +		.exp_def = 0x0450,
> +		.hts_def = 0x02a0 * 4,
> +		.vts_def = 0x08b8,
> +		.reg_list = ov5695_1920x1080_regs,
> +	},
> +	{
> +		.width = 1296,
> +		.height = 972,
> +		.max_fps = 60,
> +		.exp_def = 0x03e0,
> +		.hts_def = 0x02e4 * 4,
> +		.vts_def = 0x03f4,
> +		.reg_list = ov5695_1296x972_regs,
> +	},
> +	{
> +		.width = 1280,
> +		.height = 720,
> +		.max_fps = 30,
> +		.exp_def = 0x0450,
> +		.hts_def = 0x02a0 * 4,
> +		.vts_def = 0x08b8,
> +		.reg_list = ov5695_1280x720_regs,
> +	},
> +	{
> +		.width = 640,
> +		.height = 480,
> +		.max_fps = 120,
> +		.exp_def = 0x0450,
> +		.hts_def = 0x02a0 * 4,
> +		.vts_def = 0x022e,
> +		.reg_list = ov5695_640x480_regs,
> +	},
> +};
> +
> +#define OV5695_LINK_FREQ_420MHZ		420000000
> +static const s64 link_freq_menu_items[] = {
> +	OV5695_LINK_FREQ_420MHZ
> +};
> +
> +static const char * const ov5695_test_pattern_menu[] = {
> +	"Disabled",
> +	"Vertical Color Bar Type 1",
> +	"Vertical Color Bar Type 2",
> +	"Vertical Color Bar Type 3",
> +	"Vertical Color Bar Type 4"
> +};
> +
> +/* Write registers up to 4 at a time */
> +static int ov5695_write_reg(struct i2c_client *client, u16 reg,
> +			    unsigned int len, u32 val)
> +{
> +	int buf_i;
> +	int 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 ov5695_write_array(struct i2c_client *client,
> +			      const struct regval *regs)
> +{
> +	int i, ret = 0;
> +
> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> +		ret = ov5695_write_reg(client, regs[i].addr,
> +				       OV5695_REG_VALUE_08BIT, regs[i].val);
> +
> +	return ret;
> +}
> +
> +/* Read registers up to 4 at a time */
> +static int ov5695_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)
> +		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 ov5695_get_reso_dist(const struct ov5695_mode *mode,
> +				struct v4l2_mbus_framefmt *framefmt)
> +{
> +	return abs(mode->width - framefmt->width) +
> +	       abs(mode->height - framefmt->height);
> +}
> +
> +static const struct ov5695_mode *ov5695_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 = ov5695_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 ov5695_set_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +	const struct ov5695_mode *mode;
> +	s64 h_blank, vblank_def;
> +
> +	mutex_lock(&ov5695->mutex);
> +
> +	mode = ov5695_find_best_fit(fmt);
> +	fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	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(&ov5695->mutex);
> +		return -ENOTTY;
> +#endif
> +	} else {
> +		ov5695->cur_mode = mode;
> +		h_blank = mode->hts_def - mode->width;
> +		__v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
> +					 h_blank, 1, h_blank);
> +		vblank_def = mode->vts_def - mode->height;
> +		__v4l2_ctrl_modify_range(ov5695->vblank, vblank_def,
> +					 OV5695_VTS_MAX - mode->height,
> +					 1, vblank_def);
> +	}
> +
> +	mutex_unlock(&ov5695->mutex);
> +	return 0;
> +}
> +
> +static int ov5695_get_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +	const struct ov5695_mode *mode = ov5695->cur_mode;
> +
> +	mutex_lock(&ov5695->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(&ov5695->mutex);
> +		return -ENOTTY;
> +#endif
> +	} else {
> +		fmt->format.width = mode->width;
> +		fmt->format.height = mode->height;
> +		fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +		fmt->format.field = V4L2_FIELD_NONE;
> +	}
> +	mutex_unlock(&ov5695->mutex);
> +	return 0;
> +}
> +
> +static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index != 0)
> +		return -EINVAL;
> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +
> +	return 0;
> +}
> +
> +static int ov5695_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_SBGGR10_1X10)
> +		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 int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
> +{
> +	u32 val;
> +
> +	if (pattern)
> +		val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
> +	else
> +		val = OV5695_TEST_PATTERN_DISABLE;
> +
> +	return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
> +				OV5695_REG_VALUE_08BIT, val);
> +}
> +
> +static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val)
> +{
> +	/* 4 least significant bits of expsoure are fractional part */
> +	ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
> +			 OV5695_REG_VALUE_24BIT, val << 4);
> +}
> +
> +static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val)
> +{
> +	ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
> +			 OV5695_REG_VALUE_08BIT, val);
> +}
> +
> +static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val)
> +{
> +	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
> +			 OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK);
> +	val >>= OV5695_DIGI_GAIN_H_SHIFT;
> +	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
> +			 OV5695_REG_VALUE_08BIT, val);
> +}
> +
> +static void ov5695_set_vts(struct ov5695 *ov5695, s32 val)
> +{
> +	val += ov5695->cur_mode->height;
> +
> +	ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
> +			 OV5695_REG_VALUE_16BIT, val);
> +}
> +
> +static int __ov5695_start_stream(struct ov5695 *ov5695)
> +{
> +	int ret;
> +
> +	ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
> +	if (ret)
> +		return ret;
> +	ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
> +	if (ret)
> +		return ret;
> +
> +	/* In case these controls are set before streaming */
> +	ov5695_set_exposure(ov5695, ov5695->exposure->val);
> +	ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val);
> +	ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val);
> +	ov5695_set_vts(ov5695, ov5695->vblank->val);
> +	ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val);
> +
> +	ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
> +			       OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
> +	return ret;
> +}
> +
> +static int __ov5695_stop_stream(struct ov5695 *ov5695)
> +{
> +	return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
> +				OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
> +}
> +
> +static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
> +{
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +	struct i2c_client *client = ov5695->client;
> +	int ret = 0;
> +
> +	mutex_lock(&ov5695->mutex);
> +	on = !!on;
> +	if (on == ov5695->streaming)
> +		goto unlock_and_return;
> +
> +	if (on) {
> +		ret = pm_runtime_get_sync(&client->dev);
> +		if (ret)
> +			goto unlock_and_return;
> +
> +		ret = __ov5695_start_stream(ov5695);
> +		if (ret) {
> +			v4l2_err(sd, "start stream failed while write regs\n");
> +			pm_runtime_put(&client->dev);

Here you will set "ov5695->streaming = 1". Should't you jump to the
"unlock_and_return" label instead?

> +		}
> +	} else {
> +		ret = __ov5695_stop_stream(ov5695);
> +		if (ret)
> +			goto unlock_and_return;
> +		pm_runtime_put(&client->dev);

I would runtime_put even if writing to REG_CTRL_MODE fails.

> +	}
> +
> +	ov5695->streaming = on;
> +
> +unlock_and_return:
> +	mutex_unlock(&ov5695->mutex);
> +	return ret;
> +}
> +
> +static int __ov5695_power_on(struct ov5695 *ov5695)
> +{
> +	int ret;
> +	struct device *dev = &ov5695->client->dev;
> +
> +	ret = clk_prepare_enable(ov5695->xvclk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable xvclk\n");
> +		return ret;
> +	}
> +
> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
> +
> +	/* AVDD and DOVDD may rise in any order */
> +	ret = regulator_enable(ov5695->avdd_regulator);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable regulator\n");
> +		goto disable_clk;
> +	}
> +	ret = regulator_enable(ov5695->dovdd_regulator);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable DOVDD regulator\n");
> +		goto disable_avdd;
> +	}
> +	/* DVDD must rise after AVDD and DOVDD */
> +	ret = regulator_enable(ov5695->dvdd_regulator);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable DVDD regulator\n");
> +		goto disable_dovdd;
> +	}
> +
> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
> +	/* 8192 cycles prior to first SCCB transaction */
> +	usleep_range(1000, 1500);

I assume 8192 xvclk cycles, shouldn't the delay be calculated using
that value? (or does the (1000, 1500) intervals catches all the
supported xvclk frequencies?)

> +
> +	return 0;
> +
> +disable_dovdd:
> +	regulator_disable(ov5695->dovdd_regulator);
> +disable_avdd:
> +	regulator_disable(ov5695->avdd_regulator);
> +disable_clk:
> +	clk_disable_unprepare(ov5695->xvclk);
> +
> +	return ret;
> +}
> +
> +static void __ov5695_power_off(struct ov5695 *ov5695)
> +{
> +	clk_disable_unprepare(ov5695->xvclk);
> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
> +	regulator_disable(ov5695->dvdd_regulator);
> +	regulator_disable(ov5695->dovdd_regulator);
> +	regulator_disable(ov5695->avdd_regulator);
> +}
> +
> +static int ov5695_runtime_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +	int ret;
> +
> +	ret = __ov5695_power_on(ov5695);
> +	if (ret)
> +		return ret;
> +
> +	if (ov5695->streaming) {
> +		ret = __ov5695_start_stream(ov5695);
> +		if (ret)
> +			__ov5695_power_off(ov5695);
> +	}

If you fix my comment above on s_stream, you won't have any case where
runtime_susped is called with "ov5695->streaming = 1" and you can
remove this if branch.

> +
> +	return ret;
> +}
> +
> +static int ov5695_runtime_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +
> +	__ov5695_power_off(ov5695);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct ov5695 *ov5695 = to_ov5695(sd);
> +	struct v4l2_mbus_framefmt *try_fmt =
> +				v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +
> +	mutex_lock(&ov5695->mutex);
> +	/* Initialize try_fmt */
> +	try_fmt->width = ov5695->cur_mode->width;
> +	try_fmt->height = ov5695->cur_mode->height;
> +	try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	try_fmt->field = V4L2_FIELD_NONE;
> +
> +	mutex_unlock(&ov5695->mutex);
> +	/* No crop or compose */

Checkpatch should complain about returns with no blank line before
(here and in many other places in this patch).

Maybe try run it with --strict switch...

> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops ov5695_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
> +			   ov5695_runtime_resume, NULL)
> +};
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
> +	.open = ov5695_open,
> +};
> +#endif
> +
> +static struct v4l2_subdev_video_ops ov5695_video_ops = {
> +	.s_stream = ov5695_s_stream,
> +};
> +
> +static struct v4l2_subdev_pad_ops ov5695_pad_ops = {
> +	.enum_mbus_code = ov5695_enum_mbus_code,
> +	.enum_frame_size = ov5695_enum_frame_sizes,
> +	.get_fmt = ov5695_get_fmt,
> +	.set_fmt = ov5695_set_fmt,
> +};
> +
> +static struct v4l2_subdev_ops ov5695_subdev_ops = {
> +	.video	= &ov5695_video_ops,
> +	.pad	= &ov5695_pad_ops,
> +};
> +
> +static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ov5695 *ov5695 = container_of(ctrl->handler,
> +					     struct ov5695, ctrl_handler);
> +	struct i2c_client *client = ov5695->client;
> +	s64 max;
> +	int ret = 0;
> +
> +	/* Propagate change of current control to all related controls */
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK:
> +		/* Update max exposure while meeting expected vblanking */
> +		max = ov5695->cur_mode->height + ctrl->val - 4;
> +		__v4l2_ctrl_modify_range(ov5695->exposure,
> +					 ov5695->exposure->minimum, max,
> +					 ov5695->exposure->step,
> +					 ov5695->exposure->default_value);
> +		break;
> +	}
> +
> +	pm_runtime_get_sync(&client->dev);
> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		ov5695_set_exposure(ov5695, ctrl->val);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ov5695_set_analog_gain(ov5695, ctrl->val);
> +		break;
> +	case V4L2_CID_DIGITAL_GAIN:
> +		ov5695_set_digi_gain(ov5695, ctrl->val);
> +		break;
> +	case V4L2_CID_VBLANK:
> +		ov5695_set_vts(ov5695, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		ret = ov5695_enable_test_pattern(ov5695, ctrl->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 ov5695_ctrl_ops = {
> +	.s_ctrl = ov5695_set_ctrl,
> +};
> +
> +static int ov5695_initialize_controls(struct ov5695 *ov5695)
> +{
> +	const struct ov5695_mode *mode;
> +	struct v4l2_ctrl_handler *handler;
> +	struct v4l2_ctrl *ctrl;
> +	s64 exposure_max, vblank_def;
> +	u32 h_blank;
> +	int ret;
> +
> +	handler = &ov5695->ctrl_handler;
> +	mode = ov5695->cur_mode;
> +	ret = v4l2_ctrl_handler_init(handler, 1);
> +	if (ret)
> +		return ret;
> +	handler->lock = &ov5695->mutex;
> +
> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
> +				      0, 0, link_freq_menu_items);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
> +			  0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
> +
> +	h_blank = mode->hts_def - mode->width;
> +	ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> +				h_blank, h_blank, 1, h_blank);
> +	if (ov5695->hblank)
> +		ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	vblank_def = mode->vts_def - mode->height;
> +	ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
> +				V4L2_CID_VBLANK, vblank_def,
> +				OV5695_VTS_MAX - mode->height,
> +				1, vblank_def);
> +
> +	exposure_max = mode->vts_def - 4;
> +	ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
> +				V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
> +				exposure_max, OV5695_EXPOSURE_STEP,
> +				mode->exp_def);
> +
> +	ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
> +				V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
> +				ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
> +				ANALOG_GAIN_DEFAULT);
> +
> +	/* Digital gain */
> +	ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
> +				V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
> +				OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
> +				OV5695_DIGI_GAIN_DEFAULT);
> +
> +	ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
> +				&ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
> +				ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
> +				0, 0, ov5695_test_pattern_menu);
> +
> +	if (handler->error) {
> +		v4l2_ctrl_handler_free(handler);
> +		return handler->error;
> +	}
> +
> +	ov5695->subdev.ctrl_handler = handler;
> +
> +	return 0;
> +}
> +
> +static int ov5695_check_sensor_id(struct ov5695 *ov5695,
> +				  struct i2c_client *client)
> +{
> +	struct device *dev = &ov5695->client->dev;
> +	u32 id;
> +	int ret;
> +
> +	ret = __ov5695_power_on(ov5695);
> +	if (ret)
> +		return ret;
> +	ov5695_read_reg(client, OV5695_REG_CHIP_ID,
> +			OV5695_REG_VALUE_24BIT, &id);
> +	__ov5695_power_off(ov5695);
> +
> +	if (id != CHIP_ID) {
> +		dev_err(dev, "Wrong camera sensor id(%06x)\n", id);
> +		return -EINVAL;
> +	}
> +
> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
> +
> +	return 0;
> +}
> +
> +static int ov5695_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct ov5695 *ov5695;
> +	struct v4l2_subdev *sd;
> +	int ret;
> +
> +	ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
> +	if (!ov5695)
> +		return -ENOMEM;
> +
> +	ov5695->client = client;
> +	ov5695->cur_mode = &supported_modes[0];
> +
> +	ov5695->xvclk = devm_clk_get(dev, "xvclk");
> +	if (IS_ERR(ov5695->xvclk)) {
> +		dev_err(dev, "Failed to get xvclk\n");
> +		return -EINVAL;
> +	}
> +	ret = clk_set_rate(ov5695->xvclk, 24000000);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to set xvclk rate (24M)\n");
> +		return ret;
> +	}
> +
> +	ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov5695->reset_gpio)) {
> +		dev_err(dev, "Failed to get reset-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ov5695->avdd_regulator = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(ov5695->avdd_regulator)) {
> +		dev_err(dev, "Failed to get avdd-supply\n");
> +		return -EINVAL;
> +	}
> +	ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd");
> +	if (IS_ERR(ov5695->dovdd_regulator)) {
> +		dev_err(dev, "Failed to get dovdd-supply\n");
> +		return -EINVAL;
> +	}
> +	ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd");
> +	if (IS_ERR(ov5695->dvdd_regulator)) {
> +		dev_err(dev, "Failed to get dvdd-supply\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&ov5695->mutex);
> +
> +	sd = &ov5695->subdev;
> +	v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	sd->internal_ops = &ov5695_internal_ops;
> +#endif
> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;

Shouldn't a devnode be registered only if V4L2_SUBDEV_API is defined?

Thanks
   j


> +
> +	ret = ov5695_initialize_controls(ov5695);
> +	if (ret)
> +		return ret;
> +	ret = ov5695_check_sensor_id(ov5695, client);
> +	if (ret)
> +		return ret;
> +
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
> +	if (ret < 0)
> +		return ret;
> +#endif
> +
> +	ret = v4l2_async_register_subdev(sd);
> +	if (ret) {
> +		dev_err(dev, "v4l2 async register subdev failed\n");
> +		goto clean_entity;
> +	}
> +
> +	pm_runtime_enable(dev);
> +	dev_info(dev, "Probe successfully\n");
> +
> +	return 0;
> +
> +clean_entity:
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	media_entity_cleanup(&sd->entity);
> +#endif
> +
> +	return ret;
> +}
> +
> +static int ov5695_remove(struct i2c_client *client)
> +{
> +	struct ov5695 *ov5695 = i2c_get_clientdata(client);
> +
> +	v4l2_async_unregister_subdev(&ov5695->subdev);
> +
> +	pm_runtime_disable(&client->dev);
> +	return 0;
> +}
> +
> +static const struct of_device_id ov5695_of_match[] = {
> +	{ .compatible = "ovti,ov5695" },
> +	{},
> +};
> +
> +static struct i2c_driver ov5695_i2c_driver = {
> +	.driver = {
> +		.name = "ov5695",
> +		.owner = THIS_MODULE,
> +		.pm = &ov5695_pm_ops,
> +		.of_match_table = ov5695_of_match
> +	},
> +	.probe		= &ov5695_probe,
> +	.remove		= &ov5695_remove,
> +};
> +
> +module_i2c_driver(ov5695_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695
       [not found]     ` <1514534905-21393-2-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  2018-01-02  9:19       ` jacopo mondi
@ 2018-01-02 14:25       ` Sakari Ailus
  1 sibling, 0 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-02 14:25 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw

Hi Shunqian,

Thanks for the patchset.

On Fri, Dec 29, 2017 at 04:08:23PM +0800, Shunqian Zheng wrote:
> Add device tree binding documentation for the OV5695 sensor.
> 
> Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
>  .../devicetree/bindings/media/i2c/ov5695.txt       | 38 ++++++++++++++++++++++
>  1 file changed, 38 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ov5695.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ov5695.txt b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
> new file mode 100644
> index 0000000..8d87dbc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ov5695.txt
> @@ -0,0 +1,38 @@
> +* Omnivision OV5695 MIPI CSI-2 sensor
> +
> +Required Properties:
> +- compatible: should be "ovti,ov5695"

s/should/shall/

> +- clocks: reference to the 24M xvclk input clock.

I presume 24 MHz may be currently assumed by the driver but it's not a
property of the sensor. You should therefore check the frequency in the
driver instead.

> +- clock-names: should be "xvclk".
> +- dovdd-supply: Digital I/O voltage supply, 1.8 volts
> +- avdd-supply: Analog voltage supply, 2.8 volts
> +- dvdd-supply: Digital core voltage supply, 1.2 volts
> +- reset-gpios: Low active reset gpio
> +
> +The device node must contain one 'port' child node for its digital output

s/must/shall/

Please add that it shall also contain one endpoint node.

> +video port, in accordance with the video interface bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.

Which properties are mandatory? You may leave out remote-endpoint as it's
already required by the video interface bindings --- the rest are hardware
specific in a way or another.

> +
> +Example:
> +&i2c1: camera-sensor@36 {
> +	compatible = "ovti,ov5695";
> +	reg = <0x36>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&clk_24m_cam>;
> +
> +	clocks = <&cru SCLK_TESTCLKOUT1>;
> +	clock-names = "xvclk";
> +
> +	avdd-supply = <&pp2800_cam>;
> +	dvdd-supply = <&pp1250_cam>;
> +	dovdd-supply = <&pp1800>;
> +
> +	reset-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>;
> +
> +	port {
> +		wcam_out: endpoint {
> +			remote-endpoint = <&mipi_in_wcam>;
> +			data-lanes = <1 2>;
> +		};
> +	};
> +};

-- 
Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
  2017-12-29  8:08   ` [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor Shunqian Zheng
@ 2018-01-03 11:43     ` Sakari Ailus
  2018-01-08 13:37       ` Shunqian Zheng
  2018-01-12  2:30       ` Shunqian Zheng
  0 siblings, 2 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-03 11:43 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

Hi Shunqian,

Please see my comments below.

On Fri, Dec 29, 2017 at 04:08:24PM +0800, Shunqian Zheng wrote:
> This patch adds driver for Omnivision's ov2685 sensor.
> Though the ov2685 can output yuv data, this driver only
> supports the raw bayer format, including the following features:
>   - output 1600x1200 at 30fps
>   - test patterns
>   - manual exposure/gain control
>   - vblank and hblank
>   - media controller
>   - runtime pm
> 
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>
> ---
>  drivers/media/i2c/Kconfig  |  12 +
>  drivers/media/i2c/Makefile |   1 +
>  drivers/media/i2c/ov2685.c | 841 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 854 insertions(+)
>  create mode 100644 drivers/media/i2c/ov2685.c
> 
> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
> index 55b37c8..63a175d 100644
> --- a/drivers/media/i2c/Kconfig
> +++ b/drivers/media/i2c/Kconfig
> @@ -586,6 +586,18 @@ config VIDEO_OV2659
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called ov2659.
>  
> +config VIDEO_OV2685
> +	tristate "OmniVision OV2685 sensor support"
> +	depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
> +	depends on MEDIA_CAMERA_SUPPORT
> +	select V4L2_FWNODE
> +	---help---
> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
> +	  OV2685 camera.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called ov2685.
> +
>  config VIDEO_OV5640
>  	tristate "OmniVision OV5640 sensor support"
>  	depends on OF
> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
> index a063030..3054c69 100644
> --- a/drivers/media/i2c/Makefile
> +++ b/drivers/media/i2c/Makefile
> @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
>  obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
>  obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
>  obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
> +obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>  obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>  obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>  obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
> diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
> new file mode 100644
> index 0000000..e037d20
> --- /dev/null
> +++ b/drivers/media/i2c/ov2685.c
> @@ -0,0 +1,841 @@
> +/*
> + * ov2685 driver
> + *
> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#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 <media/media-entity.h>
> +#include <media/v4l2-async.h>
> +#include <media/v4l2-ctrls.h>
> +#include <media/v4l2-subdev.h>
> +
> +#define CHIP_ID				0x2685
> +#define OV2685_REG_CHIP_ID		0x300a
> +
> +#define REG_SC_CTRL_MODE		0x0100
> +#define     SC_CTRL_MODE_STANDBY	0x0
> +#define     SC_CTRL_MODE_STREAMING	BIT(0)
> +
> +#define OV2685_REG_EXPOSURE		0x3500
> +#define	OV2685_EXPOSURE_MIN		4
> +#define	OV2685_EXPOSURE_STEP		1
> +
> +#define OV2685_REG_VTS			0x380e
> +#define OV2685_VTS_MAX			0x7fff
> +
> +#define OV2685_REG_GAIN			0x350a
> +#define OV2685_GAIN_MIN			0
> +#define OV2685_GAIN_MAX			0x07ff
> +#define OV2685_GAIN_STEP		0x1
> +#define OV2685_GAIN_DEFAULT		0x0036
> +
> +#define OV2685_REG_TEST_PATTERN		0x5080
> +#define OV2685_TEST_PATTERN_DISABLED		0x00
> +#define OV2685_TEST_PATTERN_COLOR_BAR		0x80
> +#define OV2685_TEST_PATTERN_RND			0x81
> +#define OV2685_TEST_PATTERN_COLOR_BAR_FADE	0x88
> +#define OV2685_TEST_PATTERN_BW_SQUARE		0x92
> +#define OV2685_TEST_PATTERN_COLOR_SQUARE	0x82
> +
> +#define REG_NULL			0xFFFF
> +
> +#define OV2685_REG_VALUE_08BIT		1
> +#define OV2685_REG_VALUE_16BIT		2
> +#define OV2685_REG_VALUE_24BIT		3
> +
> +#define OV2685_LANES			1
> +#define OV2685_BITS_PER_SAMPLE		10
> +
> +struct regval {
> +	u16 addr;
> +	u8 val;
> +};
> +
> +struct ov2685_mode {
> +	u32 width;
> +	u32 height;
> +	u32 exp_def;
> +	u32 hts_def;
> +	u32 vts_def;
> +	const struct regval *reg_list;
> +};
> +
> +struct ov2685 {
> +	struct i2c_client	*client;
> +	struct clk		*xvclk;
> +	struct regulator	*avdd_regulator;	/* Analog power */
> +	struct regulator	*dovdd_regulator;	/* Digital I/O power */
> +				/* use internal DVDD power */
> +	struct gpio_desc	*reset_gpio;
> +
> +	bool			streaming;
> +	struct mutex		mutex;
> +	struct v4l2_subdev	subdev;
> +	struct media_pad	pad;
> +	struct v4l2_ctrl	*anal_gain;
> +	struct v4l2_ctrl	*exposure;
> +	struct v4l2_ctrl	*hblank;
> +	struct v4l2_ctrl	*vblank;
> +	struct v4l2_ctrl	*test_pattern;
> +	struct v4l2_ctrl_handler ctrl_handler;
> +
> +	const struct ov2685_mode *cur_mode;
> +};
> +#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
> +
> +/* PLL settings bases on 24M xvclk */
> +static struct regval ov2685_1600x1200_regs[] = {
> +	{0x0103, 0x01},
> +	{0x0100, 0x00},
> +	{0x3002, 0x00},
> +	{0x3016, 0x1c},
> +	{0x3018, 0x44},
> +	{0x301d, 0xf0},
> +	{0x3020, 0x00},
> +	{0x3082, 0x37},
> +	{0x3083, 0x03},
> +	{0x3084, 0x09},
> +	{0x3085, 0x04},
> +	{0x3086, 0x00},
> +	{0x3087, 0x00},
> +	{0x3501, 0x4e},
> +	{0x3502, 0xe0},
> +	{0x3503, 0x07},
> +	{0x350b, 0x36},
> +	{0x3600, 0xb4},
> +	{0x3603, 0x35},
> +	{0x3604, 0x24},
> +	{0x3605, 0x00},
> +	{0x3620, 0x24},
> +	{0x3621, 0x34},
> +	{0x3622, 0x03},
> +	{0x3628, 0x10},
> +	{0x3705, 0x3c},
> +	{0x370a, 0x21},
> +	{0x370c, 0x50},
> +	{0x370d, 0xc0},
> +	{0x3717, 0x58},
> +	{0x3718, 0x80},
> +	{0x3720, 0x00},
> +	{0x3721, 0x09},
> +	{0x3722, 0x06},
> +	{0x3723, 0x59},
> +	{0x3738, 0x99},
> +	{0x3781, 0x80},
> +	{0x3784, 0x0c},
> +	{0x3789, 0x60},
> +	{0x3800, 0x00},
> +	{0x3801, 0x00},
> +	{0x3802, 0x00},
> +	{0x3803, 0x00},
> +	{0x3804, 0x06},
> +	{0x3805, 0x4f},
> +	{0x3806, 0x04},
> +	{0x3807, 0xbf},
> +	{0x3808, 0x06},
> +	{0x3809, 0x40},
> +	{0x380a, 0x04},
> +	{0x380b, 0xb0},
> +	{0x380c, 0x06},
> +	{0x380d, 0xa4},
> +	{0x380e, 0x05},
> +	{0x380f, 0x0e},
> +	{0x3810, 0x00},
> +	{0x3811, 0x08},
> +	{0x3812, 0x00},
> +	{0x3813, 0x08},
> +	{0x3814, 0x11},
> +	{0x3815, 0x11},
> +	{0x3819, 0x04},
> +	{0x3820, 0xc0},
> +	{0x3821, 0x00},
> +	{0x3a06, 0x01},
> +	{0x3a07, 0x84},
> +	{0x3a08, 0x01},
> +	{0x3a09, 0x43},
> +	{0x3a0a, 0x24},
> +	{0x3a0b, 0x60},
> +	{0x3a0c, 0x28},
> +	{0x3a0d, 0x60},
> +	{0x3a0e, 0x04},
> +	{0x3a0f, 0x8c},
> +	{0x3a10, 0x05},
> +	{0x3a11, 0x0c},
> +	{0x4000, 0x81},
> +	{0x4001, 0x40},
> +	{0x4008, 0x02},
> +	{0x4009, 0x09},
> +	{0x4300, 0x00},
> +	{0x430e, 0x00},
> +	{0x4602, 0x02},
> +	{0x481b, 0x40},
> +	{0x481f, 0x40},
> +	{0x4837, 0x18},
> +	{0x5000, 0x1f},
> +	{0x5001, 0x05},
> +	{0x5002, 0x30},
> +	{0x5003, 0x04},
> +	{0x5004, 0x00},
> +	{0x5005, 0x0c},
> +	{0x5280, 0x15},
> +	{0x5281, 0x06},
> +	{0x5282, 0x06},
> +	{0x5283, 0x08},
> +	{0x5284, 0x1c},
> +	{0x5285, 0x1c},
> +	{0x5286, 0x20},
> +	{0x5287, 0x10},
> +	{REG_NULL, 0x00}
> +};
> +
> +#define OV2685_LINK_FREQ_330MHZ		330000000
> +static const s64 link_freq_menu_items[] = {
> +	OV2685_LINK_FREQ_330MHZ
> +};
> +
> +static const char * const ov2685_test_pattern_menu[] = {
> +	"Disabled",
> +	"Color Bar",
> +	"RND PATTERN",
> +	"Color Bar FADE",
> +	"BW SQUARE",
> +	"COLOR SQUARE"

Could you spell out the abbreviations and use capitalisation consistently?

> +};
> +
> +static const int ov2685_test_pattern_val[] = {
> +	OV2685_TEST_PATTERN_DISABLED,
> +	OV2685_TEST_PATTERN_COLOR_BAR,
> +	OV2685_TEST_PATTERN_RND,
> +	OV2685_TEST_PATTERN_COLOR_BAR_FADE,
> +	OV2685_TEST_PATTERN_BW_SQUARE,
> +	OV2685_TEST_PATTERN_COLOR_SQUARE,
> +};
> +
> +static const struct ov2685_mode supported_modes[] = {
> +	{
> +		.width = 1600,
> +		.height = 1200,
> +		.exp_def = 0x04ee,
> +		.hts_def = 0x06a4,
> +		.vts_def = 0x050e,
> +		.reg_list = ov2685_1600x1200_regs,
> +	},
> +};
> +
> +/* Write registers up to 4 at a time */
> +static int ov2685_write_reg(struct i2c_client *client, u16 reg,
> +			    unsigned int len, u32 val)
> +{
> +	int buf_i;
> +	int val_i;

unsigned int?

> +	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 ov2685_write_array(struct i2c_client *client,
> +			      const struct regval *regs)
> +{
> +	int i, ret = 0;

unsigned int i?

> +
> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
> +		ret = ov2685_write_reg(client, regs[i].addr,
> +				       OV2685_REG_VALUE_08BIT, regs[i].val);
> +
> +	return ret;
> +}
> +
> +/* Read registers up to 4 at a time */
> +static int ov2685_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)
> +		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 void ov2685_fill_fmt(struct ov2685 *ov2685,
> +			    struct v4l2_mbus_framefmt *fmt)
> +{
> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +	fmt->width = ov2685->cur_mode->width;
> +	fmt->height = ov2685->cur_mode->height;
> +	fmt->field = V4L2_FIELD_NONE;
> +}
> +
> +static int ov2685_set_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +
> +	ov2685_fill_fmt(ov2685, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov2685_get_fmt(struct v4l2_subdev *sd,
> +			  struct v4l2_subdev_pad_config *cfg,
> +			  struct v4l2_subdev_format *fmt)
> +{
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
> +
> +	ov2685_fill_fmt(ov2685, mbus_fmt);
> +
> +	return 0;
> +}
> +
> +static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
> +				 struct v4l2_subdev_pad_config *cfg,
> +				 struct v4l2_subdev_mbus_code_enum *code)
> +{
> +	if (code->index >= ARRAY_SIZE(supported_modes))
> +		return -EINVAL;
> +
> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;

Newline here?

> +	return 0;
> +}
> +
> +static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
> +				   struct v4l2_subdev_pad_config *cfg,
> +				   struct v4l2_subdev_frame_size_enum *fse)
> +{
> +	int index = fse->index;
> +
> +	if (index >= ARRAY_SIZE(supported_modes))
> +		return -EINVAL;
> +
> +	fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> +
> +	fse->min_width  = supported_modes[index].width;
> +	fse->max_width  = supported_modes[index].width;
> +	fse->max_height = supported_modes[index].height;
> +	fse->min_height = supported_modes[index].height;
> +
> +	return 0;
> +}
> +
> +static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
> +{
> +	ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
> +			 OV2685_REG_VALUE_24BIT, val << 4);

How about checking the return value for errors? Same elsewhere.

It'd also be simpler to move these to the s_ctrl callback handler below.

> +}
> +
> +static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
> +{
> +	ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
> +			 OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
> +}
> +
> +static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
> +{
> +	val += ov2685->cur_mode->height;
> +	ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
> +			 OV2685_REG_VALUE_16BIT, val);
> +}
> +
> +static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
> +{
> +	ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
> +			 OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
> +}
> +
> +static int __ov2685_power_on(struct ov2685 *ov2685)
> +{
> +	int ret;
> +	struct device *dev = &ov2685->client->dev;
> +
> +	ret = clk_prepare_enable(ov2685->xvclk);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable xvclk\n");
> +		return ret;
> +	}
> +	clk_set_rate(ov2685->xvclk, 24000000);
> +
> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
> +	/* AVDD and DOVDD may rise in any order */
> +	ret = regulator_enable(ov2685->avdd_regulator);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable AVDD regulator\n");
> +		goto disable_xvclk;
> +	}
> +	ret = regulator_enable(ov2685->dovdd_regulator);
> +	if (ret < 0) {
> +		dev_err(dev, "Failed to enable DOVDD regulator\n");
> +		goto disable_avdd;
> +	}
> +	/* The minimum delay between AVDD and reset rising can be 0 */
> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
> +	/* 8192 xvclk cycles prior to the first SCCB transaction.
> +	 * NOTE: An additional 1ms must be added to wait for
> +	 *       SCCB to become stable when using internal DVDD.
> +	 */
> +	usleep_range(1350, 1500);
> +
> +	/* HACK: ov2685 would output messy data after reset(R0103),
> +	 * writing register before .s_stream() as a workaround
> +	 */
> +	ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
> +
> +	return ret;
> +disable_avdd:
> +	regulator_disable(ov2685->avdd_regulator);
> +disable_xvclk:
> +	clk_disable_unprepare(ov2685->xvclk);
> +
> +	return ret;
> +}
> +
> +static void __ov2685_power_off(struct ov2685 *ov2685)
> +{
> +	/* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */

Could you calculate the delay if it's dependent on the clock frequency?

> +	usleep_range(30, 50);
> +	clk_disable_unprepare(ov2685->xvclk);
> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
> +	regulator_disable(ov2685->dovdd_regulator);
> +	regulator_disable(ov2685->avdd_regulator);
> +}
> +
> +static int ov2685_s_power(struct v4l2_subdev *sd, int on)
> +{
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	int ret = 0;
> +
> +	mutex_lock(&ov2685->mutex);
> +
> +	if (on)
> +		ret = pm_runtime_get_sync(&ov2685->client->dev);

If pm_runtime_get_sync fails, you need to decrement the usage count by
calling e.g. pm_runtime_put_noidle on the device.

If you move the power management to s_stream callback, you can remove the
s_power callback altogether --- these are essentially where the registers
are generally accessed, apart from the driver's probe function.

> +	else
> +		ret = pm_runtime_put(&ov2685->client->dev);
> +
> +	mutex_unlock(&ov2685->mutex);
> +
> +	return ret;
> +}
> +
> +static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
> +{
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	struct i2c_client *client = ov2685->client;
> +	int ret = 0;
> +
> +	mutex_lock(&ov2685->mutex);
> +
> +	on = !!on;
> +	if (on == ov2685->streaming)
> +		goto unlock_and_return;
> +
> +	if (on) {
> +		/* In case these controls are set before streaming */
> +		ov2685_set_exposure(ov2685, ov2685->exposure->val);
> +		ov2685_set_gain(ov2685, ov2685->anal_gain->val);
> +		ov2685_set_vts(ov2685, ov2685->vblank->val);
> +		ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);

You should use __v4l2_ctrl_handler_setup() here. Or put that to the
driver's runtime_resume function. That actually might be better.

> +
> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
> +		if (ret)
> +			goto unlock_and_return;
> +	} else {
> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
> +		if (ret)
> +			goto unlock_and_return;
> +	}
> +
> +	ov2685->streaming = on;
> +
> +unlock_and_return:
> +	mutex_unlock(&ov2685->mutex);
> +	return ret;
> +}
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
> +{
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	struct v4l2_mbus_framefmt *try_fmt;
> +
> +	mutex_lock(&ov2685->mutex);
> +
> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
> +	/* Initialize try_fmt */
> +	ov2685_fill_fmt(ov2685, try_fmt);

The default try format should not be dependent on current device
configuration but... the default configuration.

> +
> +	mutex_unlock(&ov2685->mutex);
> +
> +	return 0;
> +}
> +#endif
> +
> +static int ov2685_runtime_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +	int ret;
> +
> +	ret = __ov2685_power_on(ov2685);
> +	if (ret)
> +		return ret;
> +
> +	if (ov2685->streaming) {

This would be suitable for system sleep PM op, not runtime PM which sets
device power state while the entire system is up and running.

> +		ret = ov2685_s_stream(sd, 1);
> +		if (ret)
> +			__ov2685_power_off(ov2685);
> +	}
> +
> +	return ret;
> +}
> +
> +static int ov2685_runtime_suspend(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
> +	struct ov2685 *ov2685 = to_ov2685(sd);
> +
> +	__ov2685_power_off(ov2685);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops ov2685_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
> +			   ov2685_runtime_resume, NULL)
> +};
> +
> +static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
> +{
> +	struct ov2685 *ov2685 = container_of(ctrl->handler,
> +					     struct ov2685, ctrl_handler);
> +	struct i2c_client *client = ov2685->client;
> +	s64 max;
> +	int ret = 0;
> +
> +	/* Propagate change of current control to all related controls */
> +	switch (ctrl->id) {
> +	case V4L2_CID_VBLANK:
> +		/* Update max exposure while meeting expected vblanking */
> +		max = ov2685->cur_mode->height + ctrl->val - 4;
> +		__v4l2_ctrl_modify_range(ov2685->exposure,
> +					 ov2685->exposure->minimum, max,
> +					 ov2685->exposure->step,
> +					 ov2685->exposure->default_value);
> +		break;
> +	}
> +
> +	pm_runtime_get_sync(&client->dev);

Instead you could do as the ov13868 driver:

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

That way the device wouldn't need to be powered on (and likely off right
away) just for the sake of writing a register.

> +	switch (ctrl->id) {
> +	case V4L2_CID_EXPOSURE:
> +		ov2685_set_exposure(ov2685, ctrl->val);
> +		break;
> +	case V4L2_CID_ANALOGUE_GAIN:
> +		ov2685_set_gain(ov2685, ctrl->val);
> +		break;
> +	case V4L2_CID_VBLANK:
> +		ov2685_set_vts(ov2685, ctrl->val);
> +		break;
> +	case V4L2_CID_TEST_PATTERN:
> +		ov2685_enable_test_pattern(ov2685, ctrl->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 struct v4l2_subdev_core_ops ov2685_core_ops = {

const. Same below.

> +	.s_power = ov2685_s_power,
> +};
> +
> +static struct v4l2_subdev_video_ops ov2685_video_ops = {
> +	.s_stream = ov2685_s_stream,
> +};
> +
> +static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
> +	.enum_mbus_code = ov2685_enum_mbus_code,
> +	.enum_frame_size = ov2685_enum_frame_sizes,
> +	.get_fmt = ov2685_get_fmt,
> +	.set_fmt = ov2685_set_fmt,
> +};
> +
> +static struct v4l2_subdev_ops ov2685_subdev_ops = {
> +	.core	= &ov2685_core_ops,
> +	.video	= &ov2685_video_ops,
> +	.pad	= &ov2685_pad_ops,
> +};
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
> +	.open = ov2685_open,
> +};
> +#endif
> +
> +static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
> +	.s_ctrl = ov2685_set_ctrl,
> +};
> +
> +static int ov2685_initialize_controls(struct ov2685 *ov2685)
> +{
> +	const struct ov2685_mode *mode;
> +	struct v4l2_ctrl_handler *handler;
> +	struct v4l2_ctrl *ctrl;
> +	u64 exposure_max;
> +	u32 pixel_rate, h_blank;
> +	int ret;
> +
> +	handler = &ov2685->ctrl_handler;
> +	mode = ov2685->cur_mode;
> +	ret = v4l2_ctrl_handler_init(handler, 1);

8 would be a better guess.

> +	if (ret)
> +		return ret;
> +	handler->lock = &ov2685->mutex;
> +
> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
> +				      0, 0, link_freq_menu_items);
> +	if (ctrl)
> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
> +		     OV2685_BITS_PER_SAMPLE;
> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
> +			  0, pixel_rate, 1, pixel_rate);
> +
> +	h_blank = mode->hts_def - mode->width;
> +	ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
> +				h_blank, h_blank, 1, h_blank);
> +	if (ov2685->hblank)
> +		ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
> +
> +	ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
> +				V4L2_CID_VBLANK, mode->vts_def - mode->height,
> +				OV2685_VTS_MAX - mode->height, 1,
> +				mode->vts_def - mode->height);
> +
> +	exposure_max = mode->vts_def - 4;
> +	ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
> +				V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
> +				exposure_max, OV2685_EXPOSURE_STEP,
> +				mode->exp_def);
> +
> +	ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
> +				V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
> +				OV2685_GAIN_MAX, OV2685_GAIN_STEP,
> +				OV2685_GAIN_DEFAULT);
> +
> +	ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
> +				&ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
> +				ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
> +				0, 0, ov2685_test_pattern_menu);
> +
> +	if (handler->error) {
> +		v4l2_ctrl_handler_free(handler);
> +		return handler->error;

v4l2_ctrl_handler_free() sets handler->error to 0 currently.

As a matter of fact, it'd be nice to change that behaviour.

> +	}
> +
> +	ov2685->subdev.ctrl_handler = handler;
> +
> +	return 0;
> +}
> +
> +static int ov2685_check_sensor_id(struct ov2685 *ov2685,
> +				  struct i2c_client *client)
> +{
> +	struct device *dev = &ov2685->client->dev;
> +	int id, ret;
> +
> +	ret = __ov2685_power_on(ov2685);
> +	if (ret)
> +		return ret;

Newline?

> +	ov2685_read_reg(client, OV2685_REG_CHIP_ID,
> +			OV2685_REG_VALUE_16BIT, &id);

How about checking the return value?

> +	__ov2685_power_off(ov2685);
> +
> +	if (id != CHIP_ID) {
> +		dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
> +		return -EINVAL;
> +	}
> +
> +	dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
> +
> +	return 0;
> +}
> +
> +static int ov2685_probe(struct i2c_client *client,
> +			const struct i2c_device_id *id)
> +{
> +	struct device *dev = &client->dev;
> +	struct ov2685 *ov2685;
> +	int ret;
> +
> +	ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
> +	if (!ov2685)
> +		return -ENOMEM;
> +
> +	ov2685->client = client;
> +	ov2685->cur_mode = &supported_modes[0];
> +
> +	ov2685->xvclk = devm_clk_get(dev, "xvclk");
> +	if (IS_ERR(ov2685->xvclk)) {
> +		dev_err(dev, "Failed to get xvclk\n");
> +		return -EINVAL;
> +	}
> +
> +	ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> +	if (IS_ERR(ov2685->reset_gpio)) {
> +		dev_err(dev, "Failed to get reset-gpios\n");
> +		return -EINVAL;
> +	}
> +
> +	ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
> +	if (IS_ERR(ov2685->avdd_regulator)) {
> +		dev_err(dev, "Failed to get avdd-supply\n");
> +		return -EINVAL;
> +	}
> +	ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
> +	if (IS_ERR(ov2685->dovdd_regulator)) {
> +		dev_err(dev, "Failed to get dovdd-supply\n");
> +		return -EINVAL;
> +	}
> +
> +	mutex_init(&ov2685->mutex);
> +	v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
> +	ret = ov2685_initialize_controls(ov2685);
> +	if (ret)
> +		goto destroy_mutex;
> +
> +	ret = ov2685_check_sensor_id(ov2685, client);
> +	if (ret)
> +		return ret;
> +
> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> +	ov2685->subdev.internal_ops = &ov2685_internal_ops;
> +#endif
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
> +	ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> +	ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> +	ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
> +	if (ret < 0)
> +		goto free_ctrl_handler;
> +#endif
> +
> +	ret = v4l2_async_register_subdev(&ov2685->subdev);
> +	if (ret) {
> +		dev_err(dev, "v4l2 async register subdev failed\n");
> +		goto clean_entity;
> +	}
> +

The device should be already powered up here... and then:

pm_runtime_set_active(dev);

> +	pm_runtime_enable(dev);

This will then power it down:

pm_runtime_idle(dev);

> +	return 0;
> +
> +clean_entity:
> +#if defined(CONFIG_MEDIA_CONTROLLER)
> +	media_entity_cleanup(&ov2685->subdev.entity);
> +#endif
> +free_ctrl_handler:
> +	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
> +destroy_mutex:
> +	mutex_destroy(&ov2685->mutex);
> +
> +	return ret;
> +}
> +
> +static int ov2685_remove(struct i2c_client *client)
> +{
> +	struct ov2685 *ov2685 = i2c_get_clientdata(client);
> +
> +	__ov2685_power_off(ov2685);

This goes after unregistering the devices, see below:

> +	v4l2_async_unregister_subdev(&ov2685->subdev);
> +	media_entity_cleanup(&ov2685->subdev.entity);
> +	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
> +	mutex_destroy(&ov2685->mutex);

pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev))
		__ov2685_power_off(ov2685);
pm_runtime_set_suspended(&client->dev);

> +
> +	return 0;
> +}
> +
> +static const struct of_device_id ov2685_of_match[] = {
> +	{ .compatible = "ovti,ov2685" },
> +	{},
> +};
> +
> +static struct i2c_driver ov2685_i2c_driver = {
> +	.driver = {
> +		.name = "ov2685",
> +		.owner = THIS_MODULE,
> +		.pm = &ov2685_pm_ops,
> +		.of_match_table = ov2685_of_match
> +	},
> +	.probe		= &ov2685_probe,
> +	.remove		= &ov2685_remove,
> +};
> +
> +module_i2c_driver(ov2685_i2c_driver);
> +
> +MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
> +MODULE_LICENSE("GPL v2");

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685
       [not found]     ` <1514534905-21393-4-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
@ 2018-01-03 12:14       ` Sakari Ailus
  0 siblings, 0 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-03 12:14 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw

Hi Shunqian,

On Fri, Dec 29, 2017 at 04:08:25PM +0800, Shunqian Zheng wrote:
> Add device tree binding documentation for the OV2685 sensor.

DT bindings should precede the driver. Speaking of which --- you should add
an entry in the MAINTAINERS file for both.

> 
> Signed-off-by: Shunqian Zheng <zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
>  .../devicetree/bindings/media/i2c/ov2685.txt       | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/i2c/ov2685.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/i2c/ov2685.txt b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
> new file mode 100644
> index 0000000..85aec03
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/i2c/ov2685.txt
> @@ -0,0 +1,35 @@
> +* Omnivision OV2685 MIPI CSI-2 sensor
> +
> +Required Properties:
> +- compatible: should be "ovti,ov2685"
> +- clocks: reference to the 24M xvclk input clock.
> +- clock-names: should be "xvclk".
> +- avdd-supply: Analog voltage supply, 2.8 volts
> +- dvdd-supply: Digital core voltage supply, 1.2 volts
> +- reset-gpios: Low active reset gpio
> +
> +The device node must contain one 'port' child node for its digital output

s/must/shall/

Please add that the port shall contain one endpoint node as well.

> +video port, in accordance with the video interface bindings defined in
> +Documentation/devicetree/bindings/media/video-interfaces.txt.

Which specific properties are required and which ones are optional?

> +
> +Example:
> +	ucam: camera-sensor@3c {
> +		compatible = "ovti,ov2685";
> +		reg = <0x3c>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&clk_24m_cam>;
> +
> +		clocks = <&cru SCLK_TESTCLKOUT1>;
> +		clock-names = "xvclk";
> +
> +		avdd-supply = <&pp2800_cam>;
> +		dovdd-supply = <&pp1800>;
> +		reset-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>;
> +
> +		port {
> +			ucam_out: endpoint {
> +				remote-endpoint = <&mipi_in_ucam>;
> +				data-lanes = <1>;
> +			};
> +		};
> +	};

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor
  2017-12-29  8:08 [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor Shunqian Zheng
       [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
@ 2018-01-03 14:30 ` Sakari Ailus
  1 sibling, 0 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-03 14:30 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

On Fri, Dec 29, 2017 at 04:08:22PM +0800, Shunqian Zheng wrote:
> This patch adds driver for Omnivision's ov5695 sensor,
> the driver supports following features:
>  - supported resolutions
>    + 2592x1944 at 30fps
>    + 1920x1080 at 30fps
>    + 1296x972 at 60fps
>    + 1280x720 at 30fps
>    + 640x480 at 120fps
>  - test patterns
>  - manual exposure/gain(analog and digital) control
>  - vblank and hblank
>  - media controller
>  - runtime pm
> 
> Signed-off-by: Shunqian Zheng <zhengsq@rock-chips.com>

The same comments I've given on the other driver mostly appear to apply
this one as well.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
  2018-01-03 11:43     ` Sakari Ailus
@ 2018-01-08 13:37       ` Shunqian Zheng
  2018-01-08 22:23         ` Sakari Ailus
  2018-01-12  2:30       ` Shunqian Zheng
  1 sibling, 1 reply; 15+ messages in thread
From: Shunqian Zheng @ 2018-01-08 13:37 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

[-- Attachment #1: Type: text/plain, Size: 28143 bytes --]

Hi Sakari,

Thank you very much for your comments,


On 2018年01月03日 19:43, Sakari Ailus wrote:
> Hi Shunqian,
>
> Please see my comments below.
>
> On Fri, Dec 29, 2017 at 04:08:24PM +0800, Shunqian Zheng wrote:
>> This patch adds driver for Omnivision's ov2685 sensor.
>> Though the ov2685 can output yuv data, this driver only
>> supports the raw bayer format, including the following features:
>>    - output 1600x1200 at 30fps
>>    - test patterns
>>    - manual exposure/gain control
>>    - vblank and hblank
>>    - media controller
>>    - runtime pm
>>
>> Signed-off-by: Shunqian Zheng<zhengsq@rock-chips.com>
>> ---
>>   drivers/media/i2c/Kconfig  |  12 +
>>   drivers/media/i2c/Makefile |   1 +
>>   drivers/media/i2c/ov2685.c | 841 +++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 854 insertions(+)
>>   create mode 100644 drivers/media/i2c/ov2685.c
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 55b37c8..63a175d 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -586,6 +586,18 @@ config VIDEO_OV2659
>>   	  To compile this driver as a module, choose M here: the
>>   	  module will be called ov2659.
>>   
>> +config VIDEO_OV2685
>> +	tristate "OmniVision OV2685 sensor support"
>> +	depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER
>> +	depends on MEDIA_CAMERA_SUPPORT
>> +	select V4L2_FWNODE
>> +	---help---
>> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
>> +	  OV2685 camera.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called ov2685.
>> +
>>   config VIDEO_OV5640
>>   	tristate "OmniVision OV5640 sensor support"
>>   	depends on OF
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index a063030..3054c69 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o
>>   obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
>>   obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
>>   obj-$(CONFIG_VIDEO_OV2640) += ov2640.o
>> +obj-$(CONFIG_VIDEO_OV2685) += ov2685.o
>>   obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>>   obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>>   obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>> diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
>> new file mode 100644
>> index 0000000..e037d20
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov2685.c
>> @@ -0,0 +1,841 @@
>> +/*
>> + * ov2685 driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#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 <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#define CHIP_ID				0x2685
>> +#define OV2685_REG_CHIP_ID		0x300a
>> +
>> +#define REG_SC_CTRL_MODE		0x0100
>> +#define     SC_CTRL_MODE_STANDBY	0x0
>> +#define     SC_CTRL_MODE_STREAMING	BIT(0)
>> +
>> +#define OV2685_REG_EXPOSURE		0x3500
>> +#define	OV2685_EXPOSURE_MIN		4
>> +#define	OV2685_EXPOSURE_STEP		1
>> +
>> +#define OV2685_REG_VTS			0x380e
>> +#define OV2685_VTS_MAX			0x7fff
>> +
>> +#define OV2685_REG_GAIN			0x350a
>> +#define OV2685_GAIN_MIN			0
>> +#define OV2685_GAIN_MAX			0x07ff
>> +#define OV2685_GAIN_STEP		0x1
>> +#define OV2685_GAIN_DEFAULT		0x0036
>> +
>> +#define OV2685_REG_TEST_PATTERN		0x5080
>> +#define OV2685_TEST_PATTERN_DISABLED		0x00
>> +#define OV2685_TEST_PATTERN_COLOR_BAR		0x80
>> +#define OV2685_TEST_PATTERN_RND			0x81
>> +#define OV2685_TEST_PATTERN_COLOR_BAR_FADE	0x88
>> +#define OV2685_TEST_PATTERN_BW_SQUARE		0x92
>> +#define OV2685_TEST_PATTERN_COLOR_SQUARE	0x82
>> +
>> +#define REG_NULL			0xFFFF
>> +
>> +#define OV2685_REG_VALUE_08BIT		1
>> +#define OV2685_REG_VALUE_16BIT		2
>> +#define OV2685_REG_VALUE_24BIT		3
>> +
>> +#define OV2685_LANES			1
>> +#define OV2685_BITS_PER_SAMPLE		10
>> +
>> +struct regval {
>> +	u16 addr;
>> +	u8 val;
>> +};
>> +
>> +struct ov2685_mode {
>> +	u32 width;
>> +	u32 height;
>> +	u32 exp_def;
>> +	u32 hts_def;
>> +	u32 vts_def;
>> +	const struct regval *reg_list;
>> +};
>> +
>> +struct ov2685 {
>> +	struct i2c_client	*client;
>> +	struct clk		*xvclk;
>> +	struct regulator	*avdd_regulator;	/* Analog power */
>> +	struct regulator	*dovdd_regulator;	/* Digital I/O power */
>> +				/* use internal DVDD power */
>> +	struct gpio_desc	*reset_gpio;
>> +
>> +	bool			streaming;
>> +	struct mutex		mutex;
>> +	struct v4l2_subdev	subdev;
>> +	struct media_pad	pad;
>> +	struct v4l2_ctrl	*anal_gain;
>> +	struct v4l2_ctrl	*exposure;
>> +	struct v4l2_ctrl	*hblank;
>> +	struct v4l2_ctrl	*vblank;
>> +	struct v4l2_ctrl	*test_pattern;
>> +	struct v4l2_ctrl_handler ctrl_handler;
>> +
>> +	const struct ov2685_mode *cur_mode;
>> +};
>> +#define to_ov2685(sd) container_of(sd, struct ov2685, subdev)
>> +
>> +/* PLL settings bases on 24M xvclk */
>> +static struct regval ov2685_1600x1200_regs[] = {
>> +	{0x0103, 0x01},
>> +	{0x0100, 0x00},
>> +	{0x3002, 0x00},
>> +	{0x3016, 0x1c},
>> +	{0x3018, 0x44},
>> +	{0x301d, 0xf0},
>> +	{0x3020, 0x00},
>> +	{0x3082, 0x37},
>> +	{0x3083, 0x03},
>> +	{0x3084, 0x09},
>> +	{0x3085, 0x04},
>> +	{0x3086, 0x00},
>> +	{0x3087, 0x00},
>> +	{0x3501, 0x4e},
>> +	{0x3502, 0xe0},
>> +	{0x3503, 0x07},
>> +	{0x350b, 0x36},
>> +	{0x3600, 0xb4},
>> +	{0x3603, 0x35},
>> +	{0x3604, 0x24},
>> +	{0x3605, 0x00},
>> +	{0x3620, 0x24},
>> +	{0x3621, 0x34},
>> +	{0x3622, 0x03},
>> +	{0x3628, 0x10},
>> +	{0x3705, 0x3c},
>> +	{0x370a, 0x21},
>> +	{0x370c, 0x50},
>> +	{0x370d, 0xc0},
>> +	{0x3717, 0x58},
>> +	{0x3718, 0x80},
>> +	{0x3720, 0x00},
>> +	{0x3721, 0x09},
>> +	{0x3722, 0x06},
>> +	{0x3723, 0x59},
>> +	{0x3738, 0x99},
>> +	{0x3781, 0x80},
>> +	{0x3784, 0x0c},
>> +	{0x3789, 0x60},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x00},
>> +	{0x3803, 0x00},
>> +	{0x3804, 0x06},
>> +	{0x3805, 0x4f},
>> +	{0x3806, 0x04},
>> +	{0x3807, 0xbf},
>> +	{0x3808, 0x06},
>> +	{0x3809, 0x40},
>> +	{0x380a, 0x04},
>> +	{0x380b, 0xb0},
>> +	{0x380c, 0x06},
>> +	{0x380d, 0xa4},
>> +	{0x380e, 0x05},
>> +	{0x380f, 0x0e},
>> +	{0x3810, 0x00},
>> +	{0x3811, 0x08},
>> +	{0x3812, 0x00},
>> +	{0x3813, 0x08},
>> +	{0x3814, 0x11},
>> +	{0x3815, 0x11},
>> +	{0x3819, 0x04},
>> +	{0x3820, 0xc0},
>> +	{0x3821, 0x00},
>> +	{0x3a06, 0x01},
>> +	{0x3a07, 0x84},
>> +	{0x3a08, 0x01},
>> +	{0x3a09, 0x43},
>> +	{0x3a0a, 0x24},
>> +	{0x3a0b, 0x60},
>> +	{0x3a0c, 0x28},
>> +	{0x3a0d, 0x60},
>> +	{0x3a0e, 0x04},
>> +	{0x3a0f, 0x8c},
>> +	{0x3a10, 0x05},
>> +	{0x3a11, 0x0c},
>> +	{0x4000, 0x81},
>> +	{0x4001, 0x40},
>> +	{0x4008, 0x02},
>> +	{0x4009, 0x09},
>> +	{0x4300, 0x00},
>> +	{0x430e, 0x00},
>> +	{0x4602, 0x02},
>> +	{0x481b, 0x40},
>> +	{0x481f, 0x40},
>> +	{0x4837, 0x18},
>> +	{0x5000, 0x1f},
>> +	{0x5001, 0x05},
>> +	{0x5002, 0x30},
>> +	{0x5003, 0x04},
>> +	{0x5004, 0x00},
>> +	{0x5005, 0x0c},
>> +	{0x5280, 0x15},
>> +	{0x5281, 0x06},
>> +	{0x5282, 0x06},
>> +	{0x5283, 0x08},
>> +	{0x5284, 0x1c},
>> +	{0x5285, 0x1c},
>> +	{0x5286, 0x20},
>> +	{0x5287, 0x10},
>> +	{REG_NULL, 0x00}
>> +};
>> +
>> +#define OV2685_LINK_FREQ_330MHZ		330000000
>> +static const s64 link_freq_menu_items[] = {
>> +	OV2685_LINK_FREQ_330MHZ
>> +};
>> +
>> +static const char * const ov2685_test_pattern_menu[] = {
>> +	"Disabled",
>> +	"Color Bar",
>> +	"RND PATTERN",
>> +	"Color Bar FADE",
>> +	"BW SQUARE",
>> +	"COLOR SQUARE"
> Could you spell out the abbreviations and use capitalisation consistently?
Done
>> +};
>> +
>> +static const int ov2685_test_pattern_val[] = {
>> +	OV2685_TEST_PATTERN_DISABLED,
>> +	OV2685_TEST_PATTERN_COLOR_BAR,
>> +	OV2685_TEST_PATTERN_RND,
>> +	OV2685_TEST_PATTERN_COLOR_BAR_FADE,
>> +	OV2685_TEST_PATTERN_BW_SQUARE,
>> +	OV2685_TEST_PATTERN_COLOR_SQUARE,
>> +};
>> +
>> +static const struct ov2685_mode supported_modes[] = {
>> +	{
>> +		.width = 1600,
>> +		.height = 1200,
>> +		.exp_def = 0x04ee,
>> +		.hts_def = 0x06a4,
>> +		.vts_def = 0x050e,
>> +		.reg_list = ov2685_1600x1200_regs,
>> +	},
>> +};
>> +
>> +/* Write registers up to 4 at a time */
>> +static int ov2685_write_reg(struct i2c_client *client, u16 reg,
>> +			    unsigned int len, u32 val)
>> +{
>> +	int buf_i;
>> +	int val_i;
> unsigned int?
Use u32 in this file for consistency.
>> +	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 ov2685_write_array(struct i2c_client *client,
>> +			      const struct regval *regs)
>> +{
>> +	int i, ret = 0;
> unsigned int i?
Use u32 in this file for consistency.
>> +
>> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
>> +		ret = ov2685_write_reg(client, regs[i].addr,
>> +				       OV2685_REG_VALUE_08BIT, regs[i].val);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Read registers up to 4 at a time */
>> +static int ov2685_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)
>> +		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 void ov2685_fill_fmt(struct ov2685 *ov2685,
>> +			    struct v4l2_mbus_framefmt *fmt)
>> +{
>> +	fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	fmt->width = ov2685->cur_mode->width;
>> +	fmt->height = ov2685->cur_mode->height;
>> +	fmt->field = V4L2_FIELD_NONE;
>> +}
>> +
>> +static int ov2685_set_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_pad_config *cfg,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +
>> +	ov2685_fill_fmt(ov2685, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2685_get_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_pad_config *cfg,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	struct v4l2_mbus_framefmt *mbus_fmt = &fmt->format;
>> +
>> +	ov2685_fill_fmt(ov2685, mbus_fmt);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2685_enum_mbus_code(struct v4l2_subdev *sd,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	if (code->index >= ARRAY_SIZE(supported_modes))
>> +		return -EINVAL;
>> +
>> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
> Newline here?
Done
>> +	return 0;
>> +}
>> +
>> +static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd,
>> +				   struct v4l2_subdev_pad_config *cfg,
>> +				   struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> +	int index = fse->index;
>> +
>> +	if (index >= ARRAY_SIZE(supported_modes))
>> +		return -EINVAL;
>> +
>> +	fse->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +
>> +	fse->min_width  = supported_modes[index].width;
>> +	fse->max_width  = supported_modes[index].width;
>> +	fse->max_height = supported_modes[index].height;
>> +	fse->min_height = supported_modes[index].height;
>> +
>> +	return 0;
>> +}
>> +
>> +static inline void ov2685_set_exposure(struct ov2685 *ov2685, s32 val)
>> +{
>> +	ov2685_write_reg(ov2685->client, OV2685_REG_EXPOSURE,
>> +			 OV2685_REG_VALUE_24BIT, val << 4);
> How about checking the return value for errors? Same elsewhere.
>
> It'd also be simpler to move these to the s_ctrl callback handler below.
Done
>> +}
>> +
>> +static inline void ov2685_set_gain(struct ov2685 *ov2685, s32 val)
>> +{
>> +	ov2685_write_reg(ov2685->client, OV2685_REG_GAIN,
>> +			 OV2685_REG_VALUE_16BIT, val & OV2685_GAIN_MAX);
>> +}
>> +
>> +static inline void ov2685_set_vts(struct ov2685 *ov2685, s32 val)
>> +{
>> +	val += ov2685->cur_mode->height;
>> +	ov2685_write_reg(ov2685->client, OV2685_REG_VTS,
>> +			 OV2685_REG_VALUE_16BIT, val);
>> +}
>> +
>> +static inline void ov2685_enable_test_pattern(struct ov2685 *ov2685, u32 pat)
>> +{
>> +	ov2685_write_reg(ov2685->client, OV2685_REG_TEST_PATTERN,
>> +			 OV2685_REG_VALUE_08BIT, ov2685_test_pattern_val[pat]);
>> +}
>> +
>> +static int __ov2685_power_on(struct ov2685 *ov2685)
>> +{
>> +	int ret;
>> +	struct device *dev = &ov2685->client->dev;
>> +
>> +	ret = clk_prepare_enable(ov2685->xvclk);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable xvclk\n");
>> +		return ret;
>> +	}
>> +	clk_set_rate(ov2685->xvclk, 24000000);
>> +
>> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
>> +	/* AVDD and DOVDD may rise in any order */
>> +	ret = regulator_enable(ov2685->avdd_regulator);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable AVDD regulator\n");
>> +		goto disable_xvclk;
>> +	}
>> +	ret = regulator_enable(ov2685->dovdd_regulator);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable DOVDD regulator\n");
>> +		goto disable_avdd;
>> +	}
>> +	/* The minimum delay between AVDD and reset rising can be 0 */
>> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 0);
>> +	/* 8192 xvclk cycles prior to the first SCCB transaction.
>> +	 * NOTE: An additional 1ms must be added to wait for
>> +	 *       SCCB to become stable when using internal DVDD.
>> +	 */
>> +	usleep_range(1350, 1500);
>> +
>> +	/* HACK: ov2685 would output messy data after reset(R0103),
>> +	 * writing register before .s_stream() as a workaround
>> +	 */
>> +	ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list);
>> +
>> +	return ret;
>> +disable_avdd:
>> +	regulator_disable(ov2685->avdd_regulator);
>> +disable_xvclk:
>> +	clk_disable_unprepare(ov2685->xvclk);
>> +
>> +	return ret;
>> +}
>> +
>> +static void __ov2685_power_off(struct ov2685 *ov2685)
>> +{
>> +	/* 512 xvclk cycles after the last SCCB transaction or MIPI frame end */
> Could you calculate the delay if it's dependent on the clock frequency?
Done
>> +	usleep_range(30, 50);
>> +	clk_disable_unprepare(ov2685->xvclk);
>> +	gpiod_set_value_cansleep(ov2685->reset_gpio, 1);
>> +	regulator_disable(ov2685->dovdd_regulator);
>> +	regulator_disable(ov2685->avdd_regulator);
>> +}
>> +
>> +static int ov2685_s_power(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov2685->mutex);
>> +
>> +	if (on)
>> +		ret = pm_runtime_get_sync(&ov2685->client->dev);
> If pm_runtime_get_sync fails, you need to decrement the usage count by
> calling e.g. pm_runtime_put_noidle on the device.
Done
> If you move the power management to s_stream callback, you can remove the
> s_power callback altogether --- these are essentially where the registers
> are generally accessed, apart from the driver's probe function.
Removed s_power callback.
>> +	else
>> +		ret = pm_runtime_put(&ov2685->client->dev);
>> +
>> +	mutex_unlock(&ov2685->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	struct i2c_client *client = ov2685->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov2685->mutex);
>> +
>> +	on = !!on;
>> +	if (on == ov2685->streaming)
>> +		goto unlock_and_return;
>> +
>> +	if (on) {
>> +		/* In case these controls are set before streaming */
>> +		ov2685_set_exposure(ov2685, ov2685->exposure->val);
>> +		ov2685_set_gain(ov2685, ov2685->anal_gain->val);
>> +		ov2685_set_vts(ov2685, ov2685->vblank->val);
>> +		ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
> You should use __v4l2_ctrl_handler_setup() here. Or put that to the
> driver's runtime_resume function. That actually might be better.
Put __v4l2_ctrl_handler_setup to ov2685_runtime_resume().
>> +
>> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
>> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +	} else {
>> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
>> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +	}
>> +
>> +	ov2685->streaming = on;
>> +
>> +unlock_and_return:
>> +	mutex_unlock(&ov2685->mutex);
>> +	return ret;
>> +}
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static int ov2685_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	struct v4l2_mbus_framefmt *try_fmt;
>> +
>> +	mutex_lock(&ov2685->mutex);
>> +
>> +	try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0);
>> +	/* Initialize try_fmt */
>> +	ov2685_fill_fmt(ov2685, try_fmt);
> The default try format should not be dependent on current device
> configuration but... the default configuration.
>
>> +
>> +	mutex_unlock(&ov2685->mutex);
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static int ov2685_runtime_resume(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	int ret;
>> +
>> +	ret = __ov2685_power_on(ov2685);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (ov2685->streaming) {
> This would be suitable for system sleep PM op, not runtime PM which sets
> device power state while the entire system is up and running.
Removed.
>> +		ret = ov2685_s_stream(sd, 1);
>> +		if (ret)
>> +			__ov2685_power_off(ov2685);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov2685_runtime_suspend(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +
>> +	__ov2685_power_off(ov2685);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct dev_pm_ops ov2685_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(ov2685_runtime_suspend,
>> +			   ov2685_runtime_resume, NULL)
>> +};
>> +
>> +static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct ov2685 *ov2685 = container_of(ctrl->handler,
>> +					     struct ov2685, ctrl_handler);
>> +	struct i2c_client *client = ov2685->client;
>> +	s64 max;
>> +	int ret = 0;
>> +
>> +	/* Propagate change of current control to all related controls */
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_VBLANK:
>> +		/* Update max exposure while meeting expected vblanking */
>> +		max = ov2685->cur_mode->height + ctrl->val - 4;
>> +		__v4l2_ctrl_modify_range(ov2685->exposure,
>> +					 ov2685->exposure->minimum, max,
>> +					 ov2685->exposure->step,
>> +					 ov2685->exposure->default_value);
>> +		break;
>> +	}
>> +
>> +	pm_runtime_get_sync(&client->dev);
> Instead you could do as the ov13868 driver:
>
> 	if (pm_runtime_get_if_in_use(&client->dev) <= 0)
> 		return 0;
>
> That way the device wouldn't need to be powered on (and likely off right
> away) just for the sake of writing a register.
Done.
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_EXPOSURE:
>> +		ov2685_set_exposure(ov2685, ctrl->val);
>> +		break;
>> +	case V4L2_CID_ANALOGUE_GAIN:
>> +		ov2685_set_gain(ov2685, ctrl->val);
>> +		break;
>> +	case V4L2_CID_VBLANK:
>> +		ov2685_set_vts(ov2685, ctrl->val);
>> +		break;
>> +	case V4L2_CID_TEST_PATTERN:
>> +		ov2685_enable_test_pattern(ov2685, ctrl->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 struct v4l2_subdev_core_ops ov2685_core_ops = {
> const. Same below.
Done.
>> +	.s_power = ov2685_s_power,
>> +};
>> +
>> +static struct v4l2_subdev_video_ops ov2685_video_ops = {
>> +	.s_stream = ov2685_s_stream,
>> +};
>> +
>> +static struct v4l2_subdev_pad_ops ov2685_pad_ops = {
>> +	.enum_mbus_code = ov2685_enum_mbus_code,
>> +	.enum_frame_size = ov2685_enum_frame_sizes,
>> +	.get_fmt = ov2685_get_fmt,
>> +	.set_fmt = ov2685_set_fmt,
>> +};
>> +
>> +static struct v4l2_subdev_ops ov2685_subdev_ops = {
>> +	.core	= &ov2685_core_ops,
>> +	.video	= &ov2685_video_ops,
>> +	.pad	= &ov2685_pad_ops,
>> +};
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static const struct v4l2_subdev_internal_ops ov2685_internal_ops = {
>> +	.open = ov2685_open,
>> +};
>> +#endif
>> +
>> +static const struct v4l2_ctrl_ops ov2685_ctrl_ops = {
>> +	.s_ctrl = ov2685_set_ctrl,
>> +};
>> +
>> +static int ov2685_initialize_controls(struct ov2685 *ov2685)
>> +{
>> +	const struct ov2685_mode *mode;
>> +	struct v4l2_ctrl_handler *handler;
>> +	struct v4l2_ctrl *ctrl;
>> +	u64 exposure_max;
>> +	u32 pixel_rate, h_blank;
>> +	int ret;
>> +
>> +	handler = &ov2685->ctrl_handler;
>> +	mode = ov2685->cur_mode;
>> +	ret = v4l2_ctrl_handler_init(handler, 1);
> 8 would be a better guess.
Done.
>> +	if (ret)
>> +		return ret;
>> +	handler->lock = &ov2685->mutex;
>> +
>> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
>> +				      0, 0, link_freq_menu_items);
>> +	if (ctrl)
>> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	pixel_rate = (link_freq_menu_items[0] * 2 * OV2685_LANES) /
>> +		     OV2685_BITS_PER_SAMPLE;
>> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
>> +			  0, pixel_rate, 1, pixel_rate);
>> +
>> +	h_blank = mode->hts_def - mode->width;
>> +	ov2685->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
>> +				h_blank, h_blank, 1, h_blank);
>> +	if (ov2685->hblank)
>> +		ov2685->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	ov2685->vblank = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
>> +				V4L2_CID_VBLANK, mode->vts_def - mode->height,
>> +				OV2685_VTS_MAX - mode->height, 1,
>> +				mode->vts_def - mode->height);
>> +
>> +	exposure_max = mode->vts_def - 4;
>> +	ov2685->exposure = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
>> +				V4L2_CID_EXPOSURE, OV2685_EXPOSURE_MIN,
>> +				exposure_max, OV2685_EXPOSURE_STEP,
>> +				mode->exp_def);
>> +
>> +	ov2685->anal_gain = v4l2_ctrl_new_std(handler, &ov2685_ctrl_ops,
>> +				V4L2_CID_ANALOGUE_GAIN, OV2685_GAIN_MIN,
>> +				OV2685_GAIN_MAX, OV2685_GAIN_STEP,
>> +				OV2685_GAIN_DEFAULT);
>> +
>> +	ov2685->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
>> +				&ov2685_ctrl_ops, V4L2_CID_TEST_PATTERN,
>> +				ARRAY_SIZE(ov2685_test_pattern_menu) - 1,
>> +				0, 0, ov2685_test_pattern_menu);
>> +
>> +	if (handler->error) {
>> +		v4l2_ctrl_handler_free(handler);
>> +		return handler->error;
> v4l2_ctrl_handler_free() sets handler->error to 0 currently.
Done.
> As a matter of fact, it'd be nice to change that behaviour.
>
>> +	}
>> +
>> +	ov2685->subdev.ctrl_handler = handler;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2685_check_sensor_id(struct ov2685 *ov2685,
>> +				  struct i2c_client *client)
>> +{
>> +	struct device *dev = &ov2685->client->dev;
>> +	int id, ret;
>> +
>> +	ret = __ov2685_power_on(ov2685);
>> +	if (ret)
>> +		return ret;
> Newline?
Done.
>> +	ov2685_read_reg(client, OV2685_REG_CHIP_ID,
>> +			OV2685_REG_VALUE_16BIT, &id);
> How about checking the return value?
Print the return value to kernel log if id is not expected.
>> +	__ov2685_power_off(ov2685);
>> +
>> +	if (id != CHIP_ID) {
>> +		dev_err(dev, "Wrong camera sensor id(%04x)\n", id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	dev_info(dev, "Detected OV%04x sensor\n", CHIP_ID);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov2685_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct ov2685 *ov2685;
>> +	int ret;
>> +
>> +	ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
>> +	if (!ov2685)
>> +		return -ENOMEM;
>> +
>> +	ov2685->client = client;
>> +	ov2685->cur_mode = &supported_modes[0];
>> +
>> +	ov2685->xvclk = devm_clk_get(dev, "xvclk");
>> +	if (IS_ERR(ov2685->xvclk)) {
>> +		dev_err(dev, "Failed to get xvclk\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ov2685->reset_gpio)) {
>> +		dev_err(dev, "Failed to get reset-gpios\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
>> +	if (IS_ERR(ov2685->avdd_regulator)) {
>> +		dev_err(dev, "Failed to get avdd-supply\n");
>> +		return -EINVAL;
>> +	}
>> +	ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
>> +	if (IS_ERR(ov2685->dovdd_regulator)) {
>> +		dev_err(dev, "Failed to get dovdd-supply\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_init(&ov2685->mutex);
>> +	v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
>> +	ret = ov2685_initialize_controls(ov2685);
>> +	if (ret)
>> +		goto destroy_mutex;
>> +
>> +	ret = ov2685_check_sensor_id(ov2685, client);
>> +	if (ret)
>> +		return ret;
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +	ov2685->subdev.internal_ops = &ov2685_internal_ops;
>> +#endif
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> +	ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +	ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
>> +	if (ret < 0)
>> +		goto free_ctrl_handler;
>> +#endif
>> +
>> +	ret = v4l2_async_register_subdev(&ov2685->subdev);
>> +	if (ret) {
>> +		dev_err(dev, "v4l2 async register subdev failed\n");
>> +		goto clean_entity;
>> +	}
>> +
> The device should be already powered up here... and then:
>
> pm_runtime_set_active(dev);
In my test case, sensor is not powered (power on and off in 
ov2685_check_sensor_id()).
Checking the ov13858.c which is turned on by i2c-core with ACPI domain 
PM, this driver
doesn't depend on ACPI.
>> +	pm_runtime_enable(dev);
> This will then power it down:
>
> pm_runtime_idle(dev);
>
>> +	return 0;
>> +
>> +clean_entity:
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	media_entity_cleanup(&ov2685->subdev.entity);
>> +#endif
>> +free_ctrl_handler:
>> +	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
>> +destroy_mutex:
>> +	mutex_destroy(&ov2685->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov2685_remove(struct i2c_client *client)
>> +{
>> +	struct ov2685 *ov2685 = i2c_get_clientdata(client);
>> +
>> +	__ov2685_power_off(ov2685);
> This goes after unregistering the devices, see below:
>
>> +	v4l2_async_unregister_subdev(&ov2685->subdev);
>> +	media_entity_cleanup(&ov2685->subdev.entity);
>> +	v4l2_ctrl_handler_free(&ov2685->ctrl_handler);
>> +	mutex_destroy(&ov2685->mutex);
> pm_runtime_disable(&client->dev);
> if (!pm_runtime_status_suspended(&client->dev))
> 		__ov2685_power_off(ov2685);
> pm_runtime_set_suspended(&client->dev);
Done.

Thanks
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id ov2685_of_match[] = {
>> +	{ .compatible = "ovti,ov2685" },
>> +	{},
>> +};
>> +
>> +static struct i2c_driver ov2685_i2c_driver = {
>> +	.driver = {
>> +		.name = "ov2685",
>> +		.owner = THIS_MODULE,
>> +		.pm = &ov2685_pm_ops,
>> +		.of_match_table = ov2685_of_match
>> +	},
>> +	.probe		= &ov2685_probe,
>> +	.remove		= &ov2685_remove,
>> +};
>> +
>> +module_i2c_driver(ov2685_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("OmniVision ov2685 sensor driver");
>> +MODULE_LICENSE("GPL v2");


[-- Attachment #2: Type: text/html, Size: 31662 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor
  2018-01-02 10:09   ` [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor jacopo mondi
@ 2018-01-08 13:40     ` Shunqian Zheng
  0 siblings, 0 replies; 15+ messages in thread
From: Shunqian Zheng @ 2018-01-08 13:40 UTC (permalink / raw)
  To: jacopo mondi
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

[-- Attachment #1: Type: text/plain, Size: 39396 bytes --]

Hi jacopo,


On 2018年01月02日 18:09, jacopo mondi wrote:
> Hi Shunqian,
>     thank you for the patch
>
> On Fri, Dec 29, 2017 at 04:08:22PM +0800, Shunqian Zheng wrote:
>> This patch adds driver for Omnivision's ov5695 sensor,
>> the driver supports following features:
>>   - supported resolutions
>>     + 2592x1944 at 30fps
>>     + 1920x1080 at 30fps
>>     + 1296x972 at 60fps
>>     + 1280x720 at 30fps
>>     + 640x480 at 120fps
>>   - test patterns
>>   - manual exposure/gain(analog and digital) control
>>   - vblank and hblank
>>   - media controller
>>   - runtime pm
>>
>> Signed-off-by: Shunqian Zheng<zhengsq@rock-chips.com>
>> ---
>>   drivers/media/i2c/Kconfig  |   11 +
>>   drivers/media/i2c/Makefile |    1 +
>>   drivers/media/i2c/ov5695.c | 1396 ++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 1408 insertions(+)
>>   create mode 100644 drivers/media/i2c/ov5695.c
>>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 3c6d642..55b37c8 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -645,6 +645,17 @@ config VIDEO_OV5670
>>   	  To compile this driver as a module, choose M here: the
>>   	  module will be called ov5670.
>>
>> +config VIDEO_OV5695
>> +	tristate "OmniVision OV5695 sensor support"
>> +	depends on I2C && VIDEO_V4L2
>> +	depends on MEDIA_CAMERA_SUPPORT
>> +	---help---
>> +	  This is a Video4Linux2 sensor-level driver for the OmniVision
>> +	  OV5695 camera.
>> +
>> +	  To compile this driver as a module, choose M here: the
>> +	  module will be called ov5695.
>> +
>>   config VIDEO_OV7640
>>   	tristate "OmniVision OV7640 sensor support"
>>   	depends on I2C && VIDEO_V4L2
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index 548a9ef..a063030 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_OV5640) += ov5640.o
>>   obj-$(CONFIG_VIDEO_OV5645) += ov5645.o
>>   obj-$(CONFIG_VIDEO_OV5647) += ov5647.o
>>   obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
>> +obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
>>   obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
>>   obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
>>   obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
>> diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
>> new file mode 100644
>> index 0000000..4745da4
>> --- /dev/null
>> +++ b/drivers/media/i2c/ov5695.c
>> @@ -0,0 +1,1396 @@
>> +/*
>> + * ov5695 driver
>> + *
>> + * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
> Please use SPDX identifiers and drop the license text
Done.
>> +
>> +#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 <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +#ifndef V4L2_CID_DIGITAL_GAIN
>> +#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
>> +#endif
>> +
>> +/* 45Mhz * 4 Binning */
>> +#define OV5695_PIXEL_RATE		(45 * 1000 * 1000 * 4)
>> +#define CHIP_ID				0x005695
>> +#define OV5695_REG_CHIP_ID		0x300a
>> +
>> +#define OV5695_REG_CTRL_MODE		0x0100
>> +#define OV5695_MODE_SW_STANDBY		0x0
>> +#define OV5695_MODE_STREAMING		BIT(0)
>> +
>> +#define OV5695_REG_EXPOSURE		0x3500
>> +#define	OV5695_EXPOSURE_MIN		4
>> +#define	OV5695_EXPOSURE_STEP		1
>> +#define OV5695_VTS_MAX			0x7fff
>> +
>> +#define OV5695_REG_ANALOG_GAIN		0x3509
>> +#define	ANALOG_GAIN_MIN			0x10
>> +#define	ANALOG_GAIN_MAX			0xf8
>> +#define	ANALOG_GAIN_STEP		1
>> +#define	ANALOG_GAIN_DEFAULT		0xf8
>> +
>> +#define OV5695_REG_DIGI_GAIN_H		0x350a
>> +#define OV5695_REG_DIGI_GAIN_L		0x350b
>> +#define OV5695_DIGI_GAIN_L_MASK		0x3f
>> +#define OV5695_DIGI_GAIN_H_SHIFT	6
>> +#define OV5695_DIGI_GAIN_MIN		0
>> +#define OV5695_DIGI_GAIN_MAX		(0x4000 - 1)
>> +#define OV5695_DIGI_GAIN_STEP		1
>> +#define OV5695_DIGI_GAIN_DEFAULT	1024
>> +
>> +#define OV5695_REG_TEST_PATTERN		0x4503
>> +#define	OV5695_TEST_PATTERN_ENABLE	0x80
>> +#define	OV5695_TEST_PATTERN_DISABLE	0x0
>> +
>> +#define OV5695_REG_VTS			0x380e
>> +
>> +#define REG_NULL			0xFFFF
>> +
>> +#define OV5695_REG_VALUE_08BIT		1
>> +#define OV5695_REG_VALUE_16BIT		2
>> +#define OV5695_REG_VALUE_24BIT		3
>> +
>> +#define OV5695_LANES			2
>> +#define OV5695_BITS_PER_SAMPLE		10
>> +
>> +struct regval {
>> +	u16 addr;
>> +	u8 val;
>> +};
>> +
>> +struct ov5695_mode {
>> +	u32 width;
>> +	u32 height;
>> +	u32 max_fps;
>> +	u32 hts_def;
>> +	u32 vts_def;
>> +	u32 exp_def;
>> +	const struct regval *reg_list;
>> +};
>> +
>> +struct ov5695 {
>> +	struct i2c_client	*client;
>> +	struct clk		*xvclk;
>> +	struct regulator        *avdd_regulator;
>> +	struct regulator        *dovdd_regulator;
>> +	struct regulator        *dvdd_regulator;
>> +	struct gpio_desc	*reset_gpio;
>> +
>> +	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	*test_pattern;
>> +	struct mutex		mutex;
>> +	bool			streaming;
>> +	const struct ov5695_mode *cur_mode;
>> +};
>> +#define to_ov5695(sd) container_of(sd, struct ov5695, subdev)
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 672(0x2a0)
>> + * framelength 2232(0x8b8)
>> + * grabwindow_width 1296
>> + * grabwindow_height 972
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_global_regs[] = {
>> +	{0x0103, 0x01},
>> +	{0x0100, 0x00},
>> +	{0x0300, 0x04},
>> +	{0x0301, 0x00},
>> +	{0x0302, 0x69},
>> +	{0x0303, 0x00},
>> +	{0x0304, 0x00},
>> +	{0x0305, 0x01},
>> +	{0x0307, 0x00},
>> +	{0x030b, 0x00},
>> +	{0x030c, 0x00},
>> +	{0x030d, 0x1e},
>> +	{0x030e, 0x04},
>> +	{0x030f, 0x03},
>> +	{0x0312, 0x01},
>> +	{0x3000, 0x00},
>> +	{0x3002, 0xa1},
>> +	{0x0308, 0x00},
>> +	{0x0310, 0x00},
>> +	{0x3022, 0x51},
>> +	{0x3106, 0x15},
>> +	{0x3107, 0x01},
>> +	{0x3108, 0x05},
>> +	{0x3500, 0x00},
>> +	{0x3501, 0x45},
>> +	{0x3502, 0x00},
>> +	{0x3503, 0x08},
>> +	{0x3504, 0x03},
>> +	{0x3505, 0x8c},
>> +	{0x3507, 0x03},
>> +	{0x3508, 0x00},
>> +	{0x3509, 0x10},
>> +	{0x350c, 0x00},
>> +	{0x350d, 0x80},
>> +	{0x3510, 0x00},
>> +	{0x3511, 0x02},
>> +	{0x3512, 0x00},
>> +	{0x3601, 0x55},
>> +	{0x3602, 0x58},
>> +	{0x3614, 0x30},
>> +	{0x3615, 0x77},
>> +	{0x3621, 0x08},
>> +	{0x3624, 0x40},
>> +	{0x3633, 0x0c},
>> +	{0x3634, 0x0c},
>> +	{0x3635, 0x0c},
>> +	{0x3636, 0x0c},
>> +	{0x3638, 0x00},
>> +	{0x3639, 0x00},
>> +	{0x363a, 0x00},
>> +	{0x363b, 0x00},
>> +	{0x363c, 0xff},
>> +	{0x363d, 0xfa},
>> +	{0x3650, 0x44},
>> +	{0x3651, 0x44},
>> +	{0x3652, 0x44},
>> +	{0x3653, 0x44},
>> +	{0x3654, 0x44},
>> +	{0x3655, 0x44},
>> +	{0x3656, 0x44},
>> +	{0x3657, 0x44},
>> +	{0x3660, 0x00},
>> +	{0x3661, 0x00},
>> +	{0x3662, 0x00},
>> +	{0x366a, 0x00},
>> +	{0x366e, 0x0c},
>> +	{0x3673, 0x04},
>> +	{0x3700, 0x14},
>> +	{0x3703, 0x0c},
>> +	{0x3715, 0x01},
>> +	{0x3733, 0x10},
>> +	{0x3734, 0x40},
>> +	{0x373f, 0xa0},
>> +	{0x3765, 0x20},
>> +	{0x37a1, 0x1d},
>> +	{0x37a8, 0x26},
>> +	{0x37ab, 0x14},
>> +	{0x37c2, 0x04},
>> +	{0x37cb, 0x09},
>> +	{0x37cc, 0x13},
>> +	{0x37cd, 0x1f},
>> +	{0x37ce, 0x1f},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x00},
>> +	{0x3803, 0x00},
>> +	{0x3804, 0x0a},
>> +	{0x3805, 0x3f},
>> +	{0x3806, 0x07},
>> +	{0x3807, 0xaf},
>> +	{0x3808, 0x05},
>> +	{0x3809, 0x10},
>> +	{0x380a, 0x03},
>> +	{0x380b, 0xcc},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xa0},
>> +	{0x380e, 0x08},
>> +	{0x380f, 0xb8},
>> +	{0x3810, 0x00},
>> +	{0x3811, 0x06},
>> +	{0x3812, 0x00},
>> +	{0x3813, 0x06},
>> +	{0x3814, 0x03},
>> +	{0x3815, 0x01},
>> +	{0x3816, 0x03},
>> +	{0x3817, 0x01},
>> +	{0x3818, 0x00},
>> +	{0x3819, 0x00},
>> +	{0x381a, 0x00},
>> +	{0x381b, 0x01},
>> +	{0x3820, 0x8b},
>> +	{0x3821, 0x01},
>> +	{0x3c80, 0x08},
>> +	{0x3c82, 0x00},
>> +	{0x3c83, 0x00},
>> +	{0x3c88, 0x00},
>> +	{0x3d85, 0x14},
>> +	{0x3f02, 0x08},
>> +	{0x3f03, 0x10},
>> +	{0x4008, 0x02},
>> +	{0x4009, 0x09},
>> +	{0x404e, 0x20},
>> +	{0x4501, 0x00},
>> +	{0x4502, 0x10},
>> +	{0x4800, 0x00},
>> +	{0x481f, 0x2a},
>> +	{0x4837, 0x13},
>> +	{0x5000, 0x17},
>> +	{0x5780, 0x3e},
>> +	{0x5781, 0x0f},
>> +	{0x5782, 0x44},
>> +	{0x5783, 0x02},
>> +	{0x5784, 0x01},
>> +	{0x5785, 0x01},
>> +	{0x5786, 0x00},
>> +	{0x5787, 0x04},
>> +	{0x5788, 0x02},
>> +	{0x5789, 0x0f},
>> +	{0x578a, 0xfd},
>> +	{0x578b, 0xf5},
>> +	{0x578c, 0xf5},
>> +	{0x578d, 0x03},
>> +	{0x578e, 0x08},
>> +	{0x578f, 0x0c},
>> +	{0x5790, 0x08},
>> +	{0x5791, 0x06},
>> +	{0x5792, 0x00},
>> +	{0x5793, 0x52},
>> +	{0x5794, 0xa3},
>> +	{0x5b00, 0x00},
>> +	{0x5b01, 0x1c},
>> +	{0x5b02, 0x00},
>> +	{0x5b03, 0x7f},
>> +	{0x5b05, 0x6c},
>> +	{0x5e10, 0xfc},
>> +	{0x4010, 0xf1},
>> +	{0x3503, 0x08},
>> +	{0x3505, 0x8c},
>> +	{0x3507, 0x03},
>> +	{0x3508, 0x00},
>> +	{0x3509, 0xf8},
>> +	{REG_NULL, 0x00},
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 740(0x2e4)
>> + * framelength 2024(0x7e8)
>> + * grabwindow_width 2592
>> + * grabwindow_height 1944
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_2592x1944_regs[] = {
>> +	{0x3501, 0x7e},
>> +	{0x366e, 0x18},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x00},
>> +	{0x3803, 0x04},
>> +	{0x3804, 0x0a},
>> +	{0x3805, 0x3f},
>> +	{0x3806, 0x07},
>> +	{0x3807, 0xab},
>> +	{0x3808, 0x0a},
>> +	{0x3809, 0x20},
>> +	{0x380a, 0x07},
>> +	{0x380b, 0x98},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xe4},
>> +	{0x380e, 0x07},
>> +	{0x380f, 0xe8},
>> +	{0x3811, 0x06},
>> +	{0x3813, 0x08},
>> +	{0x3814, 0x01},
>> +	{0x3816, 0x01},
>> +	{0x3817, 0x01},
>> +	{0x3820, 0x88},
>> +	{0x3821, 0x00},
>> +	{0x4501, 0x00},
>> +	{0x4008, 0x04},
>> +	{0x4009, 0x13},
>> +	{REG_NULL, 0x00},
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 672(0x2a0)
>> + * framelength 2232(0x8b8)
>> + * grabwindow_width 1920
>> + * grabwindow_height 1080
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_1920x1080_regs[] = {
>> +	{0x3501, 0x45},
>> +	{0x366e, 0x18},
>> +	{0x3800, 0x01},
>> +	{0x3801, 0x50},
>> +	{0x3802, 0x01},
>> +	{0x3803, 0xb8},
>> +	{0x3804, 0x08},
>> +	{0x3805, 0xef},
>> +	{0x3806, 0x05},
>> +	{0x3807, 0xf7},
>> +	{0x3808, 0x07},
>> +	{0x3809, 0x80},
>> +	{0x380a, 0x04},
>> +	{0x380b, 0x38},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xa0},
>> +	{0x380e, 0x08},
>> +	{0x380f, 0xb8},
>> +	{0x3811, 0x06},
>> +	{0x3813, 0x04},
>> +	{0x3814, 0x01},
>> +	{0x3816, 0x01},
>> +	{0x3817, 0x01},
>> +	{0x3820, 0x88},
>> +	{0x3821, 0x00},
>> +	{0x4501, 0x00},
>> +	{0x4008, 0x04},
>> +	{0x4009, 0x13},
>> +	{REG_NULL, 0x00}
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 740(0x02e4)
>> + * framelength 1012(0x03f4)
>> + * grabwindow_width 1296
>> + * grabwindow_height 972
>> + * max_framerate 60fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_1296x972_regs[] = {
>> +	{0x0103, 0x01},
>> +	{0x0100, 0x00},
>> +	{0x0300, 0x04},
>> +	{0x0301, 0x00},
>> +	{0x0302, 0x69},
>> +	{0x0303, 0x00},
>> +	{0x0304, 0x00},
>> +	{0x0305, 0x01},
>> +	{0x0307, 0x00},
>> +	{0x030b, 0x00},
>> +	{0x030c, 0x00},
>> +	{0x030d, 0x1e},
>> +	{0x030e, 0x04},
>> +	{0x030f, 0x03},
>> +	{0x0312, 0x01},
>> +	{0x3000, 0x00},
>> +	{0x3002, 0x21},
>> +	{0x3016, 0x32},
>> +	{0x3022, 0x51},
>> +	{0x3106, 0x15},
>> +	{0x3107, 0x01},
>> +	{0x3108, 0x05},
>> +	{0x3500, 0x00},
>> +	{0x3501, 0x3e},
>> +	{0x3502, 0x00},
>> +	{0x3503, 0x08},
>> +	{0x3504, 0x03},
>> +	{0x3505, 0x8c},
>> +	{0x3507, 0x03},
>> +	{0x3508, 0x00},
>> +	{0x3509, 0x10},
>> +	{0x350c, 0x00},
>> +	{0x350d, 0x80},
>> +	{0x3510, 0x00},
>> +	{0x3511, 0x02},
>> +	{0x3512, 0x00},
>> +	{0x3601, 0x55},
>> +	{0x3602, 0x58},
>> +	{0x3611, 0x58},
>> +	{0x3614, 0x30},
>> +	{0x3615, 0x77},
>> +	{0x3621, 0x08},
>> +	{0x3624, 0x40},
>> +	{0x3633, 0x0c},
>> +	{0x3634, 0x0c},
>> +	{0x3635, 0x0c},
>> +	{0x3636, 0x0c},
>> +	{0x3638, 0x00},
>> +	{0x3639, 0x00},
>> +	{0x363a, 0x00},
>> +	{0x363b, 0x00},
>> +	{0x363c, 0xff},
>> +	{0x363d, 0xfa},
>> +	{0x3650, 0x44},
>> +	{0x3651, 0x44},
>> +	{0x3652, 0x44},
>> +	{0x3653, 0x44},
>> +	{0x3654, 0x44},
>> +	{0x3655, 0x44},
>> +	{0x3656, 0x44},
>> +	{0x3657, 0x44},
>> +	{0x3660, 0x00},
>> +	{0x3661, 0x00},
>> +	{0x3662, 0x00},
>> +	{0x366a, 0x00},
>> +	{0x366e, 0x0c},
>> +	{0x3673, 0x04},
>> +	{0x3700, 0x14},
>> +	{0x3703, 0x0c},
>> +	{0x3706, 0x24},
>> +	{0x3714, 0x27},
>> +	{0x3715, 0x01},
>> +	{0x3716, 0x00},
>> +	{0x3717, 0x02},
>> +	{0x3733, 0x10},
>> +	{0x3734, 0x40},
>> +	{0x373f, 0xa0},
>> +	{0x3765, 0x20},
>> +	{0x37a1, 0x1d},
>> +	{0x37a8, 0x26},
>> +	{0x37ab, 0x14},
>> +	{0x37c2, 0x04},
>> +	{0x37c3, 0xf0},
>> +	{0x37cb, 0x09},
>> +	{0x37cc, 0x13},
>> +	{0x37cd, 0x1f},
>> +	{0x37ce, 0x1f},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x00},
>> +	{0x3803, 0x00},
>> +	{0x3804, 0x0a},
>> +	{0x3805, 0x3f},
>> +	{0x3806, 0x07},
>> +	{0x3807, 0xaf},
>> +	{0x3808, 0x05},
>> +	{0x3809, 0x10},
>> +	{0x380a, 0x03},
>> +	{0x380b, 0xcc},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xe4},
>> +	{0x380e, 0x03},
>> +	{0x380f, 0xf4},
>> +	{0x3810, 0x00},
>> +	{0x3811, 0x00},
>> +	{0x3812, 0x00},
>> +	{0x3813, 0x06},
>> +	{0x3814, 0x03},
>> +	{0x3815, 0x01},
>> +	{0x3816, 0x03},
>> +	{0x3817, 0x01},
>> +	{0x3818, 0x00},
>> +	{0x3819, 0x00},
>> +	{0x381a, 0x00},
>> +	{0x381b, 0x01},
>> +	{0x3820, 0x8b},
>> +	{0x3821, 0x01},
>> +	{0x3c80, 0x08},
>> +	{0x3c82, 0x00},
>> +	{0x3c83, 0x00},
>> +	{0x3c88, 0x00},
>> +	{0x3d85, 0x14},
>> +	{0x3f02, 0x08},
>> +	{0x3f03, 0x10},
>> +	{0x4008, 0x02},
>> +	{0x4009, 0x09},
>> +	{0x404e, 0x20},
>> +	{0x4501, 0x00},
>> +	{0x4502, 0x10},
>> +	{0x4800, 0x00},
>> +	{0x481f, 0x2a},
>> +	{0x4837, 0x13},
>> +	{0x5000, 0x13},
>> +	{0x5780, 0x3e},
>> +	{0x5781, 0x0f},
>> +	{0x5782, 0x44},
>> +	{0x5783, 0x02},
>> +	{0x5784, 0x01},
>> +	{0x5785, 0x01},
>> +	{0x5786, 0x00},
>> +	{0x5787, 0x04},
>> +	{0x5788, 0x02},
>> +	{0x5789, 0x0f},
>> +	{0x578a, 0xfd},
>> +	{0x578b, 0xf5},
>> +	{0x578c, 0xf5},
>> +	{0x578d, 0x03},
>> +	{0x578e, 0x08},
>> +	{0x578f, 0x0c},
>> +	{0x5790, 0x08},
>> +	{0x5791, 0x06},
>> +	{0x5792, 0x00},
>> +	{0x5793, 0x52},
>> +	{0x5794, 0xa3},
>> +	{0x5b00, 0x00},
>> +	{0x5b01, 0x1c},
>> +	{0x5b02, 0x00},
>> +	{0x5b03, 0x7f},
>> +	{0x5b05, 0x6c},
>> +	{0x5e10, 0xfc},
>> +	{0x4010, 0xf1},
>> +	{0x3503, 0x08},
>> +	{0x3505, 0x8c},
>> +	{0x3507, 0x03},
>> +	{0x3508, 0x00},
>> +	{0x3509, 0xf8},
>> +	{0x0100, 0x01},
>> +	{REG_NULL, 0x00}
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 672(0x2a0)
>> + * framelength 2232(0x8b8)
>> + * grabwindow_width 1280
>> + * grabwindow_height 720
>> + * max_framerate 30fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_1280x720_regs[] = {
>> +	{0x3501, 0x45},
>> +	{0x366e, 0x0c},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x01},
>> +	{0x3803, 0x00},
>> +	{0x3804, 0x0a},
>> +	{0x3805, 0x3f},
>> +	{0x3806, 0x06},
>> +	{0x3807, 0xaf},
>> +	{0x3808, 0x05},
>> +	{0x3809, 0x00},
>> +	{0x380a, 0x02},
>> +	{0x380b, 0xd0},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xa0},
>> +	{0x380e, 0x08},
>> +	{0x380f, 0xb8},
>> +	{0x3811, 0x06},
>> +	{0x3813, 0x02},
>> +	{0x3814, 0x03},
>> +	{0x3816, 0x03},
>> +	{0x3817, 0x01},
>> +	{0x3820, 0x8b},
>> +	{0x3821, 0x01},
>> +	{0x4501, 0x00},
>> +	{0x4008, 0x02},
>> +	{0x4009, 0x09},
>> +	{REG_NULL, 0x00}
>> +};
>> +
>> +/*
>> + * Xclk 24Mhz
>> + * Pclk 45Mhz
>> + * linelength 672(0x2a0)
>> + * framelength 558(0x22e)
>> + * grabwindow_width 640
>> + * grabwindow_height 480
>> + * max_framerate 120fps
>> + * mipi_datarate per lane 840Mbps
>> + */
>> +static const struct regval ov5695_640x480_regs[] = {
>> +	{0x3501, 0x22},
>> +	{0x366e, 0x0c},
>> +	{0x3800, 0x00},
>> +	{0x3801, 0x00},
>> +	{0x3802, 0x00},
>> +	{0x3803, 0x08},
>> +	{0x3804, 0x0a},
>> +	{0x3805, 0x3f},
>> +	{0x3806, 0x07},
>> +	{0x3807, 0xa7},
>> +	{0x3808, 0x02},
>> +	{0x3809, 0x80},
>> +	{0x380a, 0x01},
>> +	{0x380b, 0xe0},
>> +	{0x380c, 0x02},
>> +	{0x380d, 0xa0},
>> +	{0x380e, 0x02},
>> +	{0x380f, 0x2e},
>> +	{0x3811, 0x06},
>> +	{0x3813, 0x04},
>> +	{0x3814, 0x07},
>> +	{0x3816, 0x05},
>> +	{0x3817, 0x03},
>> +	{0x3820, 0x8d},
>> +	{0x3821, 0x01},
>> +	{0x4501, 0x00},
>> +	{0x4008, 0x02},
>> +	{0x4009, 0x09},
>> +	{REG_NULL, 0x00}
>> +};
>> +
>> +static const struct ov5695_mode supported_modes[] = {
>> +	{
>> +		.width = 2592,
>> +		.height = 1944,
>> +		.max_fps = 30,
>> +		.exp_def = 0x0450,
>> +		.hts_def = 0x02e4 * 4,
>> +		.vts_def = 0x07e8,
>> +		.reg_list = ov5695_2592x1944_regs,
>> +	},
>> +	{
>> +		.width = 1920,
>> +		.height = 1080,
>> +		.max_fps = 30,
>> +		.exp_def = 0x0450,
>> +		.hts_def = 0x02a0 * 4,
>> +		.vts_def = 0x08b8,
>> +		.reg_list = ov5695_1920x1080_regs,
>> +	},
>> +	{
>> +		.width = 1296,
>> +		.height = 972,
>> +		.max_fps = 60,
>> +		.exp_def = 0x03e0,
>> +		.hts_def = 0x02e4 * 4,
>> +		.vts_def = 0x03f4,
>> +		.reg_list = ov5695_1296x972_regs,
>> +	},
>> +	{
>> +		.width = 1280,
>> +		.height = 720,
>> +		.max_fps = 30,
>> +		.exp_def = 0x0450,
>> +		.hts_def = 0x02a0 * 4,
>> +		.vts_def = 0x08b8,
>> +		.reg_list = ov5695_1280x720_regs,
>> +	},
>> +	{
>> +		.width = 640,
>> +		.height = 480,
>> +		.max_fps = 120,
>> +		.exp_def = 0x0450,
>> +		.hts_def = 0x02a0 * 4,
>> +		.vts_def = 0x022e,
>> +		.reg_list = ov5695_640x480_regs,
>> +	},
>> +};
>> +
>> +#define OV5695_LINK_FREQ_420MHZ		420000000
>> +static const s64 link_freq_menu_items[] = {
>> +	OV5695_LINK_FREQ_420MHZ
>> +};
>> +
>> +static const char * const ov5695_test_pattern_menu[] = {
>> +	"Disabled",
>> +	"Vertical Color Bar Type 1",
>> +	"Vertical Color Bar Type 2",
>> +	"Vertical Color Bar Type 3",
>> +	"Vertical Color Bar Type 4"
>> +};
>> +
>> +/* Write registers up to 4 at a time */
>> +static int ov5695_write_reg(struct i2c_client *client, u16 reg,
>> +			    unsigned int len, u32 val)
>> +{
>> +	int buf_i;
>> +	int 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 ov5695_write_array(struct i2c_client *client,
>> +			      const struct regval *regs)
>> +{
>> +	int i, ret = 0;
>> +
>> +	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
>> +		ret = ov5695_write_reg(client, regs[i].addr,
>> +				       OV5695_REG_VALUE_08BIT, regs[i].val);
>> +
>> +	return ret;
>> +}
>> +
>> +/* Read registers up to 4 at a time */
>> +static int ov5695_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)
>> +		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 ov5695_get_reso_dist(const struct ov5695_mode *mode,
>> +				struct v4l2_mbus_framefmt *framefmt)
>> +{
>> +	return abs(mode->width - framefmt->width) +
>> +	       abs(mode->height - framefmt->height);
>> +}
>> +
>> +static const struct ov5695_mode *ov5695_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 = ov5695_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 ov5695_set_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_pad_config *cfg,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +	const struct ov5695_mode *mode;
>> +	s64 h_blank, vblank_def;
>> +
>> +	mutex_lock(&ov5695->mutex);
>> +
>> +	mode = ov5695_find_best_fit(fmt);
>> +	fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	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(&ov5695->mutex);
>> +		return -ENOTTY;
>> +#endif
>> +	} else {
>> +		ov5695->cur_mode = mode;
>> +		h_blank = mode->hts_def - mode->width;
>> +		__v4l2_ctrl_modify_range(ov5695->hblank, h_blank,
>> +					 h_blank, 1, h_blank);
>> +		vblank_def = mode->vts_def - mode->height;
>> +		__v4l2_ctrl_modify_range(ov5695->vblank, vblank_def,
>> +					 OV5695_VTS_MAX - mode->height,
>> +					 1, vblank_def);
>> +	}
>> +
>> +	mutex_unlock(&ov5695->mutex);
>> +	return 0;
>> +}
>> +
>> +static int ov5695_get_fmt(struct v4l2_subdev *sd,
>> +			  struct v4l2_subdev_pad_config *cfg,
>> +			  struct v4l2_subdev_format *fmt)
>> +{
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +	const struct ov5695_mode *mode = ov5695->cur_mode;
>> +
>> +	mutex_lock(&ov5695->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(&ov5695->mutex);
>> +		return -ENOTTY;
>> +#endif
>> +	} else {
>> +		fmt->format.width = mode->width;
>> +		fmt->format.height = mode->height;
>> +		fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +		fmt->format.field = V4L2_FIELD_NONE;
>> +	}
>> +	mutex_unlock(&ov5695->mutex);
>> +	return 0;
>> +}
>> +
>> +static int ov5695_enum_mbus_code(struct v4l2_subdev *sd,
>> +				 struct v4l2_subdev_pad_config *cfg,
>> +				 struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> +	if (code->index != 0)
>> +		return -EINVAL;
>> +	code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov5695_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_SBGGR10_1X10)
>> +		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 int ov5695_enable_test_pattern(struct ov5695 *ov5695, u32 pattern)
>> +{
>> +	u32 val;
>> +
>> +	if (pattern)
>> +		val = (pattern - 1) | OV5695_TEST_PATTERN_ENABLE;
>> +	else
>> +		val = OV5695_TEST_PATTERN_DISABLE;
>> +
>> +	return ov5695_write_reg(ov5695->client, OV5695_REG_TEST_PATTERN,
>> +				OV5695_REG_VALUE_08BIT, val);
>> +}
>> +
>> +static void ov5695_set_exposure(struct ov5695 *ov5695, s32 val)
>> +{
>> +	/* 4 least significant bits of expsoure are fractional part */
>> +	ov5695_write_reg(ov5695->client, OV5695_REG_EXPOSURE,
>> +			 OV5695_REG_VALUE_24BIT, val << 4);
>> +}
>> +
>> +static void ov5695_set_analog_gain(struct ov5695 *ov5695, s32 val)
>> +{
>> +	ov5695_write_reg(ov5695->client, OV5695_REG_ANALOG_GAIN,
>> +			 OV5695_REG_VALUE_08BIT, val);
>> +}
>> +
>> +static void ov5695_set_digi_gain(struct ov5695 *ov5695, s32 val)
>> +{
>> +	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_L,
>> +			 OV5695_REG_VALUE_08BIT, val & OV5695_DIGI_GAIN_L_MASK);
>> +	val >>= OV5695_DIGI_GAIN_H_SHIFT;
>> +	ov5695_write_reg(ov5695->client, OV5695_REG_DIGI_GAIN_H,
>> +			 OV5695_REG_VALUE_08BIT, val);
>> +}
>> +
>> +static void ov5695_set_vts(struct ov5695 *ov5695, s32 val)
>> +{
>> +	val += ov5695->cur_mode->height;
>> +
>> +	ov5695_write_reg(ov5695->client, OV5695_REG_VTS,
>> +			 OV5695_REG_VALUE_16BIT, val);
>> +}
>> +
>> +static int __ov5695_start_stream(struct ov5695 *ov5695)
>> +{
>> +	int ret;
>> +
>> +	ret = ov5695_write_array(ov5695->client, ov5695_global_regs);
>> +	if (ret)
>> +		return ret;
>> +	ret = ov5695_write_array(ov5695->client, ov5695->cur_mode->reg_list);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* In case these controls are set before streaming */
>> +	ov5695_set_exposure(ov5695, ov5695->exposure->val);
>> +	ov5695_set_analog_gain(ov5695, ov5695->anal_gain->val);
>> +	ov5695_set_digi_gain(ov5695, ov5695->digi_gain->val);
>> +	ov5695_set_vts(ov5695, ov5695->vblank->val);
>> +	ov5695_enable_test_pattern(ov5695, ov5695->test_pattern->val);
>> +
>> +	ret = ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
>> +			       OV5695_REG_VALUE_08BIT, OV5695_MODE_STREAMING);
>> +	return ret;
>> +}
>> +
>> +static int __ov5695_stop_stream(struct ov5695 *ov5695)
>> +{
>> +	return ov5695_write_reg(ov5695->client, OV5695_REG_CTRL_MODE,
>> +				OV5695_REG_VALUE_08BIT, OV5695_MODE_SW_STANDBY);
>> +}
>> +
>> +static int ov5695_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +	struct i2c_client *client = ov5695->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov5695->mutex);
>> +	on = !!on;
>> +	if (on == ov5695->streaming)
>> +		goto unlock_and_return;
>> +
>> +	if (on) {
>> +		ret = pm_runtime_get_sync(&client->dev);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +
>> +		ret = __ov5695_start_stream(ov5695);
>> +		if (ret) {
>> +			v4l2_err(sd, "start stream failed while write regs\n");
>> +			pm_runtime_put(&client->dev);
> Here you will set "ov5695->streaming = 1". Should't you jump to the
> "unlock_and_return" label instead?
Done.
>> +		}
>> +	} else {
>> +		ret = __ov5695_stop_stream(ov5695);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +		pm_runtime_put(&client->dev);
> I would runtime_put even if writing to REG_CTRL_MODE fails.
Done.
>> +	}
>> +
>> +	ov5695->streaming = on;
>> +
>> +unlock_and_return:
>> +	mutex_unlock(&ov5695->mutex);
>> +	return ret;
>> +}
>> +
>> +static int __ov5695_power_on(struct ov5695 *ov5695)
>> +{
>> +	int ret;
>> +	struct device *dev = &ov5695->client->dev;
>> +
>> +	ret = clk_prepare_enable(ov5695->xvclk);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable xvclk\n");
>> +		return ret;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
>> +
>> +	/* AVDD and DOVDD may rise in any order */
>> +	ret = regulator_enable(ov5695->avdd_regulator);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable regulator\n");
>> +		goto disable_clk;
>> +	}
>> +	ret = regulator_enable(ov5695->dovdd_regulator);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable DOVDD regulator\n");
>> +		goto disable_avdd;
>> +	}
>> +	/* DVDD must rise after AVDD and DOVDD */
>> +	ret = regulator_enable(ov5695->dvdd_regulator);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to enable DVDD regulator\n");
>> +		goto disable_dovdd;
>> +	}
>> +
>> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 0);
>> +	/* 8192 cycles prior to first SCCB transaction */
>> +	usleep_range(1000, 1500);
> I assume 8192 xvclk cycles, shouldn't the delay be calculated using
> that value? (or does the (1000, 1500) intervals catches all the
> supported xvclk frequencies?)
Calculated by clk rate.
>> +
>> +	return 0;
>> +
>> +disable_dovdd:
>> +	regulator_disable(ov5695->dovdd_regulator);
>> +disable_avdd:
>> +	regulator_disable(ov5695->avdd_regulator);
>> +disable_clk:
>> +	clk_disable_unprepare(ov5695->xvclk);
>> +
>> +	return ret;
>> +}
>> +
>> +static void __ov5695_power_off(struct ov5695 *ov5695)
>> +{
>> +	clk_disable_unprepare(ov5695->xvclk);
>> +	gpiod_set_value_cansleep(ov5695->reset_gpio, 1);
>> +	regulator_disable(ov5695->dvdd_regulator);
>> +	regulator_disable(ov5695->dovdd_regulator);
>> +	regulator_disable(ov5695->avdd_regulator);
>> +}
>> +
>> +static int ov5695_runtime_resume(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +	int ret;
>> +
>> +	ret = __ov5695_power_on(ov5695);
>> +	if (ret)
>> +		return ret;
>> +
>> +	if (ov5695->streaming) {
>> +		ret = __ov5695_start_stream(ov5695);
>> +		if (ret)
>> +			__ov5695_power_off(ov5695);
>> +	}
> If you fix my comment above on s_stream, you won't have any case where
> runtime_susped is called with "ov5695->streaming = 1" and you can
> remove this if branch.
Done.
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov5695_runtime_suspend(struct device *dev)
>> +{
>> +	struct i2c_client *client = to_i2c_client(dev);
>> +	struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +
>> +	__ov5695_power_off(ov5695);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static int ov5695_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
>> +{
>> +	struct ov5695 *ov5695 = to_ov5695(sd);
>> +	struct v4l2_mbus_framefmt *try_fmt =
>> +				v4l2_subdev_get_try_format(sd, fh->pad, 0);
>> +
>> +	mutex_lock(&ov5695->mutex);
>> +	/* Initialize try_fmt */
>> +	try_fmt->width = ov5695->cur_mode->width;
>> +	try_fmt->height = ov5695->cur_mode->height;
>> +	try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
>> +	try_fmt->field = V4L2_FIELD_NONE;
>> +
>> +	mutex_unlock(&ov5695->mutex);
>> +	/* No crop or compose */
> Checkpatch should complain about returns with no blank line before
> (here and in many other places in this patch).
>
> Maybe try run it with --strict switch...
Done.
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static const struct dev_pm_ops ov5695_pm_ops = {
>> +	SET_RUNTIME_PM_OPS(ov5695_runtime_suspend,
>> +			   ov5695_runtime_resume, NULL)
>> +};
>> +
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +static const struct v4l2_subdev_internal_ops ov5695_internal_ops = {
>> +	.open = ov5695_open,
>> +};
>> +#endif
>> +
>> +static struct v4l2_subdev_video_ops ov5695_video_ops = {
>> +	.s_stream = ov5695_s_stream,
>> +};
>> +
>> +static struct v4l2_subdev_pad_ops ov5695_pad_ops = {
>> +	.enum_mbus_code = ov5695_enum_mbus_code,
>> +	.enum_frame_size = ov5695_enum_frame_sizes,
>> +	.get_fmt = ov5695_get_fmt,
>> +	.set_fmt = ov5695_set_fmt,
>> +};
>> +
>> +static struct v4l2_subdev_ops ov5695_subdev_ops = {
>> +	.video	= &ov5695_video_ops,
>> +	.pad	= &ov5695_pad_ops,
>> +};
>> +
>> +static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> +	struct ov5695 *ov5695 = container_of(ctrl->handler,
>> +					     struct ov5695, ctrl_handler);
>> +	struct i2c_client *client = ov5695->client;
>> +	s64 max;
>> +	int ret = 0;
>> +
>> +	/* Propagate change of current control to all related controls */
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_VBLANK:
>> +		/* Update max exposure while meeting expected vblanking */
>> +		max = ov5695->cur_mode->height + ctrl->val - 4;
>> +		__v4l2_ctrl_modify_range(ov5695->exposure,
>> +					 ov5695->exposure->minimum, max,
>> +					 ov5695->exposure->step,
>> +					 ov5695->exposure->default_value);
>> +		break;
>> +	}
>> +
>> +	pm_runtime_get_sync(&client->dev);
>> +	switch (ctrl->id) {
>> +	case V4L2_CID_EXPOSURE:
>> +		ov5695_set_exposure(ov5695, ctrl->val);
>> +		break;
>> +	case V4L2_CID_ANALOGUE_GAIN:
>> +		ov5695_set_analog_gain(ov5695, ctrl->val);
>> +		break;
>> +	case V4L2_CID_DIGITAL_GAIN:
>> +		ov5695_set_digi_gain(ov5695, ctrl->val);
>> +		break;
>> +	case V4L2_CID_VBLANK:
>> +		ov5695_set_vts(ov5695, ctrl->val);
>> +		break;
>> +	case V4L2_CID_TEST_PATTERN:
>> +		ret = ov5695_enable_test_pattern(ov5695, ctrl->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 ov5695_ctrl_ops = {
>> +	.s_ctrl = ov5695_set_ctrl,
>> +};
>> +
>> +static int ov5695_initialize_controls(struct ov5695 *ov5695)
>> +{
>> +	const struct ov5695_mode *mode;
>> +	struct v4l2_ctrl_handler *handler;
>> +	struct v4l2_ctrl *ctrl;
>> +	s64 exposure_max, vblank_def;
>> +	u32 h_blank;
>> +	int ret;
>> +
>> +	handler = &ov5695->ctrl_handler;
>> +	mode = ov5695->cur_mode;
>> +	ret = v4l2_ctrl_handler_init(handler, 1);
>> +	if (ret)
>> +		return ret;
>> +	handler->lock = &ov5695->mutex;
>> +
>> +	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
>> +				      0, 0, link_freq_menu_items);
>> +	if (ctrl)
>> +		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
>> +			  0, OV5695_PIXEL_RATE, 1, OV5695_PIXEL_RATE);
>> +
>> +	h_blank = mode->hts_def - mode->width;
>> +	ov5695->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
>> +				h_blank, h_blank, 1, h_blank);
>> +	if (ov5695->hblank)
>> +		ov5695->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> +	vblank_def = mode->vts_def - mode->height;
>> +	ov5695->vblank = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
>> +				V4L2_CID_VBLANK, vblank_def,
>> +				OV5695_VTS_MAX - mode->height,
>> +				1, vblank_def);
>> +
>> +	exposure_max = mode->vts_def - 4;
>> +	ov5695->exposure = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
>> +				V4L2_CID_EXPOSURE, OV5695_EXPOSURE_MIN,
>> +				exposure_max, OV5695_EXPOSURE_STEP,
>> +				mode->exp_def);
>> +
>> +	ov5695->anal_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
>> +				V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN,
>> +				ANALOG_GAIN_MAX, ANALOG_GAIN_STEP,
>> +				ANALOG_GAIN_DEFAULT);
>> +
>> +	/* Digital gain */
>> +	ov5695->digi_gain = v4l2_ctrl_new_std(handler, &ov5695_ctrl_ops,
>> +				V4L2_CID_DIGITAL_GAIN, OV5695_DIGI_GAIN_MIN,
>> +				OV5695_DIGI_GAIN_MAX, OV5695_DIGI_GAIN_STEP,
>> +				OV5695_DIGI_GAIN_DEFAULT);
>> +
>> +	ov5695->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
>> +				&ov5695_ctrl_ops, V4L2_CID_TEST_PATTERN,
>> +				ARRAY_SIZE(ov5695_test_pattern_menu) - 1,
>> +				0, 0, ov5695_test_pattern_menu);
>> +
>> +	if (handler->error) {
>> +		v4l2_ctrl_handler_free(handler);
>> +		return handler->error;
>> +	}
>> +
>> +	ov5695->subdev.ctrl_handler = handler;
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov5695_check_sensor_id(struct ov5695 *ov5695,
>> +				  struct i2c_client *client)
>> +{
>> +	struct device *dev = &ov5695->client->dev;
>> +	u32 id;
>> +	int ret;
>> +
>> +	ret = __ov5695_power_on(ov5695);
>> +	if (ret)
>> +		return ret;
>> +	ov5695_read_reg(client, OV5695_REG_CHIP_ID,
>> +			OV5695_REG_VALUE_24BIT, &id);
>> +	__ov5695_power_off(ov5695);
>> +
>> +	if (id != CHIP_ID) {
>> +		dev_err(dev, "Wrong camera sensor id(%06x)\n", id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
>> +
>> +	return 0;
>> +}
>> +
>> +static int ov5695_probe(struct i2c_client *client,
>> +			const struct i2c_device_id *id)
>> +{
>> +	struct device *dev = &client->dev;
>> +	struct ov5695 *ov5695;
>> +	struct v4l2_subdev *sd;
>> +	int ret;
>> +
>> +	ov5695 = devm_kzalloc(dev, sizeof(*ov5695), GFP_KERNEL);
>> +	if (!ov5695)
>> +		return -ENOMEM;
>> +
>> +	ov5695->client = client;
>> +	ov5695->cur_mode = &supported_modes[0];
>> +
>> +	ov5695->xvclk = devm_clk_get(dev, "xvclk");
>> +	if (IS_ERR(ov5695->xvclk)) {
>> +		dev_err(dev, "Failed to get xvclk\n");
>> +		return -EINVAL;
>> +	}
>> +	ret = clk_set_rate(ov5695->xvclk, 24000000);
>> +	if (ret < 0) {
>> +		dev_err(dev, "Failed to set xvclk rate (24M)\n");
>> +		return ret;
>> +	}
>> +
>> +	ov5695->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
>> +	if (IS_ERR(ov5695->reset_gpio)) {
>> +		dev_err(dev, "Failed to get reset-gpios\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ov5695->avdd_regulator = devm_regulator_get(dev, "avdd");
>> +	if (IS_ERR(ov5695->avdd_regulator)) {
>> +		dev_err(dev, "Failed to get avdd-supply\n");
>> +		return -EINVAL;
>> +	}
>> +	ov5695->dovdd_regulator = devm_regulator_get(dev, "dovdd");
>> +	if (IS_ERR(ov5695->dovdd_regulator)) {
>> +		dev_err(dev, "Failed to get dovdd-supply\n");
>> +		return -EINVAL;
>> +	}
>> +	ov5695->dvdd_regulator = devm_regulator_get(dev, "dvdd");
>> +	if (IS_ERR(ov5695->dvdd_regulator)) {
>> +		dev_err(dev, "Failed to get dvdd-supply\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	mutex_init(&ov5695->mutex);
>> +
>> +	sd = &ov5695->subdev;
>> +	v4l2_i2c_subdev_init(sd, client, &ov5695_subdev_ops);
>> +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
>> +	sd->internal_ops = &ov5695_internal_ops;
>> +#endif
>> +	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> Shouldn't a devnode be registered only if V4L2_SUBDEV_API is defined?
Done.

Thanks
> Thanks
>     j
>
>
>> +
>> +	ret = ov5695_initialize_controls(ov5695);
>> +	if (ret)
>> +		return ret;
>> +	ret = ov5695_check_sensor_id(ov5695, client);
>> +	if (ret)
>> +		return ret;
>> +
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	ov5695->pad.flags = MEDIA_PAD_FL_SOURCE;
>> +	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +	ret = media_entity_pads_init(&sd->entity, 1, &ov5695->pad);
>> +	if (ret < 0)
>> +		return ret;
>> +#endif
>> +
>> +	ret = v4l2_async_register_subdev(sd);
>> +	if (ret) {
>> +		dev_err(dev, "v4l2 async register subdev failed\n");
>> +		goto clean_entity;
>> +	}
>> +
>> +	pm_runtime_enable(dev);
>> +	dev_info(dev, "Probe successfully\n");
>> +
>> +	return 0;
>> +
>> +clean_entity:
>> +#if defined(CONFIG_MEDIA_CONTROLLER)
>> +	media_entity_cleanup(&sd->entity);
>> +#endif
>> +
>> +	return ret;
>> +}
>> +
>> +static int ov5695_remove(struct i2c_client *client)
>> +{
>> +	struct ov5695 *ov5695 = i2c_get_clientdata(client);
>> +
>> +	v4l2_async_unregister_subdev(&ov5695->subdev);
>> +
>> +	pm_runtime_disable(&client->dev);
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id ov5695_of_match[] = {
>> +	{ .compatible = "ovti,ov5695" },
>> +	{},
>> +};
>> +
>> +static struct i2c_driver ov5695_i2c_driver = {
>> +	.driver = {
>> +		.name = "ov5695",
>> +		.owner = THIS_MODULE,
>> +		.pm = &ov5695_pm_ops,
>> +		.of_match_table = ov5695_of_match
>> +	},
>> +	.probe		= &ov5695_probe,
>> +	.remove		= &ov5695_remove,
>> +};
>> +
>> +module_i2c_driver(ov5695_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("OmniVision ov5695 sensor driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.9.1
>>
>


[-- Attachment #2: Type: text/html, Size: 38004 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
  2018-01-08 13:37       ` Shunqian Zheng
@ 2018-01-08 22:23         ` Sakari Ailus
  0 siblings, 0 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-08 22:23 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

Hi Shunqian,

On Mon, Jan 08, 2018 at 09:37:20PM +0800, Shunqian Zheng wrote:
...
> > > +static int ov2685_probe(struct i2c_client *client,
> > > +			const struct i2c_device_id *id)
> > > +{
> > > +	struct device *dev = &client->dev;
> > > +	struct ov2685 *ov2685;
> > > +	int ret;
> > > +
> > > +	ov2685 = devm_kzalloc(dev, sizeof(*ov2685), GFP_KERNEL);
> > > +	if (!ov2685)
> > > +		return -ENOMEM;
> > > +
> > > +	ov2685->client = client;
> > > +	ov2685->cur_mode = &supported_modes[0];
> > > +
> > > +	ov2685->xvclk = devm_clk_get(dev, "xvclk");
> > > +	if (IS_ERR(ov2685->xvclk)) {
> > > +		dev_err(dev, "Failed to get xvclk\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
> > > +	if (IS_ERR(ov2685->reset_gpio)) {
> > > +		dev_err(dev, "Failed to get reset-gpios\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	ov2685->avdd_regulator = devm_regulator_get(dev, "avdd");
> > > +	if (IS_ERR(ov2685->avdd_regulator)) {
> > > +		dev_err(dev, "Failed to get avdd-supply\n");
> > > +		return -EINVAL;
> > > +	}
> > > +	ov2685->dovdd_regulator = devm_regulator_get(dev, "dovdd");
> > > +	if (IS_ERR(ov2685->dovdd_regulator)) {
> > > +		dev_err(dev, "Failed to get dovdd-supply\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	mutex_init(&ov2685->mutex);
> > > +	v4l2_i2c_subdev_init(&ov2685->subdev, client, &ov2685_subdev_ops);
> > > +	ret = ov2685_initialize_controls(ov2685);
> > > +	if (ret)
> > > +		goto destroy_mutex;
> > > +
> > > +	ret = ov2685_check_sensor_id(ov2685, client);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
> > > +	ov2685->subdev.internal_ops = &ov2685_internal_ops;
> > > +#endif
> > > +#if defined(CONFIG_MEDIA_CONTROLLER)
> > > +	ov2685->pad.flags = MEDIA_PAD_FL_SOURCE;
> > > +	ov2685->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
> > > +	ov2685->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
> > > +	ret = media_entity_pads_init(&ov2685->subdev.entity, 1, &ov2685->pad);
> > > +	if (ret < 0)
> > > +		goto free_ctrl_handler;
> > > +#endif
> > > +
> > > +	ret = v4l2_async_register_subdev(&ov2685->subdev);
> > > +	if (ret) {
> > > +		dev_err(dev, "v4l2 async register subdev failed\n");
> > > +		goto clean_entity;
> > > +	}
> > > +
> > The device should be already powered up here... and then:
> > 
> > pm_runtime_set_active(dev);
> In my test case, sensor is not powered (power on and off in
> ov2685_check_sensor_id()).
> Checking the ov13858.c which is turned on by i2c-core with ACPI domain PM,
> this driver
> doesn't depend on ACPI.

Yes, on ACPI based systems the drivers aren't responsible for powering the
devices on and off themselves. They're powered on in probe() without usin
runtime PM.

With DT, the driver should power on the device before using runtime PM.
That way the device gets powered on even if runtime PM is disabled.

Please see the smiapp driver, it works with both DT and ACPI.

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
  2018-01-03 11:43     ` Sakari Ailus
  2018-01-08 13:37       ` Shunqian Zheng
@ 2018-01-12  2:30       ` Shunqian Zheng
       [not found]         ` <8f60a7f8-8471-85e8-70d3-520701edc092-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Shunqian Zheng @ 2018-01-12  2:30 UTC (permalink / raw)
  To: Sakari Ailus
  Cc: mchehab, robh+dt, mark.rutland, linux-media, devicetree, ddl, tfiga

[-- Attachment #1: Type: text/plain, Size: 1667 bytes --]

Hi Sakari,


On 2018年01月03日 19:43, Sakari Ailus wrote:
>> +static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
>> +{
>> +	struct ov2685 *ov2685 = to_ov2685(sd);
>> +	struct i2c_client *client = ov2685->client;
>> +	int ret = 0;
>> +
>> +	mutex_lock(&ov2685->mutex);
>> +
>> +	on = !!on;
>> +	if (on == ov2685->streaming)
>> +		goto unlock_and_return;
>> +
>> +	if (on) {
>> +		/* In case these controls are set before streaming */
>> +		ov2685_set_exposure(ov2685, ov2685->exposure->val);
>> +		ov2685_set_gain(ov2685, ov2685->anal_gain->val);
>> +		ov2685_set_vts(ov2685, ov2685->vblank->val);
>> +		ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
> You should use __v4l2_ctrl_handler_setup() here. Or put that to the
> driver's runtime_resume function. That actually might be better.
The v3 put __v4l2_ctrl_handler_setup() to the runtime_resume callback. But
ov2685_s_stream()
    -> pm_runtime_get_sync()
        -> ov2685_runtime_resume()
             -> __v4l2_ctrl_handler_setup()
                 -> pm_runtime_get_if_in_use(), always <= 0 because 
dev->power.runtime_status != RPM_ACTIVE.

Seems like  __v4l2_ctrl_handler_setup() has to be in ov2685_s_stream().

Thanks
>> +
>> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
>> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +	} else {
>> +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
>> +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
>> +		if (ret)
>> +			goto unlock_and_return;
>> +	}
>> +
>> +	ov2685->streaming = on;
>> +
>> +unlock_and_return:
>> +	mutex_unlock(&ov2685->mutex);
>> +	return ret;
>> +}


[-- Attachment #2: Type: text/html, Size: 2485 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor
       [not found]         ` <8f60a7f8-8471-85e8-70d3-520701edc092-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
@ 2018-01-31 20:44           ` Sakari Ailus
  0 siblings, 0 replies; 15+ messages in thread
From: Sakari Ailus @ 2018-01-31 20:44 UTC (permalink / raw)
  To: Shunqian Zheng
  Cc: mchehab-DgEjT+Ai2ygdnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, linux-media-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, ddl-TNX95d0MmH7DzftRWevZcw,
	tfiga-F7+t8E8rja9g9hUCZPvPmw

Hi Shunqian,

On Fri, Jan 12, 2018 at 10:30:57AM +0800, Shunqian Zheng wrote:
> Hi Sakari,
> 
> 
> On 2018年01月03日 19:43, Sakari Ailus wrote:
> > > +static int ov2685_s_stream(struct v4l2_subdev *sd, int on)
> > > +{
> > > +	struct ov2685 *ov2685 = to_ov2685(sd);
> > > +	struct i2c_client *client = ov2685->client;
> > > +	int ret = 0;
> > > +
> > > +	mutex_lock(&ov2685->mutex);
> > > +
> > > +	on = !!on;
> > > +	if (on == ov2685->streaming)
> > > +		goto unlock_and_return;
> > > +
> > > +	if (on) {
> > > +		/* In case these controls are set before streaming */
> > > +		ov2685_set_exposure(ov2685, ov2685->exposure->val);
> > > +		ov2685_set_gain(ov2685, ov2685->anal_gain->val);
> > > +		ov2685_set_vts(ov2685, ov2685->vblank->val);
> > > +		ov2685_enable_test_pattern(ov2685, ov2685->test_pattern->val);
> > You should use __v4l2_ctrl_handler_setup() here. Or put that to the
> > driver's runtime_resume function. That actually might be better.
> The v3 put __v4l2_ctrl_handler_setup() to the runtime_resume callback. But
> ov2685_s_stream()
>    -> pm_runtime_get_sync()
>        -> ov2685_runtime_resume()
>             -> __v4l2_ctrl_handler_setup()
>                 -> pm_runtime_get_if_in_use(), always <= 0 because
> dev->power.runtime_status != RPM_ACTIVE.
> 
> Seems like  __v4l2_ctrl_handler_setup() has to be in ov2685_s_stream().

Right, indeed. Well spotted.

The smiapp driver uses a device specific variable for the purpose, thus I
missed this. Other drivers apply the controls while streaming on.

> 
> Thanks
> > > +
> > > +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
> > > +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STREAMING);
> > > +		if (ret)
> > > +			goto unlock_and_return;
> > > +	} else {
> > > +		ret = ov2685_write_reg(client, REG_SC_CTRL_MODE,
> > > +				OV2685_REG_VALUE_08BIT, SC_CTRL_MODE_STANDBY);
> > > +		if (ret)
> > > +			goto unlock_and_return;
> > > +	}
> > > +
> > > +	ov2685->streaming = on;
> > > +
> > > +unlock_and_return:
> > > +	mutex_unlock(&ov2685->mutex);
> > > +	return ret;
> > > +}
> 

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus-X3B1VOXEql0@public.gmane.org
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2018-01-31 20:44 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-29  8:08 [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor Shunqian Zheng
     [not found] ` <1514534905-21393-1-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
2017-12-29  8:08   ` [PATCH v2 2/4] [media] dt/bindings: Add bindings for OV5695 Shunqian Zheng
     [not found]     ` <1514534905-21393-2-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
2018-01-02  9:19       ` jacopo mondi
2018-01-02 14:25       ` Sakari Ailus
2017-12-29  8:08   ` [PATCH v2 3/4] media: ov2685: add support for OV2685 sensor Shunqian Zheng
2018-01-03 11:43     ` Sakari Ailus
2018-01-08 13:37       ` Shunqian Zheng
2018-01-08 22:23         ` Sakari Ailus
2018-01-12  2:30       ` Shunqian Zheng
     [not found]         ` <8f60a7f8-8471-85e8-70d3-520701edc092-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
2018-01-31 20:44           ` Sakari Ailus
2017-12-29  8:08   ` [PATCH v2 4/4] [media] dt/bindings: Add bindings for OV2685 Shunqian Zheng
     [not found]     ` <1514534905-21393-4-git-send-email-zhengsq-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
2018-01-03 12:14       ` Sakari Ailus
2018-01-02 10:09   ` [PATCH v2 1/4] media: ov5695: add support for OV5695 sensor jacopo mondi
2018-01-08 13:40     ` Shunqian Zheng
2018-01-03 14:30 ` Sakari Ailus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).